From 6637fba75e64a20fbd0b8b2e7a069cc84dc0402c Mon Sep 17 00:00:00 2001 From: sangun-kang Date: Fri, 24 Apr 2026 10:04:56 +0900 Subject: [PATCH 1/2] feat(flavor): emit pepper baseline unscoped in all.css MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, consumers had to wrap their app with AND import the CSS for any component to render styled. Forgetting either produced silently unstyled output — a sharp edge inconsistent with most component libraries (MUI/Chakra render with a default theme, no Provider required). The published package now leads with "import CSS and you are done", and is reserved for explicit flavor switching or nested-region theming. - scripts/build-flavor-css.mjs: for pepper, additionally emit an unprefixed pass and prepend it to the top of dist/css/all.css. Specificity of `[data-flavor="xxx"] .foo` (0,0,3,0) beats `.foo` (0,0,2,0), so any FlavorProvider still overrides the baseline. The per-flavor dist/css/{flavor}.css files are intentionally unchanged — that optimization path still requires matching FlavorProvider flavor because the single-flavor file has no other context. - README.md: reframe the usage section so the minimal "just import CSS" example comes first, and FlavorProvider is shown only in the override/nesting example. Document the existing source-order caveat for nested providers (outer lolipop + inner pepper will resolve to lolipop because CSS descendant selectors match any ancestor and the later flavor wins on tie). - FlavorProvider JSDoc: note that pepper now applies by default via CSS baseline, so the Provider is only needed for explicit flavor choices. Size: all.css 2.93 MB -> 3.29 MB (~+19 KB gzip) due to the added unscoped pepper pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 23 +++++++++++++---------- scripts/build-flavor-css.mjs | 22 +++++++++++++++++++--- src/FlavorProvider/FlavorProvider.tsx | 3 ++- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ba79881..55e1bbc 100644 --- a/README.md +++ b/README.md @@ -24,33 +24,36 @@ npm install react react-dom react-aria-components ## 使い方 -アプリのルートで `FlavorProvider` を配置し、全フレーバー同梱の CSS を読み込みます。`flavor` prop は省略時 `pepper` が適用されます。 +CSS をインポートするだけで、デフォルトの `pepper` フレーバーでコンポーネントが描画されます。 + +```tsx +import { Button } from 'wip-ui' +import 'wip-ui/css/all.css' + +export const App = () => +``` + +別のフレーバーを適用したい場合、あるいは領域ごとに異なるフレーバーを切り替えたい場合は `FlavorProvider` で対象領域を囲みます。 ```tsx import { FlavorProvider, Button } from 'wip-ui' import 'wip-ui/css/all.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` は以下の全フレーバーを含みます。 diff --git a/scripts/build-flavor-css.mjs b/scripts/build-flavor-css.mjs index 75b2bbb..88f3b8b 100644 --- a/scripts/build-flavor-css.mjs +++ b/scripts/build-flavor-css.mjs @@ -34,6 +34,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,7 +55,7 @@ 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 }) }, } @@ -136,6 +139,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 +157,13 @@ 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 fs.writeFileSync( path.join(DIST_DIR, 'all.css'), iconFontFace + combinedParts.join('\n') ) -console.log(` ✓ all.css (combined, ${FLAVORS.length} flavors)`) +console.log( + ` ✓ all.css (pepper baseline + ${FLAVORS.length} flavors scoped)` +) console.log('Done!') diff --git a/src/FlavorProvider/FlavorProvider.tsx b/src/FlavorProvider/FlavorProvider.tsx index 078e3bc..20ccfe1 100644 --- a/src/FlavorProvider/FlavorProvider.tsx +++ b/src/FlavorProvider/FlavorProvider.tsx @@ -18,7 +18,8 @@ export interface Props { /** * 配下の wip-ui コンポーネントに適用するフレーバー(デザイントークンのテーマ)を切り替えるための Provider。 - * 単一フレーバーを root で固定する用途のほか、ネストして領域ごとに異なるフレーバーを適用することもできる。 + * `wip-ui/css/all.css` をインポートするだけで pepper がデフォルト適用されるため、 + * この Provider はフレーバーを切り替えたい場合(別フレーバーの明示指定・領域別ネスト等)にのみ使用する。 * 利用前に `wip-ui/css/all.css`(全フレーバー同梱)または `wip-ui/css/{flavor}.css`(単一フレーバー)の import が必要。 * * @summary フレーバー(デザイントークンのテーマ)を切り替えるProvider From ab04d64043d5d9addc99570859ac6581ae74ea53 Mon Sep 17 00:00:00 2001 From: sangun-kang Date: Fri, 24 Apr 2026 10:28:07 +0900 Subject: [PATCH 2/2] chore(css): rename all.css to styles.css at package root Signal "this is the default stylesheet you should import" via naming + path placement. `all.css` reads like "one of seven", while the new path `wip-ui/styles.css` matches the convention consumers already know from Mantine (`@mantine/core/styles.css`) and Radix Themes (`@radix-ui/themes/styles.css`). - scripts/build-flavor-css.mjs: emit dist/styles.css (package root) instead of dist/css/all.css. Per-flavor dist/css/{flavor}.css files are unchanged. Also clean DIST_DIR and stale dist/styles.css before writing, so renames/removals don't leave orphaned files in dist/. - package.json: expose "./styles.css" subpath export. Per-flavor "./css/*.css" pattern retained for the bundle-size-optimization path. - README.md + FlavorProvider JSDoc: reference wip-ui/styles.css. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 6 +++--- package.json | 1 + scripts/build-flavor-css.mjs | 15 ++++++++++++--- src/FlavorProvider/FlavorProvider.tsx | 4 ++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 55e1bbc..536fd4c 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ CSS をインポートするだけで、デフォルトの `pepper` フレーバ ```tsx import { Button } from 'wip-ui' -import 'wip-ui/css/all.css' +import 'wip-ui/styles.css' export const App = () => ``` @@ -37,7 +37,7 @@ export const App = () => ```tsx import { FlavorProvider, Button } from 'wip-ui' -import 'wip-ui/css/all.css' +import 'wip-ui/styles.css' export const App = () => ( @@ -56,7 +56,7 @@ import { Button } from 'wip-ui/Button' ### 利用可能なフレーバー -`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 88f3b8b..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', @@ -62,8 +63,13 @@ function prefixPlugin(prefix, { scopeRoot = false } = {}) { } 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...`) @@ -157,13 +163,16 @@ for (const flavor of FLAVORS) { } } -// Combined CSS: @font-face + pepper baseline (unscoped) + 7 flavors scoped +// 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 (pepper baseline + ${FLAVORS.length} flavors scoped)` + ` ✓ styles.css (pepper baseline + ${FLAVORS.length} flavors scoped)` ) console.log('Done!') diff --git a/src/FlavorProvider/FlavorProvider.tsx b/src/FlavorProvider/FlavorProvider.tsx index 20ccfe1..58e4320 100644 --- a/src/FlavorProvider/FlavorProvider.tsx +++ b/src/FlavorProvider/FlavorProvider.tsx @@ -18,9 +18,9 @@ export interface Props { /** * 配下の wip-ui コンポーネントに適用するフレーバー(デザイントークンのテーマ)を切り替えるための Provider。 - * `wip-ui/css/all.css` をインポートするだけで pepper がデフォルト適用されるため、 + * `wip-ui/styles.css` をインポートするだけで pepper がデフォルト適用されるため、 * この Provider はフレーバーを切り替えたい場合(別フレーバーの明示指定・領域別ネスト等)にのみ使用する。 - * 利用前に `wip-ui/css/all.css`(全フレーバー同梱)または `wip-ui/css/{flavor}.css`(単一フレーバー)の import が必要。 + * 利用前に `wip-ui/styles.css`(全フレーバー同梱)または `wip-ui/css/{flavor}.css`(単一フレーバー)の import が必要。 * * @summary フレーバー(デザイントークンのテーマ)を切り替えるProvider */