sparkle-design-cli 2.4.1 → 2.5.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  Sparkle Design のプロジェクト初期セットアップ、CSS 生成、導入先プロジェクト向けのアンチパターン検査、AI エージェント向け guard 設定を行う CLI ツールです。
4
4
 
5
+ この README には**導入して動かすまでに要る最低限**だけを置いています。個別の詳細は下記を参照してください。
6
+
7
+ | ドキュメント | 内容 |
8
+ | ---------------------------------------------- | --------------------------------------------------------------------------- |
9
+ | [docs/anti-patterns.md](docs/anti-patterns.md) | `check` の抑制方法、新セマンティックトークンへの移行、余白スケール |
10
+ | [docs/config.md](docs/config.md) | `sparkle.config.json` の `extend`(フォント追加・カスタムカラー・任意 CSS) |
11
+ | [docs/theming.md](docs/theming.md) | 複数のテーマ配色を 1 デプロイでサポートする(`generate --scope`) |
12
+ | [docs/manual-setup.md](docs/manual-setup.md) | Next.js / Vite 以外への手動セットアップ |
13
+ | [docs/plugins.md](docs/plugins.md) | アンチパターン検査をプラグインで拡張する |
14
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | このリポジトリ自体の開発手順 |
15
+
16
+ **有効なルールの一覧はドキュメントではなく `npx sparkle-design-cli rules` が出します。**
17
+
5
18
  ## クイックスタート
6
19
 
