Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => <Button>クリック</Button>
```

別のフレーバーを適用したい場合、あるいは領域ごとに異なるフレーバーを切り替えたい場合は `FlavorProvider` で対象領域を囲みます。

```tsx
import { FlavorProvider, Button } from 'wip-ui'
import 'wip-ui/css/all.css'
import 'wip-ui/styles.css'

export const App = () => (
<FlavorProvider>
<FlavorProvider flavor="minne">
<Button>クリック</Button>
</FlavorProvider>
)
```

別のフレーバーに切り替える場合は `flavor` prop を指定します。ネストして領域ごとに異なるフレーバーを適用することもできます。

```tsx
<FlavorProvider flavor="minne">
<Button>クリック</Button>
</FlavorProvider>
```

コンポーネント単位でのインポートも可能です。

```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`
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
},
"./types": null,
"./utils": null,
"./styles.css": "./dist/styles.css",
"./css/*.css": "./dist/css/*.css"
},
"files": [
Expand Down
33 changes: 29 additions & 4 deletions scripts/build-flavor-css.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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 <FlavorProvider>).
function prefixPlugin(prefix, { scopeRoot = false } = {}) {
return {
postcssPlugin: 'prefix-flavor',
Expand All @@ -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...`)

Expand Down Expand Up @@ -136,18 +145,34 @@ 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 <FlavorProvider>.
// 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}`)
process.exit(1)
}
}

// 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!')
5 changes: 3 additions & 2 deletions src/FlavorProvider/FlavorProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
Loading