sparkle-design-cli 2.4.2 → 2.5.0-beta.2
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/CONTRIBUTING.md +60 -0
- package/README.md +48 -504
- package/bin/sparkle-design.js +86 -31
- package/docs/anti-patterns.md +92 -0
- package/docs/config.md +133 -0
- package/docs/manual-setup.md +117 -0
- package/docs/plugins.md +120 -0
- package/docs/theming.md +122 -0
- package/lib/anti-pattern-rules.js +564 -9
- package/lib/check.js +245 -19
- package/lib/load-plugins.js +6 -0
- package/lib/path-utils.js +120 -0
- package/lib/plugin-api.js +17 -1
- package/lib/rules-report.js +216 -0
- package/lib/spacing-scale.js +228 -0
- package/lib/stop-hook.js +158 -7
- package/lib/token-migration.js +888 -0
- package/package.json +7 -5
- package/templates/sparkle-variables/.github/workflows/validate.yml +21 -0
- package/templates/sparkle-variables/README.md +151 -0
- package/templates/sparkle-variables/colors.json +87 -87
- package/templates/sparkle-variables/gray.json +70 -70
- package/templates/sparkle-variables/radius.csv +1 -1
- package/templates/sparkle-variables/scripts/validate-tokens.mjs +734 -0
- package/templates/sparkle-variables/sparkle-design.template.css +1107 -373
package/bin/sparkle-design.js
CHANGED
|
@@ -6,8 +6,9 @@ import { setupAssistant } from '../lib/setup.js';
|
|
|
6
6
|
import { runStopHook } from '../lib/stop-hook.js';
|
|
7
7
|
import { PLUGIN_SPEC_MARKDOWN } from '../lib/plugin-api.js';
|
|
8
8
|
import { loadAntiPatternPlugins } from '../lib/load-plugins.js';
|
|
9
|
+
import { runRules } from '../lib/rules-report.js';
|
|
9
10
|
|
|
10
|
-
const SUBCOMMANDS = new Set(['generate', 'check', 'setup', 'stop-hook', 'plugin-spec']);
|
|
11
|
+
const SUBCOMMANDS = new Set(['generate', 'check', 'rules', 'setup', 'stop-hook', 'plugin-spec']);
|
|
11
12
|
|
|
12
13
|
function requireOptionValue(args, index, flags) {
|
|
13
14
|
const value = args[index + 1];
|
|
@@ -134,6 +135,41 @@ function parseSetupOptions(args) {
|
|
|
134
135
|
return options;
|
|
135
136
|
}
|
|
136
137
|
|
|
138
|
+
/**
|
|
139
|
+
* `rules` のオプション解析。
|
|
140
|
+
*
|
|
141
|
+
* 値の検証を省くと、`--format jsno` やタイプミスが**黙ってテキスト出力に
|
|
142
|
+
* フォールバックして exit 0** になる。JSON を期待している CI や AI は、
|
|
143
|
+
* エラーではなく解析できない stdout を受け取ることになる。他のサブコマンドと
|
|
144
|
+
* 同じく、未知のフラグと不正な値はその場で失敗させる。
|
|
145
|
+
* en: Without validation a typo like `--format jsno` would silently emit text
|
|
146
|
+
* and exit 0, handing non-JSON stdout to a caller that asked for JSON.
|
|
147
|
+
*/
|
|
148
|
+
function parseRulesOptions(args) {
|
|
149
|
+
const options = { format: 'text', help: false };
|
|
150
|
+
const FORMATS = new Set(['text', 'json']);
|
|
151
|
+
|
|
152
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
153
|
+
const arg = args[i];
|
|
154
|
+
if (arg === '-h' || arg === '--help') {
|
|
155
|
+
options.help = true;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (arg === '--format') {
|
|
159
|
+
const value = requireOptionValue(args, i, '--format');
|
|
160
|
+
if (!FORMATS.has(value)) {
|
|
161
|
+
throw new Error(`Unsupported format for --format: ${value} (text | json)`);
|
|
162
|
+
}
|
|
163
|
+
options.format = value;
|
|
164
|
+
i += 1;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
throw new Error(`Unknown option for rules: ${arg}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return options;
|
|
171
|
+
}
|
|
172
|
+
|
|
137
173
|
function showHelp() {
|
|
138
174
|
console.log(`
|
|
139
175
|
Sparkle Design CLI
|
|
@@ -144,8 +180,11 @@ Sparkle Design CLI
|
|
|
144
180
|
Commands:
|
|
145
181
|
generate sparkle.config.json から CSS を生成
|
|
146
182
|
check Sparkle Design のアンチパターンを検査
|
|
183
|
+
rules 現在有効なアンチパターンルールを一覧表示(プラグイン由来のものも含む)
|
|
147
184
|
setup Sparkle Design プロジェクトをセットアップ(パッケージ導入 + 初期ファイル + AI ガード + generate)
|
|
148
|
-
stop-hook AI assistant の Stop hook 用 internal subcommand(
|
|
185
|
+
stop-hook AI assistant の Stop hook 用 internal subcommand(severity=error の findings か、
|
|
186
|
+
実行に失敗して未検査のルールがあるときに exit 2 で 1 度だけ停止をブロック / 再発火は自動回避。
|
|
187
|
+
warning / info はブロックせず件数の要約を stderr に出す)
|
|
149
188
|
plugin-spec アンチパターン拡張プラグインの契約仕様を出力(--list で導入済みプラグインを表示)
|
|
150
189
|
|
|
151
190
|
Generate:
|
|
@@ -154,7 +193,7 @@ Generate:
|
|
|
154
193
|
sparkle-design-cli generate --output ./styles/design.css
|
|
155
194
|
sparkle-design-cli generate -c ./config/custom.json -o ./dist/styles.css
|
|
156
195
|
|
|
157
|
-
# 単一バンドル内でランタイムにテーマ切替したい場合(--scope。詳細は
|
|
196
|
+
# 単一バンドル内でランタイムにテーマ切替したい場合(--scope。詳細は docs/theming.md 参照)
|
|
158
197
|
sparkle-design-cli generate -c ./config/sparkle.employee.json -o ./src/styles/sparkle-employee.css
|
|
159
198
|
sparkle-design-cli generate -c ./config/sparkle.admin.json --scope '[data-tenant-theme="admin"]' -o ./src/styles/sparkle-admin-scope.css
|
|
160
199
|
|
|
@@ -164,6 +203,10 @@ Check:
|
|
|
164
203
|
sparkle-design-cli check src --format json
|
|
165
204
|
sparkle-design-cli check src/components src/features
|
|
166
205
|
|
|
206
|
+
Rules:
|
|
207
|
+
sparkle-design-cli rules # 有効なルールを severity 別に一覧表示
|
|
208
|
+
sparkle-design-cli rules --format json # CI / AI 向け
|
|
209
|
+
|
|
167
210
|
Plugin spec:
|
|
168
211
|
sparkle-design-cli plugin-spec # アンチパターン拡張プラグインの契約仕様を出力
|
|
169
212
|
sparkle-design-cli plugin-spec --list # 現在のプロジェクトで発見されたプラグインを表示
|
|
@@ -193,8 +236,7 @@ Generate options:
|
|
|
193
236
|
だけを実値までリテラル化して指定セレクタの中にラップ出力する
|
|
194
237
|
(例: '[data-tenant-theme="admin"]')。-o/--output の指定が必須。
|
|
195
238
|
--strict / --globals-path とは併用不可(グローバル CSS を
|
|
196
|
-
パッチしないため)。詳細は
|
|
197
|
-
切替する場合」を参照
|
|
239
|
+
パッチしないため)。詳細は docs/theming.md の「ケース B」を参照
|
|
198
240
|
|
|
199
241
|
sparkle.config.json の設定フィールド:
|
|
200
242
|
|
|
@@ -202,7 +244,7 @@ sparkle.config.json の設定フィールド:
|
|
|
202
244
|
primary プライマリカラー (必須。blue, red, orange, yellow, purple, green, pink の
|
|
203
245
|
いずれか。未指定、またはそれ以外の値は generate 実行時にエラーになります。
|
|
204
246
|
7色にないブランドカラーを使いたい場合は extend.custom-css で
|
|
205
|
-
--color-primary-* / --color-gray-* を再定義してください。詳細は
|
|
247
|
+
--color-primary-* / --color-gray-* を再定義してください。詳細は docs/config.md を参照)
|
|
206
248
|
font-pro プロポーショナルフォント (Google Fonts の名前)
|
|
207
249
|
font-mono モノスペースフォント (Google Fonts の名前)
|
|
208
250
|
radius 角丸設定 (必須。none, xs, sm, md, lg, xl, 2xl, 3xl のいずれか。
|
|
@@ -229,7 +271,8 @@ sparkle.config.json の設定フィールド:
|
|
|
229
271
|
|
|
230
272
|
Check options:
|
|
231
273
|
-h, --help このヘルプメッセージを表示
|
|
232
|
-
--strict
|
|
274
|
+
--strict severity=error の違反があれば exit code 1 で終了
|
|
275
|
+
(warning / info は報告のみで exit code に影響しない)
|
|
233
276
|
--format <text|json> 出力形式 (default: text)
|
|
234
277
|
|
|
235
278
|
Setup options:
|
|
@@ -252,23 +295,12 @@ Setup の動作:
|
|
|
252
295
|
4. AI アシスタントのガードブロックを指定ファイルに挿入(存在しなければ新規作成)
|
|
253
296
|
5. sparkle-design-cli generate を実行し、sparkle-design.css と SparkleHead.tsx を生成
|
|
254
297
|
|
|
255
|
-
|
|
256
|
-
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
- Tailwind デフォルト typography(text-sm, font-medium 等)を使っている
|
|
262
|
-
- CardTitle に typography を上書きしている
|
|
263
|
-
- CardControl に Button / IconButton 以外を入れている
|
|
264
|
-
- CardHeader / CardContent の padding を上書きしている
|
|
265
|
-
- asChild と prefixIcon / suffixIcon / isLoading を併用している
|
|
266
|
-
- disabled を isDisabled の代わりに使っている
|
|
267
|
-
- Button の prefixIcon に JSX を渡している
|
|
268
|
-
- Icon の children にテキストを渡している
|
|
269
|
-
- <Card> を <button> / <a> / role="button" でラップしている(ClickableCard を使う)
|
|
270
|
-
- エントリ CSS にフォント @import が残っている(SparkleHead への移行が必要)
|
|
271
|
-
- CSP ヘッダーで Google Fonts がブロックされている可能性
|
|
298
|
+
検出するルールの一覧:
|
|
299
|
+
sparkle-design-cli rules
|
|
300
|
+
|
|
301
|
+
ここに列挙しないのは、有効なルールがプロジェクトごとに違うため。プラグインは
|
|
302
|
+
コンシューマの package.json から自動発見されるので、固定の一覧はプラグインを
|
|
303
|
+
入れた利用者にとって最初から不正確になる。
|
|
272
304
|
|
|
273
305
|
JSON 出力では manualReviewReminders も返すため、AI から実行する場合は --format json を推奨
|
|
274
306
|
|
|
@@ -353,11 +385,14 @@ async function main() {
|
|
|
353
385
|
process.exit(0);
|
|
354
386
|
}
|
|
355
387
|
|
|
356
|
-
|
|
388
|
+
// checkProject の戻り値は「findings があるか」ではなく
|
|
389
|
+
// 「exit code を 1 にすべきか」(error severity か、実行に失敗したルールがあるか)。
|
|
390
|
+
// en: The return value means "should this fail", not "were there findings".
|
|
391
|
+
const hasBlockingIssues = await checkProject(options.targets, {
|
|
357
392
|
strict: options.strict,
|
|
358
393
|
format: options.format,
|
|
359
394
|
});
|
|
360
|
-
if (
|
|
395
|
+
if (hasBlockingIssues && options.strict) {
|
|
361
396
|
process.exit(1);
|
|
362
397
|
}
|
|
363
398
|
return;
|
|
@@ -377,14 +412,34 @@ async function main() {
|
|
|
377
412
|
|
|
378
413
|
if (command === 'stop-hook') {
|
|
379
414
|
// setup で各 agent の hook 設定ファイルから呼ばれる internal subcommand。
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
|
|
384
|
-
|
|
415
|
+
// 以降の引数はすべて lint 対象 path。setup が書き出す hook は 1 つだが、
|
|
416
|
+
// 利用側が `stop-hook apps/web/app apps/web/components` のように手で複数
|
|
417
|
+
// 並べている実例があり、以前は args[1] しか読まず 2 つ目以降が黙って
|
|
418
|
+
// 未検査になっていた(issue #85)。option flag は未対応なので、`-` 始まりは
|
|
419
|
+
// path として渡さず警告する。
|
|
420
|
+
// en: Forward every positional arg. Previously only the first was used, so
|
|
421
|
+
// extra paths in a hand-written hook command were silently never checked.
|
|
422
|
+
const positional = args.slice(1).filter((arg) => !arg.startsWith('-'));
|
|
423
|
+
const flags = args.slice(1).filter((arg) => arg.startsWith('-'));
|
|
424
|
+
if (flags.length > 0) {
|
|
425
|
+
console.warn(
|
|
426
|
+
`⚠️ stop-hook はオプションを受け付けません。無視します: ${flags.join(' ')} / stop-hook takes target paths only.`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
const exitCode = await runStopHook(positional);
|
|
385
430
|
process.exit(exitCode);
|
|
386
431
|
}
|
|
387
432
|
|
|
433
|
+
if (command === 'rules') {
|
|
434
|
+
const options = parseRulesOptions(args.slice(1));
|
|
435
|
+
if (options.help) {
|
|
436
|
+
showHelp();
|
|
437
|
+
process.exit(0);
|
|
438
|
+
}
|
|
439
|
+
await runRules(options);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
|
|
388
443
|
if (command === 'plugin-spec') {
|
|
389
444
|
await runPluginSpec(args.slice(1));
|
|
390
445
|
return;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# アンチパターン検査の詳細
|
|
2
|
+
|
|
3
|
+
`sparkle-design-cli check` が検出する内容の背景と、個別ルールの扱いをまとめています。
|
|
4
|
+
|
|
5
|
+
**現在有効なルールの一覧は `npx sparkle-design-cli rules` で確認してください。** ここには一覧を置きません。プラグインで増減するため、書いた時点で古くなるからです。
|
|
6
|
+
|
|
7
|
+
## 抑制する
|
|
8
|
+
|
|
9
|
+
特定の箇所だけ検査から外すには、ESLint と同じ形のコメントを使います。
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
{/* sparkle-disable-next-line legacy-color-token */}
|
|
13
|
+
<div className="bg-primary-600" />
|
|
14
|
+
|
|
15
|
+
<div className="bg-primary-600" /> {/* sparkle-disable-line legacy-color-token */}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
ルール ID はカンマ区切りで複数指定できます。ID は `rules` コマンドの出力と `check` の指摘に出ているものです。
|
|
19
|
+
|
|
20
|
+
## 新セマンティックトークンへの移行(beta 期間中)
|
|
21
|
+
|
|
22
|
+
移行ルールは検出するだけでなく、**マッチ 1 件ごとに移行先を提示**します。
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
src/Button.tsx:12 [warning] [legacy-color-token] 旧セマンティックカラーのユーティリティクラスを使っています
|
|
26
|
+
Recommendation: [自動変換可] bg-primary-600 → bg-surface-primary-high-enabled
|
|
27
|
+
|
|
28
|
+
src/Card.tsx:8 [warning] [legacy-color-token] 旧セマンティックカラーのユーティリティクラスを使っています
|
|
29
|
+
Recommendation: [要判断] bg-primary-200 の移行先候補: surface-primary-high-disabled / surface-primary-middle-active。…
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
新体系は「用途 × 意味 × 強度 × **状態**」で、状態がトークン名に内包されます。そのため**同じ旧クラスでも文脈によって移行先が変わり**、機械的に決まらないものは `[要判断]` として候補の列挙に留めます(推測で置換しません)。
|
|
33
|
+
|
|
34
|
+
ユーティリティ接頭辞がそのまま用途に対応します。
|
|
35
|
+
|
|
36
|
+
| 接頭辞 | 用途 | 例 |
|
|
37
|
+
| -------------------------------------------- | -------------------- | ---------------------------------------------------- |
|
|
38
|
+
| `bg-` | surface(背景) | `bg-primary-600` → `bg-surface-primary-high-enabled` |
|
|
39
|
+
| `text-` | text(文字) | `text-primary-600` → `text-text-primary-enabled` |
|
|
40
|
+
| `border-` / `divide-` / `outline-` / `ring-` | border(枠線) | `border-primary-300` → `border-border-primary-high` |
|
|
41
|
+
| `fill-` / `stroke-` | object(アイコン等) | `fill-primary-600` → `fill-object-primary-enabled` |
|
|
42
|
+
|
|
43
|
+
`secondary` は `neutral` に統合されました。旧トークンは beta 期間中はテンプレートに併存するので、警告が出ていても即座には壊れません。beta 終了時に旧トークンを削除するタイミングで `error` に昇格します。
|
|
44
|
+
|
|
45
|
+
移行ルールは `.tsx` などのソースに加えて **`.css` も検査します**(`var(--color-ring-normal)` のような CSS 変数の直接参照は `.css` にこそ書かれ、しかも beta 終了で消えたときにビルドエラーにならず見た目だけ壊れるため)。
|
|
46
|
+
|
|
47
|
+
他のルール(`dialog-form` などの構造ルール)は従来どおりソースファイルのみが対象で、CSS 検査の追加による影響はありません。
|
|
48
|
+
|
|
49
|
+
**報告しないもの**(いずれも「移行が必要な使用箇所」ではないため):
|
|
50
|
+
|
|
51
|
+
- **そのファイル自身が宣言している変数への参照**。CLI が生成した CSS は旧トークンを「定義している側」なので、これで丸ごと除外されます。ファイル名ではなく内容で判定しているため、`generate --scope`(`-o` が必須で出力名が任意)の生成物にも効きます。自前で `:root` に定義を持つプロジェクトの theme ファイルも同じ理由で対象外になります(自分で定義している以上、beta 終了で壊れません)
|
|
52
|
+
- **行頭から始まるブロックコメントの中身**。「旧 `--color-ring-normal` の移行先」のような説明文を拾わないためです。行末コメントは対象外にしていません(任意位置の `/*` をコメント開始とみなすと、`matcher: ['/dashboard/*']` のような文字列リテラルを起点に実コードを黙って検査対象から外してしまうため)。逆に、複数行文字列(テンプレートリテラル)の中に行頭から `/*` を書いた場合は、閉じるまでの範囲が検査されません
|
|
53
|
+
|
|
54
|
+
> 移行ルールはクラス名らしき文字列に対する正規表現マッチのため、`url(/img/fill-base-100.svg)` のようにクラス名でない文字列を拾うことがあります。`warning` 止まりで exit code には影響しませんが、抑制したい箇所には `sparkle-disable-line legacy-color-token` のコメントを使えます。
|
|
55
|
+
>
|
|
56
|
+
> 旧→新の対応表は `lib/token-migration.js` に一元化してあり、`test/token-migration.test.js` が**実際に生成した CSS を読んで**「表に書いた移行先が実在し、旧トークンと同じ実値に解決される」ことを検証しています。表とトークン定義が食い違ったらテストが落ちるので、案内が嘘になりません。検出パターンがマッチする文字列は必ず対応表で解決できることも全数検証しています(片方だけ更新すると「検出したのに黙って捨てる」穴になるため)。
|
|
57
|
+
|
|
58
|
+
## 余白のスケール(`use-figma-spacing-scale`)
|
|
59
|
+
|
|
60
|
+
Figma の余白は 17 段(0 / 2 / 4 / 6 / 8 / 12 / 16 / 20 / 24 / 32 / 40 / 48 / 56 / 72 / 88 / 104 / 120 px)です。Tailwind の 1 ステップは 4px なので、`p-4` = 16px、`p-6` = 24px が対応します。このスケールから外れた値を `info` で報告し、前後の候補を提示します。
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
src/Popover.stories.tsx:194 [info] [use-figma-spacing-scale] Figma の Spacing: Primitives に無い余白ステップを使っています
|
|
64
|
+
Recommendation: [要判断] gap-y-20 は 80px で、Figma の Spacing: Primitives に無い値です。近いステップは gap-y-18(72px) / gap-y-22(88px)。…
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Tailwind の数字と Figma の数字は一致しません。** Figma の `padding/16` は 16px、Tailwind の `p-16` は 64px で 4 倍違います。Figma を見ながら同じ数字を書くと 4 倍の余白になります。
|
|
68
|
+
|
|
69
|
+
| Figma | px | Tailwind |
|
|
70
|
+
| ------------- | ----- | -------- |
|
|
71
|
+
| `padding/8` | 8px | `p-2` |
|
|
72
|
+
| `padding/16` | 16px | `p-4` |
|
|
73
|
+
| `padding/24` | 24px | `p-6` |
|
|
74
|
+
| `padding/120` | 120px | `p-30` |
|
|
75
|
+
|
|
76
|
+
このスケールは **CSS 変数として出力していません**。`--spacing-16` のような名前付きキーを定義すると Tailwind の `p-16` がその値に差し替わり、既存コードの余白がビルドも警告も通ったまま変わってしまうためです。だから「変数を使う」ではなく「使ってよい値を守る」という形の制約になっています(`sparkle-variables` 側でも `--spacing-*` を定義していないことを検査しています)。
|
|
77
|
+
|
|
78
|
+
対象は `p` / `m` / `gap` / `space` 系の数値ユーティリティで、`.tsx` などのソースに加えて `.css` の `@apply` も見ます。Figma のコレクション名は `padding/*` ですが、`gap-7` のようにスケールから外れた値は padding でなくても同じだけレイアウトを崩すため、margin / gap / space も対象にしています。**これは Figma がそう定義しているのではなく運用上の判断**なので、`severity` は `info` です。
|
|
79
|
+
|
|
80
|
+
提示する代替は、variant 連鎖・負のマージンの `-`・important の `!` をすべて保ったまま数字だけを差し替えます(`hover:p-7!` → `hover:p-6!` / `hover:p-8!`)。`[&>*]:` のような角括弧だけの arbitrary variant も保持します。案内どおりに書き換えた結果 important が消えていた、という事故を避けるためです。
|
|
81
|
+
|
|
82
|
+
`p-[13px]` のような arbitrary **value** は対象外です(`calc()` など正当な用途と機械的に区別できないため)。
|
|
83
|
+
|
|
84
|
+
Tailwind では `w-*` / `h-*` / `inset-*` / `translate-*` / `scroll-p-*` なども同じ spacing スケールから値を引きますが、**このルールの対象外**です。Figma のコレクションが定義しているのは余白(`padding/*`)で、サイズや位置まで同じ制約に含めると別の判断が混ざるためです。
|
|
85
|
+
|
|
86
|
+
クラス名の直前に `/` が来る文字列(`https://example.com/v1/p-7` や `import x from './gap-7'`)は誤検出を避けるため対象外にしています。
|
|
87
|
+
|
|
88
|
+
デザイン上どうしてもスケール外の値が必要な場合は Figma 側にステップを追加するのが本筋ですが、個別に外すなら `sparkle-disable-next-line use-figma-spacing-scale` を使えます。
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
[← README に戻る](../README.md)(docs/anti-patterns.md)
|
package/docs/config.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# 設定ファイルの拡張(`extend`)
|
|
2
|
+
|
|
3
|
+
Figma プラグインが出力する基本4項目に加えて、プロジェクト固有の拡張を `extend` セクションにまとめます。`extend` はオブジェクト直書き(推奨)またはファイルパスを指定できます。
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"primary": "blue",
|
|
8
|
+
"font-pro": "Montserrat",
|
|
9
|
+
"font-mono": "Roboto Mono",
|
|
10
|
+
"radius": "md",
|
|
11
|
+
"extend": {
|
|
12
|
+
"fonts": {
|
|
13
|
+
"pro": [
|
|
14
|
+
{ "family": "Montserrat", "weights": [500, 600, 700] },
|
|
15
|
+
{ "family": "Noto Sans JP", "weights": [400, 500, 600, 700] }
|
|
16
|
+
],
|
|
17
|
+
"mono": [{ "family": "Roboto Mono", "weights": [400, 700] }]
|
|
18
|
+
},
|
|
19
|
+
"source-packages": ["@goodpatch/sparkle-design-internal"],
|
|
20
|
+
"custom-css": "./src/app/custom-tokens.css"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
ファイル参照も可能です:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"primary": "blue",
|
|
30
|
+
"font-pro": "Montserrat",
|
|
31
|
+
"font-mono": "Roboto Mono",
|
|
32
|
+
"radius": "md",
|
|
33
|
+
"extend": "./sparkle.extend.json"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## extend.globals-path
|
|
38
|
+
|
|
39
|
+
Tailwind のエントリポイント CSS(`@import "tailwindcss";` を書いているファイル)のパスです。未指定なら自動検出します。CLI の `--globals-path` でも同じ指定ができ、そちらが優先されます。
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"primary": "blue",
|
|
44
|
+
"extend": {
|
|
45
|
+
"globals-path": "src/styles/app.css"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
指定すると次の 2 つに効きます。
|
|
51
|
+
|
|
52
|
+
| 効き先 | 内容 |
|
|
53
|
+
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
54
|
+
| `@source` と `sparkle-design.css` の import 先 | そのファイルにパッチする(**フォントの `@import` は入りません**。フォント読み込みは `SparkleHead.tsx` 側に移っています) |
|
|
55
|
+
| `generate` の出力先 | `-o/--output` 未指定なら**そのファイルと同じディレクトリ**に `sparkle-design.css` と `SparkleHead.tsx` を出す |
|
|
56
|
+
|
|
57
|
+
後者が重要です。既定の `src/app/` に固定されるのは globals-path を明示していない場合だけなので、`src/index.css` や `src/styles/app.css` 構成のプロジェクトで意図しない `src/app/` ディレクトリが増えることはありません。
|
|
58
|
+
|
|
59
|
+
> [!WARNING]
|
|
60
|
+
> **指定したパスが存在しない場合は `--strict` の有無に関わらず常に exit 1** です。typo に気付かないまま誤ったディレクトリを掘るより、その場で止めるほうが安全なためです。相対パスのみで、`..` を含む traversal や絶対パスは弾かれます。
|
|
61
|
+
|
|
62
|
+
## extend.fonts
|
|
63
|
+
|
|
64
|
+
フォントごとにウェイトを個別指定。`extend.fonts` がない場合は `font-pro` / `font-mono` + デフォルトウェイト `[400, 700]` が使われます。`extend.fonts` が存在しても `fonts.pro` / `fonts.mono` のどちらかが未指定なら、そのスロットのみ `font-pro` / `font-mono` にフォールバックします。同じフォントファミリーが `pro` と `mono` で重複する場合、ウェイトはマージされ import は 1 行に統合されます。
|
|
65
|
+
|
|
66
|
+
## extend.source-packages
|
|
67
|
+
|
|
68
|
+
既知のデザインシステムパッケージ(`sparkle-design` など)は `package.json` から**自動検出**されるので、通常は指定不要です。クライアント固有のパッケージなど自動検出の対象外を追加したいときだけ指定してください。指定分は自動検出分とマージされ、Tailwind エントリ CSS(自動検出)に `@source` ディレクティブとして挿入されます。`@source` をまったく出さないのは、**自動検出がゼロで、かつ `source-packages` キー自体を書いていない**ときだけです(キーがあれば空配列でも既定の `sparkle-design` 1 行が出ます)。
|
|
69
|
+
|
|
70
|
+
## extend.custom-css
|
|
71
|
+
|
|
72
|
+
プロジェクト固有のカスタムトークンの CSS ファイルパス。Tailwind エントリ CSS に `@import` が自動挿入されます。`sparkle-design.css` に直接追加すると `generate` 実行時に上書きされるため、必ず別ファイルに分離してください。
|
|
73
|
+
|
|
74
|
+
この `@import` は `sparkle-design.css` の直後に挿入されるため、`custom-css` 側で書いた `:root { --color-xxx: ... }` は CSS の cascade(後勝ち)でそのまま `sparkle-design.css` のトークンを上書きします。属性セレクタや `!important` を使う必要はありません。
|
|
75
|
+
|
|
76
|
+
### カスタムブランドカラーを primary にしたい場合
|
|
77
|
+
|
|
78
|
+
`primary` は 7 色のいずれかしか受け付けません([README の「設定オプション」](../README.md#設定オプション) を参照)。7 色にないブランドカラーを使いたい場合は、`primary` は 7 色から見た目が近いものを仮に選んだ上で、`extend.custom-css` で `--color-primary-*` と `--color-gray-*` をブランドカラー基準の値で丸ごと再定義してください。`primary` の選択自体は `custom-css` 側の定義で完全に上書きされるため実質的な意味を持たなくなりますが、フィールドとしては有効な値を入れておく必要があります。
|
|
79
|
+
|
|
80
|
+
```css
|
|
81
|
+
/* custom-tokens.css */
|
|
82
|
+
:root {
|
|
83
|
+
/* ブランドカラー基準の primary パレット(50〜900) */
|
|
84
|
+
--color-primary-50: oklch(97% 0.02 250);
|
|
85
|
+
/* ... */
|
|
86
|
+
--color-primary-500: oklch(55% 0.18 250);
|
|
87
|
+
/* ... */
|
|
88
|
+
--color-primary-900: oklch(20% 0.08 250);
|
|
89
|
+
|
|
90
|
+
/* primary に合わせた gray パレット(50〜900)も必ず一緒に定義する。
|
|
91
|
+
gray だけ 7 色デフォルトのままだと、primary だけ浮いて見えるトーンずれが起きる */
|
|
92
|
+
--color-gray-50: oklch(98% 0.005 250);
|
|
93
|
+
/* ... */
|
|
94
|
+
--color-gray-900: oklch(22% 0.01 250);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`primary` だけを差し替えて `gray` を既定のままにすると、コンポーネントの枠線・背景・テキストに使われる gray 系トークンと primary のトーンが揃わなくなるため、gray も必ずセットで再定義してください。
|
|
99
|
+
|
|
100
|
+
### `character-*` に無いウェイト(SemiBold 等)を使いたい場合
|
|
101
|
+
|
|
102
|
+
`character-*` utility の `font-weight` は Regular(400) / Bold(700) の 2 値のみで、SemiBold(600) や Medium(500) に対応する `character-*` は存在しません(Sparkle Design のプリミティブトークンである `fontWeights.text.regular` / `fontWeights.text.bold` がこの 2 値のみを持つため)。
|
|
103
|
+
|
|
104
|
+
日本語見出しでは 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` が常に優先されるためです。
|
|
105
|
+
|
|
106
|
+
```css
|
|
107
|
+
/* custom-typography.css */
|
|
108
|
+
@layer utilities {
|
|
109
|
+
/* character-3(16px/24px)の SemiBold バリアント。
|
|
110
|
+
font-family / font-size / letter-spacing / line-height は
|
|
111
|
+
character-* と同じプリミティブトークンをそのまま流用し、
|
|
112
|
+
font-weight だけプロジェクト独自の 600 に差し替える */
|
|
113
|
+
.character-3-semibold-pro {
|
|
114
|
+
font-family: var(--font-family-pro);
|
|
115
|
+
font-size: var(--font-size-16);
|
|
116
|
+
font-weight: 600;
|
|
117
|
+
letter-spacing: var(--letter-spacing-wider);
|
|
118
|
+
line-height: var(--line-height-24);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`character-*-semibold-*` は既存の `character-*-regular-*` / `character-*-bold-*` と別名のクラスなので、cascade の競合は起きません。`extend.custom-css` は `sparkle-design.css` の直後に `@import` されるため、上記のように新規クラスを追記するだけで有効になります(上書きではないので `!important` や記述順の工夫も不要です)。
|
|
124
|
+
|
|
125
|
+
使用するフォントファミリーが実際に 600 ウェイトのファイルを読み込んでいるかも確認してください。`extend.fonts.pro` / `extend.fonts.mono` の `weights` に `600` を含めていないと、`font-weight: 600` を指定してもブラウザによる疑似太字(faux bold)にフォールバックし、正しい SemiBold の字形になりません。
|
|
126
|
+
|
|
127
|
+
将来 Sparkle Design 本体が正式に `character-N-semibold-*` token を追加した場合は、cascade 上は consumer 側の `custom-css` 定義が勝ち続けるため、その時点で自前定義は削除して本体の token に乗り換えてください(放置すると Sparkle 側の値が更新されても気づかず古い定義が使われ続けます)。
|
|
128
|
+
|
|
129
|
+
なお `check` の `tailwind-typography` ルールは `font-semibold` / `font-medium` を検出対象に含めていますが、`character-*-semibold-*` のような独自クラスに置き換えた行は検出パターンに一致しないため、`sparkle-disable-line` を付けなくても違反として検出されなくなります。
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
[← README に戻る](../README.md)(docs/config.md)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# 手動セットアップ(Next.js / Vite 以外のプロジェクト向け)
|
|
2
|
+
|
|
3
|
+
`setup` コマンドは **Next.js App Router** と **Vite** を前提に scaffold / entry CSS の配置を自動判定します。Remix / Astro / TanStack Start など他のフレームワークや、独自ディレクトリ構成のプロジェクトでは自動判定が外れるため、以下の手順で手動セットアップすることを推奨します。
|
|
4
|
+
|
|
5
|
+
## 手順
|
|
6
|
+
|
|
7
|
+
**1. パッケージをインストール**
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# 本体
|
|
11
|
+
pnpm add sparkle-design # or npm install / yarn add / bun add
|
|
12
|
+
|
|
13
|
+
# Tailwind v4
|
|
14
|
+
pnpm add -D tailwindcss @tailwindcss/postcss
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**2. `sparkle.config.json` をプロジェクトルートに作成**
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"primary": "blue",
|
|
22
|
+
"font-pro": "Inter",
|
|
23
|
+
"font-mono": "JetBrains Mono",
|
|
24
|
+
"radius": "md"
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
選択肢の詳細は [README の「設定オプション」](../README.md#設定オプション)、`extend` の各項目は [docs/config.md](config.md) を参照してください。
|
|
29
|
+
|
|
30
|
+
**3. `postcss.config.mjs` をプロジェクトルートに作成**
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
export default {
|
|
34
|
+
plugins: {
|
|
35
|
+
'@tailwindcss/postcss': {},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**4. Tailwind エントリ CSS を自前で用意**
|
|
41
|
+
|
|
42
|
+
既存プロジェクトに組み込む場合、`@import "tailwindcss";` を含む CSS ファイル(例: `src/styles/app.css`)があればそれを使います。無ければ作成してアプリケーションのエントリから import します。
|
|
43
|
+
|
|
44
|
+
**5. `generate` を実行**
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx --yes sparkle-design-cli generate
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
これで `sparkle-design.css` と `SparkleHead.tsx` が生成されます。**出力先はエントリ CSS の場所で変わります** — `extend.globals-path`(または `--globals-path`)を指定していればそのディレクトリ、未指定なら既定の `src/app/` です。上の例のように `src/styles/app.css` を使う場合は、この手順の前に `extend.globals-path` を指定してください(指定しないと `src/app/` に出ます)。`-o/--output` で直接指定することもできます。
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"primary": "blue",
|
|
55
|
+
"extend": {
|
|
56
|
+
"globals-path": "src/styles/app.css"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**6. フォントの `<link>` タグを手動で配置**
|
|
62
|
+
|
|
63
|
+
Next.js / Vite 以外ではアプリケーションフレームワークごとに「`<head>` 相当」の配置方法が異なります。自動生成される `SparkleHead.tsx` はそのまま使えるはずですが、使えない場合は中身(`preconnect` + Google Fonts の `<link>` タグ群)を自前のレイアウトにコピーしてください。
|
|
64
|
+
|
|
65
|
+
たとえば Astro なら `src/layouts/BaseLayout.astro` の `<head>` に直接書きます:
|
|
66
|
+
|
|
67
|
+
```html
|
|
68
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
69
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
70
|
+
<link
|
|
71
|
+
rel="stylesheet"
|
|
72
|
+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:FILL,wght@0..1,500&display=block"
|
|
73
|
+
/>
|
|
74
|
+
<!-- sparkle.config.json の font-pro / font-mono に合わせた Google Fonts URL -->
|
|
75
|
+
<link
|
|
76
|
+
rel="stylesheet"
|
|
77
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap"
|
|
78
|
+
/>
|
|
79
|
+
<link
|
|
80
|
+
rel="stylesheet"
|
|
81
|
+
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap"
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**7. アンチパターン検査を package.json に追加(任意)**
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"scripts": {
|
|
90
|
+
"lint:sparkle": "npx --yes sparkle-design-cli check src --strict",
|
|
91
|
+
"lint:sparkle:json": "npx --yes sparkle-design-cli check src --format json"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**8. AI エージェントを使うなら Guard ブロックと hook を手動で配置**
|
|
97
|
+
|
|
98
|
+
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` は条件付き読み込みのため)。
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# ガード追加・hook 設定のみ(パッケージインストールや scaffold はスキップ)
|
|
102
|
+
npx --yes sparkle-design-cli setup --assistant claude --skip-install --skip-scaffold --skip-generate
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
この `--skip-*` 組み合わせは手動セットアップ済みのプロジェクトに対して Guard + hook だけ差し込むのに使えます。
|
|
106
|
+
|
|
107
|
+
## 既知の未対応ケース
|
|
108
|
+
|
|
109
|
+
- **モノレポ(workspaces)**: `generate` は実行した cwd からパッケージマネージャーを遡及検出しないため、サブパッケージで直接実行するのが確実です。
|
|
110
|
+
- **CSS 以外のエントリ(CSS-in-JS / vanilla-extract 等)**: Sparkle Design は Tailwind v4 の `@source` + CSS variable 前提で作られているため、ビルドパイプラインの外で CSS を扱うソリューションは対象外です。
|
|
111
|
+
- **Tailwind v3**: `@source` ディレクティブ依存のため v4 必須です。
|
|
112
|
+
|
|
113
|
+
Next.js / Vite 以外で導入したい方で困ったときは Issue で教えていただけると助かります。
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
[← README に戻る](../README.md)(docs/manual-setup.md)
|