7
20
  既存の Next.js / Vite プロジェクトで以下を実行するだけで導入完了します。詳細は [`setup` セクション](#setup-プロジェクトのフルセットアップ) を参照してください。
@@ -57,6 +70,9 @@ npx sparkle-design-cli check src --strict
57
70
 
58
71
  # AI 向けに JSON で検査結果と手動確認項目を取得
59
72
  npx sparkle-design-cli check src --format json
73
+
74
+ # 現在有効なアンチパターンルールを一覧表示(プラグイン由来のものも含む)
75
+ npx sparkle-design-cli rules
60
76
  ```
61
77
 
62
78
  ### generate: 基本的な使用方法
@@ -101,7 +117,7 @@ sparkle-design-cli generate --globals-path src/styles/app.css
101
117
  # CI 向け: @source 注入や Tailwind import 欠落などの失敗を exit 1 にする
102
118
  sparkle-design-cli generate --strict
103
119
 
104
- # 単一バンドル内でランタイムにテーマ切替したい場合(詳細は「複数のテーマ配色を1デプロイでサポートしたい場合」参照)
120
+ # 単一バンドル内でランタイムにテーマ切替したい場合(詳細は docs/theming.md 参照)
105
121
  sparkle-design-cli generate -c ./config/sparkle.admin.json --scope '[data-tenant-theme="admin"]' -o ./src/styles/sparkle-admin-scope.css
106
122
  ```
107
123
 
@@ -116,7 +132,7 @@ sparkle-design-cli generate -c ./config/sparkle.admin.json --scope '[data-tenant
116
132
  - デザインシステムパッケージが `package.json` に入っているのに Tailwind エントリ CSS が見つからない
117
133
  - エントリ CSS はあるが `@import "tailwindcss";` が書かれていない(警告メッセージに追記すべき行まで actionable に表示)
118
134
  - エントリ CSS への書き込みに失敗した
119
- - `--scope <セレクタ>`: `@theme inline` には触れず、セマンティックトークン(`--color-primary-*` 等)だけを実値までリテラル化して指定セレクタの中にラップ出力する。単一バンドル内でランタイムにテーマ切替したい場合向け(`-o/--output` の指定が必須)。詳細は「複数のテーマ配色を1デプロイでサポートしたい場合」の「ケース B」を参照
135
+ - `--scope <セレクタ>`: `@theme inline` には触れず、セマンティックトークン(`--color-primary-*` 等)だけを実値までリテラル化して指定セレクタの中にラップ出力する。単一バンドル内でランタイムにテーマ切替したい場合向け(`-o/--output` の指定が必須)。詳細は[docs/theming.md](docs/theming.md) の「ケース B」を参照
120
136
 
121
137
  ### check: アンチパターン検査
122
138
 
@@ -139,143 +155,47 @@ sparkle-design-cli check --help
139
155
  #### check オプション一覧
140
156
 
141
157
  - `-h, --help`: ヘルプメッセージを表示
142
- - `--strict`: 違反が見つかった場合に exit code 1 で終了
158
+ - `--strict`: **`error` の違反があるか、実行に失敗して未検査のルールがある**場合に exit code 1 で終了(`warning` / `info` は exit code に影響しません)
143
159
  - `--format <text|json>`: 出力形式。AI 連携では `json` を推奨
144
160
 
145
- #### 現在検出するルール
146
-
147
- - フォーム入力に Dialog を使わない
148
- - DialogCancel/DialogAction を Button で二重ラップしない
149
- - children なしの Button に prefixIcon / suffixIcon を使わない
150
- - Material Symbols を className 直書きで使わない
151
- - shadcn/ui 既定 token を Sparkle Design 内へ持ち込まない
152
- - Tailwind デフォルト typography を Sparkle Design コンポーネント内で使わない
153
- - CardTitle に typography 系クラスを付与しない
154
- - CardControl に Button / IconButton 以外を入れない
155
- - Card 系コンポーネントのデフォルト padding を安易に上書きしない
156
- - asChild 使用時に prefixIcon / suffixIcon / isLoading を使わない
157
- - Sparkle Design コンポーネントでは isDisabled を使う
158
- - Button の prefixIcon / suffixIcon に JSX を渡さない
159
- - Icon の children にテキストを渡さない
160
- - クリック可能な Card は ClickableCard を使う(button / a / role="button" でラップしない)
161
-
162
- #### 導入先での推奨設定
163
-
164
- ```json
165
- {
166
- "scripts": {
167
- "lint:sparkle": "npx --yes sparkle-design-cli check src --strict"
168
- }
169
- }
170
- ```
171
-
172
- AI エージェントや CI からこの `lint:sparkle` を呼ぶ運用にすると、ガイドラインの注意書きだけに頼らず機械的に検査できます。
161
+ #### ルールの強さ(severity)
173
162
 
174
- #### アンチパターン検知の拡張(プラグイン)
163
+ すべてのルールは強さを持ちます。すべてを同じ重みで扱うと、全部を過剰に守って不自然な実装になるか、全部を読み流すかの両極端に振れるためです。
175
164
 
176
- 他のコンポーネントライブラリ(社内拡張など)に固有のアンチパターン検知を、`sparkle-design-cli` 本体に変更を加えず追加できる拡張機構があります。
165
+ | 強さ | 意味 | `--strict` / stop-hook |
166
+ | --------- | -------------------------------------- | ------------------------ |
167
+ | `error` | 例外なし。必ず直す | **失敗させる(exit 1)** |
168
+ | `warning` | 原則ダメだが、理由があれば例外を認める | 失敗させない(報告のみ) |
169
+ | `info` | 参考。従わなくてもよい選択肢 | 失敗させない(報告のみ) |
177
170
 
178
- ##### 仕組み
171
+ 出力には `path:line [severity] [rule-id] 説明` の形で強さとルール ID が入ります。ルール ID は内容が変わっても変えないので、指摘の根拠を後から辿れます。
179
172
 
180
- プラグインを提供するパッケージは、自身の `package.json` に検知ルールのエントリを宣言します:
173
+ JSON 出力では `summary.severityCounts` に内訳が、`summary.blockingFindingCount` に exit code へ効く件数が入ります。
181
174
 
182
- ```json
183
- {
184
- "name": "@your-org/your-design-extensions",
185
- "sparkleCli": {
186
- "antiPatterns": "./anti-patterns/index.js"
187
- }
188
- }
189
- ```
175
+ **ルールの実行に失敗した場合**(プラグインの不具合など)は、`report.skippedRules` と `summary.skippedRuleCount` に記録され、`--strict` は失敗します。ルールが落ちた状態は「違反ゼロ」ではなく「検査していない」ためです。stderr にも警告が出ます(同じルールについては 1 回だけ)。
190
176
 
191
- エントリファイルは `defineAntiPatternPlugin` を使ってプラグインを default export します:
192
-
193
- ```js
194
- import { defineAntiPatternPlugin } from 'sparkle-design-cli/plugin';
195
-
196
- export default defineAntiPatternPlugin({
197
- groups: [
198
- {
199
- id: 'your-org-foocard-nesting',
200
- check: {
201
- description: 'FooCard を別の FooCard でラップしないでください。',
202
- recommendation: '入れ子表示が必要なら FooStack を使ってください。',
203
- pattern: /<FooCard[^>]*>[\s\S]*?<FooCard/g,
204
- },
205
- },
206
- ],
207
- manualReviewReminders: [
208
- {
209
- id: 'your-org-foocard-color',
210
- message: 'FooCard の color token が brand に揃っているか確認してください。',
211
- },
212
- ],
213
- });
214
- ```
177
+ #### 検出するルールを調べる
215
178
 
216
- ##### 複雑な検出(`match` とヘルパー)
217
-
218
- 単純な正規表現では安全に書けない検出(accessible name の有無、prop の組み合わせ、JSX 式を意識した走査など)には、`check.pattern` の代わりに `check.match` を使います。`match` は `(content, helpers)` で呼ばれ、第 2 引数の `helpers` に JSX パースヘルパーが **CLI から注入** されます。各パッケージがパース処理を再実装して同じ false-positive / negative を踏むのを防ぐ仕組みです。
219
-
220
- ```js
221
- // sparkle-design-cli を import しない(plain object を default export)
222
- export default {
223
- groups: [
224
- {
225
- id: 'your-org-avatar-accessible-name',
226
- check: {
227
- description: 'Avatar に accessible name がありません。',
228
- recommendation: 'aria-label か alt を指定してください。',
229
- // helpers = { matchOpeningTags, hasProp, isMultipleTypeProp, findOpeningTagEnd }
230
- match: (content, { matchOpeningTags, hasProp }) =>
231
- matchOpeningTags(
232
- content,
233
- 'Avatar',
234
- (props) =>
235
- !hasProp(props, 'src') &&
236
- !hasProp(props, 'aria-label') &&
237
- !hasProp(props, 'aria-labelledby')
238
- ),
239
- },
240
- },
241
- ],
242
- };
179
+ ```bash
180
+ npx sparkle-design-cli rules # severity 別に一覧表示
181
+ npx sparkle-design-cli rules --format json
243
182
  ```
244
183
 
245
- 注入されるヘルパー(`check.match` の第 2 引数):
246
-
247
- | ヘルパー | 用途 |
248
- | ----------------------------------------------- | ------------------------------------------------------------------------------------ |
249
- | `matchOpeningTags(content, tagName, predicate)` | `<Tag ...>` / `<Tag ... />` を走査し predicate が真のものを `{ index, text }` で返す |
250
- | `hasProp(propsBlock, propName)` | prop が指定されているかを厳密判定(`aria-*` / `data-*` を誤検出しない) |
251
- | `isMultipleTypeProp(propsBlock)` | `type` が静的に `"multiple"` か判定(動的式は `false`) |
252
- | `findOpeningTagEnd(content, startIdx)` | 低レベル: 開きタグの閉じ `>` のオフセット(文字列 / JSX 式を考慮)。無ければ `-1` |
253
-
254
- > 💡 `match` を使うプラグインは(上の `pattern` 例の `defineAntiPatternPlugin` import と違い)`sparkle-design-cli` を **import せず** plain object として default export してください。`helpers` はランタイムで CLI が注入するため import は不要で、生 JS のまま配布され npx 経由で実行される consumer 環境でも確実に解決されます。`pattern` と `match` は排他で、どちらか一方のみ指定します。
255
-
256
- ##### 自動 discovery
184
+ **一覧をドキュメントに書かないのは、有効なルールがプロジェクトごとに違うためです。** プラグインはコンシューマの `package.json` から自動発見されるので、固定の一覧はプラグインを入れた利用者にとって最初から不正確になります。
257
185
 
258
- `sparkle-design-cli check` 実行時、CLI は consumer プロジェクトの `package.json` の `dependencies` / `devDependencies` / `peerDependencies` / `optionalDependencies` を走査し、`sparkleCli.antiPatterns` を宣言しているパッケージを発見次第ロードしてビルトインのルールにマージします。
186
+ 個別ルールの背景・移行手順・抑制方法は [docs/anti-patterns.md](docs/anti-patterns.md) を参照してください。
259
187
 
260
- - プラグインパッケージが install されていなければ何も起きません
261
- - ロードや評価で失敗したプラグインは warn を出してスキップし、残りのルールで check は継続されます
262
- - ルール ID はビルトイン・他プラグインと名前空間を共有するため、`your-org-` のようなプレフィックスを付けて衝突を避けてください
263
-
264
- > ⚠️ **信頼境界**: プラグインのエントリファイルは `import()` で **任意の JavaScript を実行** します。これは Node.js の通常のパッケージ依存と同じ性質ですが、`sparkle-design-cli check` 実行時に走るコードが増えるという点で意識しておく必要があります。プラグインは信頼できる org / 著者のパッケージのみインストールしてください。
265
-
266
- ##### 契約仕様の確認
267
-
268
- 最新の契約仕様は CLI から直接出力できます:
269
-
270
- ```bash
271
- # 契約 shape のドキュメントを出力
272
- npx --yes sparkle-design-cli plugin-spec
188
+ #### 導入先での推奨設定
273
189
 
274
- # 現在のプロジェクトで discover されたプラグインを一覧
275
- npx --yes sparkle-design-cli plugin-spec --list
190
+ ```json
191
+ {
192
+ "scripts": {
193
+ "lint:sparkle": "npx --yes sparkle-design-cli check src --strict"
194
+ }
195
+ }
276
196
  ```
277
197
 
278
- 完全な型定義は `node_modules/sparkle-design-cli/lib/plugin-api.js` の JSDoc を参照してください。
198
+ AI エージェントや CI からこの `lint:sparkle` を呼ぶ運用にすると、ガイドラインの注意書きだけに頼らず機械的に検査できます。
279
199
 
280
200
  ### setup: プロジェクトのフルセットアップ
281
201
 
@@ -332,120 +252,6 @@ npx --yes sparkle-design-cli setup --assistant claude --skip-install --skip-scaf
332
252
 
333
253
  `setup` は通常実行時も JSON サマリーを stdout に表示します。`--dry-run` を付けると、その JSON を表示したままファイル変更だけを抑止します。
334
254
 
335
- ### 手動セットアップ(Next.js / Vite 以外のプロジェクト向け)
336
-
337
- `setup` コマンドは **Next.js App Router** と **Vite** を前提に scaffold / entry CSS の配置を自動判定します。Remix / Astro / TanStack Start など他のフレームワークや、独自ディレクトリ構成のプロジェクトでは自動判定が外れるため、以下の手順で手動セットアップすることを推奨します。
338
-
339
- #### 手順
340
-
341
- **1. パッケージをインストール**
342
-
343
- ```bash
344
- # 本体
345
- pnpm add sparkle-design # or npm install / yarn add / bun add
346
-
347
- # Tailwind v4
348
- pnpm add -D tailwindcss @tailwindcss/postcss
349
- ```
350
-
351
- **2. `sparkle.config.json` をプロジェクトルートに作成**
352
-
353
- ```json
354
- {
355
- "primary": "blue",
356
- "font-pro": "Inter",
357
- "font-mono": "JetBrains Mono",
358
- "radius": "md"
359
- }
360
- ```
361
-
362
- 選択肢の詳細は本 README の「[設定オプション](#設定オプション)」を参照してください。
363
-
364
- **3. `postcss.config.mjs` をプロジェクトルートに作成**
365
-
366
- ```js
367
- export default {
368
- plugins: {
369
- '@tailwindcss/postcss': {},
370
- },
371
- };
372
- ```
373
-
374
- **4. Tailwind エントリ CSS を自前で用意**
375
-
376
- 既存プロジェクトに組み込む場合、`@import "tailwindcss";` を含む CSS ファイル(例: `src/styles/app.css`)があればそれを使います。無ければ作成してアプリケーションのエントリから import します。
377
-
378
- **5. `generate` を実行**
379
-
380
- ```bash
381
- npx --yes sparkle-design-cli generate
382
- ```
383
-
384
- これで `sparkle-design.css` が `src/app/sparkle-design.css` に生成され、`SparkleHead.tsx` も同じ場所に出ます。エントリ CSS の検出に失敗する場合は `sparkle.config.json` の `extend.globals-path` に明示指定してください。
385
-
386
- ```json
387
- {
388
- "primary": "blue",
389
- "extend": {
390
- "globals-path": "src/styles/app.css"
391
- }
392
- }
393
- ```
394
-
395
- **6. フォントの `<link>` タグを手動で配置**
396
-
397
- Next.js / Vite 以外ではアプリケーションフレームワークごとに「`<head>` 相当」の配置方法が異なります。自動生成される `SparkleHead.tsx` はそのまま使えるはずですが、使えない場合は中身(`preconnect` + Google Fonts の `<link>` タグ群)を自前のレイアウトにコピーしてください。
398
-
399
- たとえば Astro なら `src/layouts/BaseLayout.astro` の `<head>` に直接書きます:
400
-
401
- ```html
402
- <link rel="preconnect" href="https://fonts.googleapis.com" />
403
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
404
- <link
405
- rel="stylesheet"
406
- href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:FILL,wght@0..1,500&display=block"
407
- />
408
- <!-- sparkle.config.json の font-pro / font-mono に合わせた Google Fonts URL -->
409
- <link
410
- rel="stylesheet"
411
- href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap"
412
- />
413
- <link
414
- rel="stylesheet"
415
- href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap"
416
- />
417
- ```
418
-
419
- **7. アンチパターン検査を package.json に追加(任意)**
420
-
421
- ```json
422
- {
423
- "scripts": {
424
- "lint:sparkle": "npx --yes sparkle-design-cli check src --strict",
425
- "lint:sparkle:json": "npx --yes sparkle-design-cli check src --format json"
426
- }
427
- }
428
- ```
429
-
430
- **8. AI エージェントを使うなら Guard ブロックと hook を手動で配置**
431
-
432
- AI ガード(`CLAUDE.md` / `AGENTS.md`)と hook 設定(`.claude/settings.json` / `.cursor/hooks.json` / `.codex/hooks.json`)は `setup` に任せるのが最も楽です。Cursor 用 Guard は rc.4 から AGENTS.md に統一しました(`.cursor/rules/*.mdc` は条件付き読み込みのため)。
433
-
434
- ```bash
435
- # ガード追加・hook 設定のみ(パッケージインストールや scaffold はスキップ)
436
- npx --yes sparkle-design-cli setup --assistant claude --skip-install --skip-scaffold --skip-generate
437
- ```
438
-
439
- この `--skip-*` 組み合わせは手動セットアップ済みのプロジェクトに対して Guard + hook だけ差し込むのに使えます。
440
-
441
- #### 既知の未対応ケース
442
-
443
- - **モノレポ(workspaces)**: `generate` は実行した cwd からパッケージマネージャーを遡及検出しないため、サブパッケージで直接実行するのが確実です。
444
- - **CSS 以外のエントリ(CSS-in-JS / vanilla-extract 等)**: Sparkle Design は Tailwind v4 の `@source` + CSS variable 前提で作られているため、ビルドパイプラインの外で CSS を扱うソリューションは対象外です。
445
- - **Tailwind v3**: `@source` ディレクティブ依存のため v4 必須です。
446
-
447
- Next.js / Vite 以外で導入したい方で困ったときは Issue で教えていただけると助かります。
448
-
449
255
  ## 設定ファイル (sparkle.config.json)
450
256
 
451
257
  ### 設定ファイルの作成
@@ -461,233 +267,11 @@ Next.js / Vite 以外で導入したい方で困ったときは Issue で教え
461
267
 
462
268
  #### Core(プラグインが出力)
463
269
 
464
- - `primary`: プライマリカラー(必須)。次の7色のいずれかを指定してください: `blue` / `red` / `orange` / `yellow` / `purple` / `green` / `pink`。未指定、またはこれら以外の値を指定した場合、`generate` は例外を投げて停止します(そのブランドカラー専用の `--color-primary-*` / `--color-gray-*` を用意していないため、`var()` の参照先が存在せず壊れた CSS になってしまうのを防ぐためのバリデーションです)。7色にないブランドカラーを使いたい場合は [extend.custom-css](#extendcustom-css) を参照してください。
270
+ - `primary`: プライマリカラー(必須)。次の7色のいずれかを指定してください: `blue` / `red` / `orange` / `yellow` / `purple` / `green` / `pink`。未指定、またはこれら以外の値を指定した場合、`generate` は例外を投げて停止します(そのブランドカラー専用の `--color-primary-*` / `--color-gray-*` を用意していないため、`var()` の参照先が存在せず壊れた CSS になってしまうのを防ぐためのバリデーションです)。7色にないブランドカラーを使いたい場合は [docs/config.md の `extend.custom-css`](docs/config.md#extendcustom-css) を参照してください。
465
271
  - `font-pro`: プロポーショナルフォント([Google Fonts](https://fonts.google.com/) の名前)
466
272
  - `font-mono`: モノスペースフォント([Google Fonts](https://fonts.google.com/) の名前)
467
273
  - `radius`: 角丸設定(必須)。次のいずれかを指定してください: `none` / `xs` / `sm` / `md` / `lg` / `xl` / `2xl` / `3xl`。未指定、またはこれら以外の値を指定した場合、`primary` と同様に `generate` は例外を投げて停止します(無効な値だと `--radius-action` が壊れた参照のまま出力されるのを防ぐためです)。
468
274
 
469
- ### extend セクション(拡張設定)
470
-
471
- Figma プラグインが出力する基本4項目に加えて、プロジェクト固有の拡張を `extend` セクションにまとめます。`extend` はオブジェクト直書き(推奨)またはファイルパスを指定できます。
472
-
473
- ```json
474
- {
475
- "primary": "blue",
476
- "font-pro": "Montserrat",
477
- "font-mono": "Roboto Mono",
478
- "radius": "md",
479
- "extend": {
480
- "fonts": {
481
- "pro": [
482
- { "family": "Montserrat", "weights": [500, 600, 700] },
483
- { "family": "Noto Sans JP", "weights": [400, 500, 600, 700] }
484
- ],
485
- "mono": [{ "family": "Roboto Mono", "weights": [400, 700] }]
486
- },
487
- "source-packages": ["@goodpatch/sparkle-design-internal"],
488
- "custom-css": "./src/app/custom-tokens.css"
489
- }
490
- }
491
- ```
492
-
493
- ファイル参照も可能です:
494
-
495
- ```json
496
- {
497
- "primary": "blue",
498
- "font-pro": "Montserrat",
499
- "font-mono": "Roboto Mono",
500
- "radius": "md",
501
- "extend": "./sparkle.extend.json"
502
- }
503
- ```
504
-
505
- #### extend.fonts
506
-
507
- フォントごとにウェイトを個別指定。`extend.fonts` がない場合は `font-pro` / `font-mono` + デフォルトウェイト `[400, 700]` が使われます。`extend.fonts` が存在しても `fonts.pro` / `fonts.mono` のどちらかが未指定なら、そのスロットのみ `font-pro` / `font-mono` にフォールバックします。同じフォントファミリーが `pro` と `mono` で重複する場合、ウェイトはマージされ import は 1 行に統合されます。
508
-
509
- #### extend.source-packages
510
-
511
- `sparkle-design` を npm パッケージとして利用する場合に必須。Tailwind エントリ CSS(自動検出)に `@source` ディレクティブを自動挿入します。`sparkle-design` は常にデフォルトで含まれます。
512
-
513
- #### extend.custom-css
514
-
515
- プロジェクト固有のカスタムトークンの CSS ファイルパス。Tailwind エントリ CSS に `@import` が自動挿入されます。`sparkle-design.css` に直接追加すると `generate` 実行時に上書きされるため、必ず別ファイルに分離してください。
516
-
517
- この `@import` は `sparkle-design.css` の直後に挿入されるため、`custom-css` 側で書いた `:root { --color-xxx: ... }` は CSS の cascade(後勝ち)でそのまま `sparkle-design.css` のトークンを上書きします。属性セレクタや `!important` を使う必要はありません。
518
-
519
- ##### カスタムブランドカラーを primary にしたい場合
520
-
521
- `primary` は 7 色のいずれかしか受け付けません(前述の Core セクション参照)。7 色にないブランドカラーを使いたい場合は、`primary` は 7 色から見た目が近いものを仮に選んだ上で、`extend.custom-css` で `--color-primary-*` と `--color-gray-*` をブランドカラー基準の値で丸ごと再定義してください。`primary` の選択自体は `custom-css` 側の定義で完全に上書きされるため実質的な意味を持たなくなりますが、フィールドとしては有効な値を入れておく必要があります。
522
-
523
- ```css
524
- /* custom-tokens.css */
525
- :root {
526
- /* ブランドカラー基準の primary パレット(50〜900) */
527
- --color-primary-50: oklch(97% 0.02 250);
528
- /* ... */
529
- --color-primary-500: oklch(55% 0.18 250);
530
- /* ... */
531
- --color-primary-900: oklch(20% 0.08 250);
532
-
533
- /* primary に合わせた gray パレット(50〜900)も必ず一緒に定義する。
534
- gray だけ 7 色デフォルトのままだと、primary だけ浮いて見えるトーンずれが起きる */
535
- --color-gray-50: oklch(98% 0.005 250);
536
- /* ... */
537
- --color-gray-900: oklch(22% 0.01 250);
538
- }
539
- ```
540
-
541
- `primary` だけを差し替えて `gray` を既定のままにすると、コンポーネントの枠線・背景・テキストに使われる gray 系トークンと primary のトーンが揃わなくなるため、gray も必ずセットで再定義してください。
542
-
543
- ##### `character-*` に無いウェイト(SemiBold 等)を使いたい場合
544
-
545
- `character-*` utility の `font-weight` は Regular(400) / Bold(700) の 2 値のみで、SemiBold(600) や Medium(500) に対応する `character-*` は存在しません(Sparkle Design のプリミティブトークンである `fontWeights.text.regular` / `fontWeights.text.bold` がこの 2 値のみを持つため)。
546
-
547
- 日本語見出しでは Bold(700) が視覚的に重すぎると判断して SemiBold(600) を使いたい、といったプロジェクト固有のウェイト方針がある場合は、`character-*` に SemiBold バリアントの追加を待つのではなく、`extend.custom-css` で独自のユーティリティクラスを定義してください。**`className="character-3-regular-pro font-semibold"` のように Tailwind の `font-semibold` を併用する方法は機能しません**。`sparkle-design.css` は Tailwind の後・`@layer utilities` 内に読み込まれるよう意図的に設計されており(同一詳細度なら後着のルールが勝つ cascade layers の仕様上)、`character-*` 側の `font-weight` が常に優先されるためです。
548
-
549
- ```css
550
- /* custom-typography.css */
551
- @layer utilities {
552
- /* character-3(16px/24px)の SemiBold バリアント。
553
- font-family / font-size / letter-spacing / line-height は
554
- character-* と同じプリミティブトークンをそのまま流用し、
555
- font-weight だけプロジェクト独自の 600 に差し替える */
556
- .character-3-semibold-pro {
557
- font-family: var(--font-family-pro);
558
- font-size: var(--font-size-16);
559
- font-weight: 600;
560
- letter-spacing: var(--letter-spacing-wider);
561
- line-height: var(--line-height-24);
562
- }
563
- }
564
- ```
565
-
566
- `character-*-semibold-*` は既存の `character-*-regular-*` / `character-*-bold-*` と別名のクラスなので、cascade の競合は起きません。`extend.custom-css` は `sparkle-design.css` の直後に `@import` されるため、上記のように新規クラスを追記するだけで有効になります(上書きではないので `!important` や記述順の工夫も不要です)。
567
-
568
- 使用するフォントファミリーが実際に 600 ウェイトのファイルを読み込んでいるかも確認してください。`extend.fonts.pro` / `extend.fonts.mono` の `weights` に `600` を含めていないと、`font-weight: 600` を指定してもブラウザによる疑似太字(faux bold)にフォールバックし、正しい SemiBold の字形になりません。
569
-
570
- 将来 Sparkle Design 本体が正式に `character-N-semibold-*` token を追加した場合は、cascade 上は consumer 側の `custom-css` 定義が勝ち続けるため、その時点で自前定義は削除して本体の token に乗り換えてください(放置すると Sparkle 側の値が更新されても気づかず古い定義が使われ続けます)。
571
-
572
- なお `check` の `tailwind-typography` ルールは `font-semibold` / `font-medium` を検出対象に含めていますが、`character-*-semibold-*` のような独自クラスに置き換えた行は検出パターンに一致しないため、`sparkle-disable-line` を付けなくても違反として検出されなくなります。
573
-
574
- ## 複数のテーマ配色を 1 デプロイでサポートしたい場合
575
-
576
- 管理画面と一般ユーザー画面で配色を出し分けたい、テナントごとに固定の配色バリアントを切り替えたいなど、「少数(数種類程度)の固定配色を 1 つのデプロイでサポートする」ケースの推奨パターンです。**「画面ごとに読み込む CSS を切り替える」場合と「1 つの画面内でランタイムに切り替える」場合とで推奨パターンが異なる**ので、まずどちらのケースかを判断してください。
577
-
578
- ### ケース A: 画面(ビルド)ごとにバリアントが固定される場合 → config を分割して複数の CSS を生成する
579
-
580
- 管理画面 / 一般ユーザー画面でレイアウトごと entry CSS を分けられる、role や tenant に応じて import する CSS を切り替えられる、など「1 画面につき常に 1 バリアントだけを読み込む」運用ができる場合の推奨パターンです。
581
-
582
- `generate` は `-c/--config` と `-o/--output` を組み合わせることで、任意の config から任意の出力先へ CSS を生成できます。バリアントの数だけ config ファイルを用意し、バリアントの数だけ `generate` を実行してください(1 config に複数テーマをまとめて生成する機能はありませんが、CI やスクリプトで複数回呼び出せば十分に運用できます)。
583
-
584
- ```jsonc
585
- // config/sparkle.admin.json
586
- {
587
- "primary": "blue",
588
- "font-pro": "Inter",
589
- "font-mono": "JetBrains Mono",
590
- "radius": "md"
591
- }
592
- ```
593
-
594
- ```jsonc
595
- // config/sparkle.employee.json
596
- {
597
- "primary": "green",
598
- "font-pro": "Inter",
599
- "font-mono": "JetBrains Mono",
600
- "radius": "md"
601
- }
602
- ```
603
-
604
- ```bash
605
- npx sparkle-design-cli generate -c ./config/sparkle.admin.json -o ./src/styles/sparkle-admin.css
606
- npx sparkle-design-cli generate -c ./config/sparkle.employee.json -o ./src/styles/sparkle-employee.css
607
- ```
608
-
609
- あとは consumer 側で、表示するバリアントに応じてどちらの CSS を読み込むかを切り替えるだけです(例: role / tenant を判定できるレイアウト単位で `import "./sparkle-admin.css"` と `import "./sparkle-employee.css"` を出し分ける、Next.js のルートグループごとに読み込む entry CSS を分ける、など)。
610
-
611
- **重要: この 2 つの CSS を同時に import してはいけません。** これはファイルサイズ最適化の話ではなく **correctness 上のハード制約** です。`generate` が出力する CSS は `@theme inline { ... }` という Tailwind v4 の at-rule を含みますが、Tailwind の仕様上 `@theme`(`@theme inline` を含む)は **stylesheet の top-level にしか置けず、かつ 1 種類のトークン定義として扱われます**。2 つの `generate` 出力を同時 import すると、両方の `@theme inline` が同じ top-level スコープで衝突し、後に読み込んだ方の値で utility class(`bg-primary-500` 等)の意味がビルド時に固定されてしまいます(`data-*` 属性の付け外しのようなランタイム操作では変わりません)。**1 画面につき常に 1 バリアントだけを読み込む**構成でのみ、この方式は安全に使えます。
612
-
613
- 「1 つの画面内で属性の付け外しだけでテーマを切り替えたい(ページ遷移や再ビルドを伴わない)」場合は、このケース A ではなく次のケース B を使ってください。
614
-
615
- ### ケース B: 単一バンドル内でランタイムに切り替えたい場合 → `generate --scope`
616
-
617
- SPA の 1 つの JS バンドル内で、`<html data-tenant-theme="admin">` のような属性の付け外しだけで配色を切り替えたい(ページ遷移・再デプロイを伴わない)場合のパターンです。ケース A の「config 分割 + 複数 `generate` の CSS を同時 import する」は、上記の理由(`@theme inline` の衝突)でこのケースには使えません。
618
-
619
- Tailwind v4 では、この種のランタイム切り替え(dark mode 等)を公式に次のパターンでサポートしています([Tailwind公式ドキュメント](https://tailwindcss.com/docs/colors#using-css-variables)より):
620
-
621
- ```css
622
- @import "tailwindcss";
623
-
624
- :root {
625
- --acme-canvas-color: oklch(0.967 0.003 264.542);
626
- }
627
-
628
- [data-theme="dark"] {
629
- --acme-canvas-color: oklch(0.21 0.034 264.665);
630
- }
631
-
632
- @theme inline {
633
- --color-canvas: var(--acme-canvas-color);
634
- }
635
- ```
636
-
637
- ポイントは、`@theme inline` は top-level に **1 つだけ** 置いたまま、実際に切り替えたい値は `@theme` を使わない普通の `:root` / `[data-attr]` スコープ付きセレクタで **参照先の CSS 変数** を上書きする、という構成です。`generate --scope` はこの「参照先を差し替える」役割を、`sparkle.config.json` の解決ロジックを再利用しながら自動生成します。
638
-
639
- ```bash
640
- # ベース(デフォルト表示)となるバリアントは今まで通り通常の generate
641
- npx sparkle-design-cli generate -c ./config/sparkle.employee.json -o ./src/styles/sparkle-employee.css
642
-
643
- # 追加バリアントは --scope で「セマンティックトークンだけをセレクタでラップした差分 CSS」を生成する
644
- npx sparkle-design-cli generate -c ./config/sparkle.admin.json \
645
- --scope '[data-tenant-theme="admin"]' \
646
- -o ./src/styles/sparkle-admin-scope.css
647
- ```
648
-
649
- ```css
650
- /* entry CSS: 両方を同時に import してよい */
651
- @import 'tailwindcss';
652
- @import './sparkle-employee.css'; /* @theme inline を含む通常の generate 出力(ベース) */
653
- @import './sparkle-admin-scope.css'; /* --scope の出力。@theme inline は含まない */
654
- ```
655
-
656
- ```html
657
- <!-- ランタイムでこの属性を付け外しするだけで配色が切り替わる。ページ遷移・再ビルド不要 -->
658
- <html data-tenant-theme="admin"></html>
659
- ```
660
-
661
- `--scope` の出力は次の性質を持ちます:
662
-
663
- - `@theme inline` には一切触れない(ベース側の `generate` 出力にある 1 つだけがそのまま有効であり続ける)ので、ケース A のような衝突は起きません。
664
- - `--color-primary-*` 等の **セマンティックトークン** を、admin config の解決結果に基づいて実値(`oklch(...)`)までリテラル化した上で `--scope` のセレクタの中に閉じ込めます。プリミティブ(`--color-blue-*` 等)ではなくセマンティックトークンを上書き対象にしているのがポイントで、デフォルトでは `primary` と `info` が同じプリミティブ(例: `blue`)を共有しているため、もしプリミティブ側を上書きしてしまうと `primary` を変えたつもりが `info` まで巻き込んで変わってしまいます。`--scope` はこの巻き込みが起きないよう、セマンティック層で解決してから出力します。
665
- - `--radius-*` / `--shadow-*` は普遍的な共有スケール値(テナント間で衝突しない)なので `var()` 参照のまま出力されます。`config.radius` がバリアントごとに異なれば、その解決結果もちゃんと反映されます。
666
- - フォント import・`@source`・SparkleHead.tsx・globals.css パッチ等、ドキュメント全体に関わる**出力**は行いません。これらはベース側の通常 `generate` が既に担っているため、バリアントごとに重複させる必要はありません。ただし `font-pro` / `font-mono` 等の **validation 自体**は通常の `generate` と同様に適用されます(出力しないだけで、config の妥当性チェックは変わりません)。
667
- - `-o/--output` の指定が必須です(既定の `sparkle-design.css` を誤って上書きしないため)。`--strict` / `--globals-path` とは併用できません(`--scope` はグローバル CSS のパッチを行わないため)。
668
- - 現状 `primary` は 7 色パレットからの選択のみ対応しています。7 色にないカスタムブランドカラーを `--scope` の変数だけで表現したい場合は、`extend.custom-css` の要領で手動でセレクタ配下に `--color-primary-*` を定義してください(`--scope` の出力とマージして使えます)。
669
- - **v2.4.1 以降**: ベース側 `generate` が出す `@theme inline` の各宣言は、プリミティブへの直接参照ではなく同名のセマンティック変数への自己参照(例: `--color-primary-500: var(--color-primary-500)`)になっています。Tailwind v4 は `@theme inline` の宣言右辺をそのまま compiled utility class にインライン展開する仕様のため、これが無いと `.bg-primary-500` 等の compiled utility は最初からプリミティブ変数だけを参照し、`--scope` がセマンティック変数だけを上書きしても utility class には一切反映されません(v2.4.0 はこの自己参照が無く、`--scope` の上書きが compiled utility に効かない状態でした)。v2.4.1 以降を使ってください。
670
-
671
- このパターンは、次の「非推奨: 属性スコープでの Tailwind クラス上書き」が抱えていた脆さも同時に解消します。`--scope` が上書きするのは Sparkle が公開しているトークン契約(`--color-primary-*` 等の CSS 変数)だけであり、ユーティリティクラス名やコンポーネント内部の specificity には一切依存しません。そのため、Sparkle 側のコンポーネント実装(クラス名の付け方や compound attribute selector 等)が変わっても、`--scope` の出力が壊れることはありません。
672
-
673
- ### 非推奨: 単一 CSS + 属性スコープでの Tailwind クラス上書き
674
-
675
- `sparkle-design.css` を 1 本のまま、`[data-theme="xxx"] .bg-primary-500 { ... }` のような属性セレクタで Tailwind ユーティリティクラスを個別に上書きする方式は **推奨しません**。
676
-
677
- - コンポーネント側の実装詳細(クラス名の付け方や CSS の specificity)に依存するため、Sparkle 側の内部実装(例: 状態別カラーリングで compound attribute selector を使っているコンポーネント)が変わると、`!important` を使っても上書きが効かなくなることがあります。上書き対象は Sparkle が公開している契約(config / トークン)ではなく非公開の実装詳細なので、バージョンアップのたびに壊れるリスクを consumer 側が抱え続けることになります。
678
- - 上書きが必要なクラス/コンポーネントが増えるたびに、手動でセレクタを足していく必要があり、考慮漏れに気づきにくいです。
679
-
680
- 上記のリスクを踏まえると、テーマ切り替えは「ケース A: config 分割」または「ケース B: `generate --scope`」+ `extend.custom-css` によるトークンレベルの上書き(cascade 順で確実に勝つ)で完結させる方が、Sparkle のバージョンアップに対しても壊れにくく、見通しの良い実装になります。
681
-
682
- ### トレードオフ
683
-
684
- | 観点 | ケース A: config 分割(推奨・画面ごと固定) | ケース B: `generate --scope`(推奨・単一バンドルでランタイム切替) | 属性スコープ上書き(非推奨) |
685
- | -------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
686
- | 適用シーン | 1 画面につき常に 1 バリアントだけを読み込める | 1 バンドル内で属性の付け外しだけで切り替えたい | (非推奨のため参考情報) |
687
- | ファイルサイズ | バリアントの数だけ CSS が増える(各 CSS は 7 色パレット全体を含む)。1 画面 1 バリアントのみ読み込む運用なら実質的な増分は小さい | ベース CSS 1 本 + バリアントごとの軽量な差分 CSS(セマンティックトークンのみ) | 単一 CSS + 上書き分の差分のみで増分は小さい |
688
- | FOUC リスク | サーバー側 / ビルド時にバリアントを決定できるならほぼ無し | 属性付与のタイミング次第でリスクがある(初期描画後に属性が付くと一瞬デフォルト配色が見える) | クライアント側で属性を付与するタイミング次第でリスクがある |
689
- | 保守性 | Sparkle が公開している config / トークンだけに依存するため、コンポーネント内部実装の変更に影響されにくい | 同左(トークンだけに依存し、ユーティリティクラス名や specificity に依存しない) | コンポーネントの内部実装(クラス名・specificity)に依存するため、Sparkle 側の変更で上書きが効かなくなるリスクがある |
690
-
691
275
  ## 出力
692
276
 
693
277
  - デフォルト出力先: `src/app/sparkle-design.css`
@@ -704,46 +288,6 @@ CLI は **Tailwind エントリ CSS(`@import "tailwindcss"` を含む CSS フ
704
288
 
705
289
  CSS 仕様上 `@import` は他の at-rule より前に書く必要があるため、順序も適切に整えます。`@import "tailwindcss"` が欠けている場合は先頭に自動追記されます。
706
290
 
707
- ## 開発
708
-
709
- ### セットアップ
710
-
711
- ```bash
712
- # submodule(templates/sparkle-variables)を取得
713
- # ※ 未初期化だと generate とそれに依存するテストが失敗する
714
- git submodule update --init --recursive
715
-
716
- # 依存関係をインストール
717
- npm install
718
-
719
- # パッケージをローカルでリンク
720
- npm link
721
- ```
722
-
723
- ### 開発用コマンド
724
-
725
- ```bash
726
- npm test # Node.js test runner
727
- npm run test:watch # テストの watch 実行
728
- npm run lint # ESLint
729
- npm run lint:fix # ESLint 自動修正
730
- npm run format # Prettier
731
- npm run format:check # Prettier チェックのみ
732
- npm run sync:anti-pattern-docs # check ルール一覧を README と隣接リポジトリの JSDoc に同期
733
- ```
734
-
735
- > **`sync:anti-pattern-docs` の注意:** このスクリプトは README だけでなく、ワークスペース内の隣接リポジトリ(`../sparkle-design` / `../sparkle-design-internal`)のファイルも書き換える横断スクリプトです。両リポジトリが隣にある Sparkle ワークスペース内で実行してください。
736
-
737
- ### リリース手順(メンテナ向け)
738
-
739
- publish は GitHub Actions の **Publish to npm** workflow 経由。ローカル `npm publish` は禁止。
740
-
741
- 1. `package.json` の `version` を更新(安定版: `X.Y.Z` / RC: `X.Y.Z-rc.N` / Beta: `X.Y.Z-beta.N`)
742
- 2. `CHANGELOG.md` に該当セクションを追加
743
- 3. PR をマージ後、**Publish to npm** workflow を `channel: auto` で実行
744
-
745
- `channel: auto` は `package.json` の version 形式から dist-tag を自動判定します(`-beta.N` → `beta` / `-rc.N` → `next` / それ以外 → `latest`)。既存 RC / Beta を latest に昇格させる場合は、新しい `X.Y.Z` として改めて publish します(`npm dist-tag add` での手動付け替えも可能ですが、version 管理が明確になる前者を推奨)。
746
-
747
291
  ## ライセンス
748
292
 
749
293
  MIT License - 詳細は [LICENSE](LICENSE) ファイルを参照してください。