diff --git a/README.md b/README.md index ba79881..536fd4c 100644 --- a/README.md +++ b/README.md @@ -24,36 +24,39 @@ npm install react react-dom react-aria-components ## 使い方 -アプリのルートで `FlavorProvider` を配置し、全フレーバー同梱の CSS を読み込みます。`flavor` prop は省略時 `pepper` が適用されます。 +CSS をインポートするだけで、デフォルトの `pepper` フレーバーでコンポーネントが描画されます。 + +```tsx +import { Button } from 'wip-ui' +import 'wip-ui/styles.css' + +export const App = () => +``` + +別のフレーバーを適用したい場合、あるいは領域ごとに異なるフレーバーを切り替えたい場合は `FlavorProvider` で対象領域を囲みます。 ```tsx import { FlavorProvider, Button } from 'wip-ui' -import 'wip-ui/css/all.css' +import 'wip-ui/styles.css' export const App = () => ( - + ) ``` -別のフレーバーに切り替える場合は `flavor` prop を指定します。ネストして領域ごとに異なるフレーバーを適用することもできます。 - -```tsx - - - -``` - コンポーネント単位でのインポートも可能です。 ```tsx import { Button } from 'wip-ui/Button' ``` +> **中の `FlavorProvider` で切り替える場合の注意**: フレーバーを入れ子にしたとき、CSS の詳細度が同じためソース順序が後ろ(`pepper` < `minne` < `apollo` < `nachiguro` < `flippers` < `kung-pu` < `lolipop` の順)のフレーバーが勝ちます。「外側 `lolipop` + 内側 `pepper`」のような組み合わせでは、内側が外側を上書きできない点にご注意ください。 + ### 利用可能なフレーバー -`wip-ui/css/all.css` は以下の全フレーバーを含みます。 +`wip-ui/styles.css` は以下の全フレーバーを含みます。 - `pepper`(デフォルト) - `minne` diff --git a/package.json b/package.json index fa098b8..2cbcd66 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ }, "./types": null, "./utils": null, + "./styles.css": "./dist/styles.css", "./css/*.css": "./dist/css/*.css" }, "files": [ diff --git a/scripts/build-flavor-css.mjs b/scripts/build-flavor-css.mjs index 75b2bbb..5022b7e 100644 --- a/scripts/build-flavor-css.mjs +++ b/scripts/build-flavor-css.mjs @@ -9,6 +9,7 @@ const NODE_MODULES = path.join(ROOT, 'node_modules') const ENTRY_SCSS = path.join(ROOT, 'src', '_all.scss') const OUTPUT_DIR = path.join(ROOT, '.storybook', 'flavors') const DIST_DIR = path.join(ROOT, 'dist', 'css') +const DIST_ROOT = path.join(ROOT, 'dist') const ICON_FONT_PATH = path.join( NODE_MODULES, '@pepabo-inhouse', @@ -34,6 +35,9 @@ const iconFontFace = (() => { // token declarations no longer live at document level. Required for the // combined (`all.css`) build to avoid the last-flavor-wins cascade conflict // when multiple flavors' `:root { --token: ... }` blocks are concatenated. +// When `prefix` is an empty string, non-:root selectors are left unchanged +// so the output acts as an unscoped baseline (used to give consumers +// styling even without ). function prefixPlugin(prefix, { scopeRoot = false } = {}) { return { postcssPlugin: 'prefix-flavor', @@ -52,15 +56,20 @@ function prefixPlugin(prefix, { scopeRoot = false } = {}) { if (sel.startsWith(':root')) { return scopeRoot ? prefix + sel.slice(':root'.length) : sel } - return `${prefix} ${sel}` + return prefix ? `${prefix} ${sel}` : sel }) }, } } prefixPlugin.postcss = true +// Clean stale CSS outputs so renames/removals don't leave leftovers in dist/ +fs.rmSync(DIST_DIR, { recursive: true, force: true }) +fs.rmSync(path.join(DIST_ROOT, 'styles.css'), { force: true }) + fs.mkdirSync(OUTPUT_DIR, { recursive: true }) fs.mkdirSync(DIST_DIR, { recursive: true }) +fs.mkdirSync(DIST_ROOT, { recursive: true }) console.log(`Building flavor CSS for ${FLAVORS.length} flavors...`) @@ -136,6 +145,17 @@ for (const flavor of FLAVORS) { ]).process(css, { from: undefined }) combinedParts.push(combined.css) + // Baseline: emit pepper with no prefix at the top of all.css so consumers + // get styled components even without wrapping with . + // Specificity of `[data-flavor="xxx"] .foo` (0,0,1,1) beats `.foo` + // (0,0,1,0), so an explicit FlavorProvider still overrides the baseline. + if (flavor === 'pepper') { + const baseline = postcss([prefixPlugin('')]).process(css, { + from: undefined, + }) + combinedParts.unshift(baseline.css) + } + console.log(` ✓ ${flavor}.css`) } catch (err) { console.error(` ✗ ${flavor}: ${err.message}`) @@ -143,11 +163,16 @@ for (const flavor of FLAVORS) { } } -// Combined CSS: single @font-face at the top, then all 7 flavors +// Combined CSS: @font-face + pepper baseline (unscoped) + 7 flavors scoped. +// Emitted at dist/styles.css to match modern convention +// (e.g. Mantine's `@mantine/core/styles.css`, Radix Themes' `@radix-ui/themes/styles.css`) +// and to signal "this is the default stylesheet consumers should import". fs.writeFileSync( - path.join(DIST_DIR, 'all.css'), + path.join(DIST_ROOT, 'styles.css'), iconFontFace + combinedParts.join('\n') ) -console.log(` ✓ all.css (combined, ${FLAVORS.length} flavors)`) +console.log( + ` ✓ styles.css (pepper baseline + ${FLAVORS.length} flavors scoped)` +) console.log('Done!') diff --git a/src/FlavorProvider/FlavorProvider.tsx b/src/FlavorProvider/FlavorProvider.tsx index 078e3bc..58e4320 100644 --- a/src/FlavorProvider/FlavorProvider.tsx +++ b/src/FlavorProvider/FlavorProvider.tsx @@ -18,8 +18,9 @@ export interface Props { /** * 配下の wip-ui コンポーネントに適用するフレーバー(デザイントークンのテーマ)を切り替えるための Provider。 - * 単一フレーバーを root で固定する用途のほか、ネストして領域ごとに異なるフレーバーを適用することもできる。 - * 利用前に `wip-ui/css/all.css`(全フレーバー同梱)または `wip-ui/css/{flavor}.css`(単一フレーバー)の import が必要。 + * `wip-ui/styles.css` をインポートするだけで pepper がデフォルト適用されるため、 + * この Provider はフレーバーを切り替えたい場合(別フレーバーの明示指定・領域別ネスト等)にのみ使用する。 + * 利用前に `wip-ui/styles.css`(全フレーバー同梱)または `wip-ui/css/{flavor}.css`(単一フレーバー)の import が必要。 * * @summary フレーバー(デザイントークンのテーマ)を切り替えるProvider */