ksk-design-system 1.59.0 → 1.60.0
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/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/DESIGN.md +9 -0
- package/MIGRATION.md +21 -0
- package/README.md +15 -0
- package/bin/init.js +17 -38
- package/bin/lint.js +24 -1
- package/bin/product-theme-override.js +73 -8
- package/contracts/components.json +1 -1
- package/contracts/product-theme-overrides.json +50 -4
- package/contracts/token-hex-cache.json +1 -1
- package/package.json +1 -2
- package/templates/AGENTS.md +1 -1
- package/templates/CLAUDE.md +1 -1
package/AGENTS.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
末尾が `codex-pr-review-guidelines` のマーカー)で囲み同期対象から除外する
|
|
18
18
|
- 片方だけ編集したら **`node scripts/check-agents-docs-sync.mjs`**(`npm run check` に
|
|
19
19
|
組み込み済み)を実行し、乖離が無いことを確認する
|
|
20
|
-
- `templates/CLAUDE.md` / `templates/AGENTS.md
|
|
20
|
+
- `templates/CLAUDE.md` / `templates/AGENTS.md`(`npx ksk-ds init` で配布するテンプレート)も
|
|
21
21
|
同じ仕組みで同期検査の対象
|
|
22
22
|
|
|
23
23
|
## 実装前セルフチェック(AI必読・最優先)
|
package/CLAUDE.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
末尾が `codex-pr-review-guidelines` のマーカー)で囲み同期対象から除外する
|
|
18
18
|
- 片方だけ編集したら **`node scripts/check-agents-docs-sync.mjs`**(`npm run check` に
|
|
19
19
|
組み込み済み)を実行し、乖離が無いことを確認する
|
|
20
|
-
- `templates/CLAUDE.md` / `templates/AGENTS.md
|
|
20
|
+
- `templates/CLAUDE.md` / `templates/AGENTS.md`(`npx ksk-ds init` で配布するテンプレート)も
|
|
21
21
|
同じ仕組みで同期検査の対象
|
|
22
22
|
|
|
23
23
|
## 実装前セルフチェック(AI必読・最優先)
|
package/DESIGN.md
CHANGED
|
@@ -311,6 +311,15 @@ Portal に載る要素(Dialog / Sheet / Popover / Toast 等)は DOM 上の
|
|
|
311
311
|
`AdminShell` の `<main>` だけが `--Product-Page-Padding-Y` を持つ。
|
|
312
312
|
- 色は従来どおり semantic トークン(`--Surface-*` / `--Text-*` / `--Brand-*`)と Brand ランプの
|
|
313
313
|
上書きで調整する。カードの面は `--card-surface`、角丸は `--Radius-Surface` が既存の公開シーム。
|
|
314
|
+
- **primitive を直接上書きしてよいのは Brand(`--Primitive-Brand-*`)と Neutral
|
|
315
|
+
(`--Primitive-Gray-50〜900` / `--Primitive-White` / `--Primitive-Black`)の2つのアイデンティティ
|
|
316
|
+
パレットだけ**(issue #377)。warm gray のように DS 既定の cool gray と世界観が違うプロダクトは
|
|
317
|
+
Neutral ランプごと差し替える。status 系(Red / Green / Blue / Yellow / Orange)の色調整は
|
|
318
|
+
semantic 層(`--Surface-Caution` / `--Text-Success` / `--Caution-Base` 等)で行う — 上書き1箇所で
|
|
319
|
+
済み、DS が primitive の割り当てを変えても壊れない。
|
|
320
|
+
**Neutral を差し替えたらコントラストは消費側の責任**。Gray は `--Text-High-Emphasis`(本文色)や
|
|
321
|
+
`--Surface-Secondary` を駆動するので、本文×背景が WCAG AA(4.5:1)を満たすか light / dark 両方で
|
|
322
|
+
実測すること。DS の `scripts/check-contrast.mjs` は DS 既定パレットしか見ていない。
|
|
314
323
|
- **`--Product-Type-Scale`**(issue #371/#372 の stract-ui からの逆輸入)は `typo-*` の font-size に
|
|
315
324
|
`calc(<px> * var(--Product-Type-Scale, 1))` として一律で掛かる乗数。line-height は unitless、
|
|
316
325
|
letter-spacing は em のため font-size の変化に自動で連動し、別途スケールしない。
|
package/MIGRATION.md
CHANGED
|
@@ -53,6 +53,27 @@ npx ksk-ds check-migration ./src
|
|
|
53
53
|
|
|
54
54
|
## v1 系内の minor 変更(参考)
|
|
55
55
|
|
|
56
|
+
### 次のリリース — `postinstall` による AI ルールファイル自動設置を廃止(要確認)
|
|
57
|
+
|
|
58
|
+
これまで `npm install ksk-design-system` の `postinstall` フックが、消費側の
|
|
59
|
+
プロジェクトルートに `CLAUDE.md` / `AGENTS.md`(`node_modules` 内の DS ルールを指す
|
|
60
|
+
薄いポインタ)を自動設置していた。**この自動設置を廃止し、明示コマンドに一本化した。**
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npx ksk-ds init # 設置(既存ファイルはスキップ)
|
|
64
|
+
npx ksk-ds init --force # 既存ファイルを最新テンプレートで上書き
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**既に設置済みの消費リポは対応不要。** 元々 `postinstall` は既存ファイルを上書きしない
|
|
68
|
+
仕様だったため、設置済みのプロジェクトでは実質 no-op だった。影響を受けるのは
|
|
69
|
+
**新規に DS を導入するプロジェクトの初回だけ**で、`npm install` の後に上記を 1 回実行する。
|
|
70
|
+
|
|
71
|
+
廃止の理由は、install 時にプロジェクトルートへ AI エージェント向け指示ファイルを書き込む
|
|
72
|
+
挙動が、サプライチェーン検査で「同意なき AI エージェント制御面の設置」として Critical 判定
|
|
73
|
+
されるため。LPM Firewall が 1.49.2 / 1.51.1 をこの理由でブロック判定しており、
|
|
74
|
+
`postinstall` を持つ限りバージョンを上げても判定が引き継がれる。あわせて
|
|
75
|
+
`INIT_CWD` 参照も削除し、書き込み先はコマンドを実行したディレクトリに固定した。
|
|
76
|
+
|
|
56
77
|
### 次のリリース — PillToggle の onChange/onValueChange 統一(破壊変更なし)
|
|
57
78
|
|
|
58
79
|
`PillToggle` のみ他コンポーネント(Switch 等の native 系を除く)と異なり `onValueChange`
|
package/README.md
CHANGED
|
@@ -48,6 +48,21 @@ Tailwind 4.1 で導入された `@source inline()` を使うため)
|
|
|
48
48
|
npm install ksk-design-system
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
AI コーディング(Claude Code / Codex)を使うプロジェクトでは、続けて 1 回だけ実行します。
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx ksk-ds init
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
プロジェクトルートに `CLAUDE.md` / `AGENTS.md`(`node_modules` 内の DS ルールを指す薄いポインタ)を
|
|
58
|
+
設置します。AI エージェントは `node_modules` 配下を自動では読まないため、このファイルが無いと
|
|
59
|
+
DS のルールが適用されません。既存ファイルはスキップされ、更新は `--force` で上書きします。
|
|
60
|
+
|
|
61
|
+
> **v1.60.0 で `postinstall` による自動設置を廃止しました。** install 時にプロジェクトルートへ
|
|
62
|
+
> AI 指示ファイルを書き込む挙動は、サプライチェーン検査で「同意なき AI エージェント制御面の設置」
|
|
63
|
+
> として Critical 判定されるためです(LPM Firewall が 1.49.2 / 1.51.1 をこの理由でブロック判定)。
|
|
64
|
+
> v1.59.0 以前から更新する場合、既に設置済みのファイルはそのまま使えます。
|
|
65
|
+
|
|
51
66
|
```css
|
|
52
67
|
/* globals.css / app.css(CSS の場所に応じて ../../ の数を調整) */
|
|
53
68
|
@import "tailwindcss";
|
package/bin/init.js
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
// プロジェクトルートに AGENTS.md / CLAUDE.md(node_modules 内の DS ルールを
|
|
7
7
|
// 参照する薄いポインタ)を置く必要がある。
|
|
8
8
|
//
|
|
9
|
+
// 設置は **明示実行のみ**(`npx ksk-ds init`)。install 時に自動で置く
|
|
10
|
+
// postinstall フックは v1.60.0 で廃止した。install-time にプロジェクトルートへ
|
|
11
|
+
// AI 指示ファイルを書き込む挙動は、サプライチェーン検査で
|
|
12
|
+
// 「同意なき AI エージェント制御面の設置」として Critical 判定されるため
|
|
13
|
+
// (LPM Firewall が 1.49.2 / 1.51.1 をこの理由でブロック判定)。
|
|
14
|
+
//
|
|
9
15
|
// Usage:
|
|
10
16
|
// npx ksk-design-system init # AGENTS.md + CLAUDE.md を設置
|
|
11
17
|
// npx ksk-design-system init --force # 既存ファイルを上書き
|
|
@@ -13,7 +19,6 @@
|
|
|
13
19
|
// npx ksk-ds lint src # contracts/rules.json に基づき consumer UI を検査
|
|
14
20
|
// npx ksk-ds check-duplicates src # DS と同名のローカル実装を検査
|
|
15
21
|
// npx ksk-ds codemod <name> ./src # scripts/codemod/<name>.mjs を実行
|
|
16
|
-
// npx ksk-design-system postinstall # npm postinstall から呼ばれる silent モード
|
|
17
22
|
|
|
18
23
|
import { copyFileSync, existsSync, readdirSync } from "node:fs"
|
|
19
24
|
import { dirname, join, resolve } from "node:path"
|
|
@@ -82,7 +87,7 @@ if (cmd === "demo") {
|
|
|
82
87
|
process.exit(0)
|
|
83
88
|
}
|
|
84
89
|
|
|
85
|
-
if (cmd !== "init"
|
|
90
|
+
if (cmd !== "init") {
|
|
86
91
|
console.error(`未知のコマンド: ${cmd}`)
|
|
87
92
|
console.error(`npx ksk-design-system help を参照してください`)
|
|
88
93
|
process.exit(1)
|
|
@@ -209,32 +214,18 @@ function runDemo(rest) {
|
|
|
209
214
|
console.log(``)
|
|
210
215
|
}
|
|
211
216
|
|
|
212
|
-
// ───
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
const
|
|
216
|
-
const consumerRoot = process.env.INIT_CWD || process.cwd()
|
|
217
|
-
|
|
218
|
-
if (isPostinstall) {
|
|
219
|
-
// DS 自身を開発している場合(INIT_CWD が pkgRoot)はスキップ
|
|
220
|
-
if (!process.env.INIT_CWD || resolve(consumerRoot) === pkgRoot) {
|
|
221
|
-
process.exit(0)
|
|
222
|
-
}
|
|
223
|
-
// consumer のルートに package.json が無ければスキップ(安全のため)
|
|
224
|
-
if (!existsSync(join(consumerRoot, "package.json"))) {
|
|
225
|
-
process.exit(0)
|
|
226
|
-
}
|
|
227
|
-
}
|
|
217
|
+
// ─── init(明示実行のみ)─────────────────────────────────────
|
|
218
|
+
// 書き込み先はコマンドを叩いたディレクトリ。install 時の暗黙実行が無くなったので
|
|
219
|
+
// INIT_CWD 等の環境変数は参照しない。
|
|
220
|
+
const consumerRoot = process.cwd()
|
|
228
221
|
|
|
229
222
|
const files = [
|
|
230
223
|
{ src: "templates/AGENTS.md", dest: "AGENTS.md", label: "Codex 用" },
|
|
231
224
|
{ src: "templates/CLAUDE.md", dest: "CLAUDE.md", label: "Claude Code 用" },
|
|
232
225
|
]
|
|
233
226
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
console.log("AI (Claude / Codex) 向けルールファイルを設置します。\n")
|
|
237
|
-
}
|
|
227
|
+
console.log("ksk-design-system init")
|
|
228
|
+
console.log("AI (Claude / Codex) 向けルールファイルを設置します。\n")
|
|
238
229
|
|
|
239
230
|
let created = 0
|
|
240
231
|
let skipped = 0
|
|
@@ -244,33 +235,21 @@ for (const { src, dest, label } of files) {
|
|
|
244
235
|
const destPath = join(consumerRoot, dest)
|
|
245
236
|
|
|
246
237
|
if (!existsSync(srcPath)) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
process.exit(isPostinstall ? 0 : 1)
|
|
238
|
+
console.error(` ✗ テンプレートが見つかりません: ${srcPath}`)
|
|
239
|
+
process.exit(1)
|
|
251
240
|
}
|
|
252
241
|
|
|
253
242
|
if (existsSync(destPath) && !force) {
|
|
254
|
-
|
|
255
|
-
console.log(` ⏭ ${dest} は既に存在するためスキップ(--force で上書き)`)
|
|
256
|
-
}
|
|
243
|
+
console.log(` ⏭ ${dest} は既に存在するためスキップ(--force で上書き)`)
|
|
257
244
|
skipped++
|
|
258
245
|
continue
|
|
259
246
|
}
|
|
260
247
|
|
|
261
248
|
copyFileSync(srcPath, destPath)
|
|
262
|
-
|
|
263
|
-
console.log(`[ksk-design-system] ${dest} をプロジェクトルートに設置しました`)
|
|
264
|
-
} else {
|
|
265
|
-
console.log(` ✓ ${dest} を設置しました(${label})`)
|
|
266
|
-
}
|
|
249
|
+
console.log(` ✓ ${dest} を設置しました(${label})`)
|
|
267
250
|
created++
|
|
268
251
|
}
|
|
269
252
|
|
|
270
|
-
if (isPostinstall) {
|
|
271
|
-
process.exit(0)
|
|
272
|
-
}
|
|
273
|
-
|
|
274
253
|
console.log(`\n完了: ${created} 作成 / ${skipped} スキップ`)
|
|
275
254
|
|
|
276
255
|
if (created > 0) {
|
package/bin/lint.js
CHANGED
|
@@ -10,16 +10,36 @@ const DEFAULT_EXTENSIONS = new Set([".js", ".jsx", ".ts", ".tsx"])
|
|
|
10
10
|
* engine: "product-theme-override" を持つルールだけをここに流す(issue #364)。
|
|
11
11
|
*/
|
|
12
12
|
const CSS_EXTENSIONS = new Set([".css"])
|
|
13
|
+
// ビルド生成物。ここを走査すると、バンドルされた DS 自身の CSS を
|
|
14
|
+
// 「consumer が DS 変数を上書きしている」と誤認して P049 が大量に出る(#378)。
|
|
15
|
+
// Next.js の `output: "export"` は out/、Nuxt は .output/、Vercel/Turbo は
|
|
16
|
+
// それぞれ .vercel/ .turbo/ を既定の出力先にする。
|
|
13
17
|
const DEFAULT_IGNORES = [
|
|
14
18
|
".git",
|
|
15
19
|
".next",
|
|
20
|
+
".nuxt",
|
|
21
|
+
".output",
|
|
22
|
+
".svelte-kit",
|
|
23
|
+
".turbo",
|
|
24
|
+
".vercel",
|
|
16
25
|
"build",
|
|
17
26
|
"coverage",
|
|
18
27
|
"dist",
|
|
28
|
+
".claude",
|
|
19
29
|
"node_modules",
|
|
30
|
+
"out",
|
|
20
31
|
"storybook-static",
|
|
21
32
|
]
|
|
22
33
|
|
|
34
|
+
// パス単位で除外するもの(セグメント名だけでは絞れないケース)。
|
|
35
|
+
// Capacitor は web のビルド成果物をネイティブプロジェクト配下へコピーするので、
|
|
36
|
+
// そこも DS 自身の CSS を含む(#378)。`public` をセグメントで除外すると
|
|
37
|
+
// Next.js の public/ まで巻き込むため、パス形で限定する。
|
|
38
|
+
const DEFAULT_IGNORE_PATHS = [
|
|
39
|
+
"ios/App/App/public",
|
|
40
|
+
"android/app/src/main/assets/public",
|
|
41
|
+
]
|
|
42
|
+
|
|
23
43
|
export async function runLintCli(argv, { cwd = process.cwd(), pkgRoot = resolve(".") } = {}) {
|
|
24
44
|
const options = parseArgs(argv)
|
|
25
45
|
const rulesPath = resolve(pkgRoot, "contracts/rules.json")
|
|
@@ -182,7 +202,9 @@ function readProductThemeContract(pkgRoot) {
|
|
|
182
202
|
const path = resolve(pkgRoot, "contracts/product-theme-overrides.json")
|
|
183
203
|
if (!existsSync(path)) return null
|
|
184
204
|
try {
|
|
185
|
-
|
|
205
|
+
// pkgRoot を渡すと「DS に実在する変数」だけを違反にする(issue #377)。
|
|
206
|
+
// DS の CSS が読めない環境では接頭辞一致だけの従来判定へフォールバックする。
|
|
207
|
+
return loadProductThemeContract(JSON.parse(readFileSync(path, "utf8")), { pkgRoot })
|
|
186
208
|
} catch {
|
|
187
209
|
return null
|
|
188
210
|
}
|
|
@@ -323,6 +345,7 @@ function shouldIgnorePath(relPath, options) {
|
|
|
323
345
|
if (!relPath || relPath === ".") return false
|
|
324
346
|
const parts = relPath.split("/")
|
|
325
347
|
if (parts.some((part) => DEFAULT_IGNORES.includes(part))) return true
|
|
348
|
+
if (DEFAULT_IGNORE_PATHS.some((ignored) => relPath.includes(ignored))) return true
|
|
326
349
|
return options.excludes.some((exclude) => relPath.includes(exclude))
|
|
327
350
|
}
|
|
328
351
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* P049 — product theme の許可リスト外の DS 変数上書きを検出する(issue #364)
|
|
2
|
+
* P049 — product theme の許可リスト外の DS 変数上書きを検出する(issue #364 / #377)
|
|
3
3
|
*
|
|
4
4
|
* 消費プロダクトの CSS が `:root { --Surface-Primary: … }` のように DS の
|
|
5
5
|
* CSS 変数を上書きするのは、契約 `contracts/product-theme-overrides.json` の
|
|
@@ -10,31 +10,90 @@
|
|
|
10
10
|
* 判定:
|
|
11
11
|
* 1. CSS のコメントを落とす
|
|
12
12
|
* 2. カスタムプロパティ宣言(`--Foo: value;`)を行番号付きで拾う
|
|
13
|
-
* 3. `dsVariableNamespaces` のどれかで始まり、`allowedVariables`
|
|
13
|
+
* 3. `dsVariableNamespaces` のどれかで始まり、`allowedVariables` に無く、
|
|
14
|
+
* かつ **DS 自身の CSS に実在する** ものを違反とする
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
+
* 3 の「実在する」条件は issue #377。接頭辞一致だけだと、消費側が DS の命名に
|
|
17
|
+
* 寄せて作った独自変数(`--Surface-Inverse-Hover` のように DS には無い名前)まで
|
|
18
|
+
* 「DS の上書き」と誤検出する。DS に存在しない変数は上書きしようがない。
|
|
19
|
+
* DS の CSS を読めない環境(パッケージ構成が変わった等)では従来どおり
|
|
20
|
+
* 接頭辞一致だけで判定してフォールバックする。
|
|
16
21
|
*/
|
|
22
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs"
|
|
23
|
+
import { join, resolve } from "node:path"
|
|
24
|
+
|
|
25
|
+
/** カスタムプロパティの**宣言**。`var(--Foo, …)` の参照はコロンの前が `(` なので一致しない。 */
|
|
26
|
+
const DECLARATION = /(^|[;{}\s])(--[A-Za-z0-9_-]+)\s*:/g
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* DS 自身の CSS 変数を宣言しているファイル群(npm package の `files` にも含まれる)。
|
|
30
|
+
* 生成ファイルを増やさずに実行時へ集めるので、DS 側の CSS を足してもドリフトしない。
|
|
31
|
+
*/
|
|
32
|
+
const DS_CSS_DIRS = ["src/styles", "src/themes"]
|
|
33
|
+
const DS_CSS_FILES = ["src/preset.css"]
|
|
17
34
|
|
|
18
35
|
/** CSS コメントを、行数を保ったまま空白に置き換える */
|
|
19
36
|
function stripCssComments(source) {
|
|
20
37
|
return source.replace(/\/\*[\s\S]*?\*\//g, (block) => block.replace(/[^\n]/g, " "))
|
|
21
38
|
}
|
|
22
39
|
|
|
40
|
+
/**
|
|
41
|
+
* DS が実際に宣言している CSS 変数名を集める。
|
|
42
|
+
* 読めない場合は null を返し、呼び出し側は接頭辞一致だけの判定へフォールバックする。
|
|
43
|
+
*
|
|
44
|
+
* @param {string} pkgRoot ksk-design-system パッケージのルート
|
|
45
|
+
* @returns {Set<string> | null}
|
|
46
|
+
*/
|
|
47
|
+
export function collectDsDeclaredVariables(pkgRoot) {
|
|
48
|
+
if (!pkgRoot) return null
|
|
49
|
+
const files = []
|
|
50
|
+
for (const dir of DS_CSS_DIRS) {
|
|
51
|
+
const abs = resolve(pkgRoot, dir)
|
|
52
|
+
if (!existsSync(abs)) continue
|
|
53
|
+
try {
|
|
54
|
+
for (const name of readdirSync(abs)) {
|
|
55
|
+
if (name.endsWith(".css")) files.push(join(abs, name))
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
// 読めないディレクトリは黙って飛ばす(他のファイルで足りることがある)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const file of DS_CSS_FILES) {
|
|
62
|
+
const abs = resolve(pkgRoot, file)
|
|
63
|
+
if (existsSync(abs)) files.push(abs)
|
|
64
|
+
}
|
|
65
|
+
if (files.length === 0) return null
|
|
66
|
+
|
|
67
|
+
const names = new Set()
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
let source
|
|
70
|
+
try {
|
|
71
|
+
source = readFileSync(file, "utf8")
|
|
72
|
+
} catch {
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
for (const match of stripCssComments(source).matchAll(DECLARATION)) names.add(match[2])
|
|
76
|
+
}
|
|
77
|
+
return names.size > 0 ? names : null
|
|
78
|
+
}
|
|
79
|
+
|
|
23
80
|
/**
|
|
24
81
|
* @param {string} source CSS ソース
|
|
25
|
-
* @param {{ allowed: Set<string>, namespaces: string[] }} contract
|
|
82
|
+
* @param {{ allowed: Set<string>, namespaces: string[], declared?: Set<string> | null }} contract
|
|
26
83
|
* @returns {Array<{ line: number, name: string }>}
|
|
27
84
|
*/
|
|
28
85
|
export function inspectProductThemeOverrides(source, contract) {
|
|
29
86
|
const stripped = stripCssComments(source)
|
|
30
87
|
const findings = []
|
|
31
|
-
|
|
32
|
-
const DECLARATION = /(^|[;{}\s])(--[A-Za-z0-9_-]+)\s*:/g
|
|
88
|
+
const declared = contract.declared ?? null
|
|
33
89
|
|
|
34
90
|
for (const match of stripped.matchAll(DECLARATION)) {
|
|
35
91
|
const name = match[2]
|
|
36
92
|
if (contract.allowed.has(name)) continue
|
|
37
93
|
if (!contract.namespaces.some((prefix) => name.startsWith(prefix))) continue
|
|
94
|
+
// DS に実在しない名前は「DS の上書き」ではない(issue #377)。
|
|
95
|
+
// 一覧が取れなかったときは従来どおり接頭辞一致だけで判定する。
|
|
96
|
+
if (declared && !declared.has(name)) continue
|
|
38
97
|
findings.push({
|
|
39
98
|
line: stripped.slice(0, match.index).split(/\r?\n/).length,
|
|
40
99
|
name,
|
|
@@ -43,10 +102,16 @@ export function inspectProductThemeOverrides(source, contract) {
|
|
|
43
102
|
return findings
|
|
44
103
|
}
|
|
45
104
|
|
|
46
|
-
/**
|
|
47
|
-
|
|
105
|
+
/**
|
|
106
|
+
* 契約 JSON から判定に使う形へ畳む。
|
|
107
|
+
*
|
|
108
|
+
* @param {object} contract contracts/product-theme-overrides.json の中身
|
|
109
|
+
* @param {{ pkgRoot?: string }} [options] pkgRoot を渡すと「DS に実在する変数」だけに絞る
|
|
110
|
+
*/
|
|
111
|
+
export function loadProductThemeContract(contract, { pkgRoot } = {}) {
|
|
48
112
|
return {
|
|
49
113
|
allowed: new Set(Object.values(contract.allowedVariables ?? {}).flat()),
|
|
50
114
|
namespaces: Array.isArray(contract.dsVariableNamespaces) ? contract.dsVariableNamespaces : [],
|
|
115
|
+
declared: collectDsDeclaredVariables(pkgRoot),
|
|
51
116
|
}
|
|
52
117
|
}
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"scope": ":root(アプリ全体)または任意の要素(その配下だけ)",
|
|
20
20
|
"allowedPattern": "preset の後に、ここで許可された CSS 変数だけを上書きする",
|
|
21
21
|
"forbiddenPattern": "消費プロダクト側に tokens.json / semantic.css / preset.css のコピーや独自 Tailwind preset を作らない。許可リストに無い内部変数を上書きしない",
|
|
22
|
-
"lintRule": "P049(npx ksk-ds lint が消費側 CSS を検査する)"
|
|
22
|
+
"lintRule": "P049(npx ksk-ds lint が消費側 CSS を検査する)",
|
|
23
|
+
"contrastResponsibility": "neutralPalette(--Primitive-Gray-* / White / Black)を差し替えたら、本文×背景のコントラストは消費プロダクト側の責任になる。Gray は --Text-High-Emphasis(本文色)・--Text-Medium-Emphasis・--Surface-Secondary・--Border-Low-Emphasis など semantic.css の 33 箇所を駆動しているため、差し替え後に本文テキスト×各 Surface が WCAG AA(4.5:1、大きい文字は 3:1)を満たすか必ず実測すること。DS 側の scripts/check-contrast.mjs は DS 既定パレット向けの検査で、消費側の差し替えは見ていない。light / dark の両方を確認する。"
|
|
23
24
|
},
|
|
24
25
|
"allowedVariables": {
|
|
25
26
|
"brandColor": [
|
|
@@ -34,23 +35,66 @@
|
|
|
34
35
|
"--Primitive-Brand-800",
|
|
35
36
|
"--Primitive-Brand-900"
|
|
36
37
|
],
|
|
38
|
+
"neutralPalette": [
|
|
39
|
+
"--Primitive-Gray-50",
|
|
40
|
+
"--Primitive-Gray-100",
|
|
41
|
+
"--Primitive-Gray-200",
|
|
42
|
+
"--Primitive-Gray-300",
|
|
43
|
+
"--Primitive-Gray-400",
|
|
44
|
+
"--Primitive-Gray-500",
|
|
45
|
+
"--Primitive-Gray-600",
|
|
46
|
+
"--Primitive-Gray-700",
|
|
47
|
+
"--Primitive-Gray-800",
|
|
48
|
+
"--Primitive-Gray-900",
|
|
49
|
+
"--Primitive-White",
|
|
50
|
+
"--Primitive-Black"
|
|
51
|
+
],
|
|
37
52
|
"semanticColor": [
|
|
38
53
|
"--Surface-Primary",
|
|
39
54
|
"--Surface-Secondary",
|
|
40
55
|
"--Surface-Tertiary",
|
|
41
56
|
"--Surface-Accent-Primary-Light",
|
|
57
|
+
"--Surface-Inverse",
|
|
42
58
|
"--Text-High-Emphasis",
|
|
43
59
|
"--Text-Medium-Emphasis",
|
|
44
60
|
"--Text-Low-Emphasis",
|
|
45
61
|
"--Text-Accent-Primary",
|
|
46
62
|
"--Text-on-Inverse",
|
|
63
|
+
"--Text-on-Inverse-Secondary",
|
|
64
|
+
"--Object-on-Inverse",
|
|
47
65
|
"--Border-Low-Emphasis",
|
|
48
66
|
"--Border-Medium-Emphasis",
|
|
49
67
|
"--Border-Accent-Primary",
|
|
50
68
|
"--Brand-Primary",
|
|
51
69
|
"--Brand-Action",
|
|
52
70
|
"--Focus-High-Emphasis",
|
|
53
|
-
"--card-surface"
|
|
71
|
+
"--card-surface",
|
|
72
|
+
"--Surface-Caution",
|
|
73
|
+
"--Surface-Caution-Subtle",
|
|
74
|
+
"--Surface-Caution-Strong",
|
|
75
|
+
"--Surface-Success",
|
|
76
|
+
"--Surface-Success-Subtle",
|
|
77
|
+
"--Surface-Warning",
|
|
78
|
+
"--Surface-Info",
|
|
79
|
+
"--Surface-Info-Subtle",
|
|
80
|
+
"--Text-Caution",
|
|
81
|
+
"--Text-Caution-on-Inverse",
|
|
82
|
+
"--Text-Success",
|
|
83
|
+
"--Text-Warning",
|
|
84
|
+
"--Text-Info",
|
|
85
|
+
"--Border-Caution",
|
|
86
|
+
"--Border-Success",
|
|
87
|
+
"--Border-Warning",
|
|
88
|
+
"--Border-Info",
|
|
89
|
+
"--Object-Caution",
|
|
90
|
+
"--Object-Success",
|
|
91
|
+
"--Object-Warning",
|
|
92
|
+
"--Object-Info",
|
|
93
|
+
"--Caution-Base",
|
|
94
|
+
"--Caution-Action",
|
|
95
|
+
"--Success-Base",
|
|
96
|
+
"--Warning-Base",
|
|
97
|
+
"--Info-Base"
|
|
54
98
|
],
|
|
55
99
|
"controlSize": [
|
|
56
100
|
"--Control-Height-Xs",
|
|
@@ -134,7 +178,7 @@
|
|
|
134
178
|
"--glass-",
|
|
135
179
|
"--card-surface"
|
|
136
180
|
],
|
|
137
|
-
"namespaceNote": "P049 が「DS
|
|
181
|
+
"namespaceNote": "P049 が「DS の変数を上書きしている」と判定する接頭辞。ここに該当し、かつ DS 自身の CSS(src/preset.css / src/styles/*.css / src/themes/*.css)に実在し、allowedVariables に無い変数を消費側 CSS が宣言していたら契約違反。接頭辞は一致するが DS に実在しない名前(消費側が DS の命名に寄せて作った独自変数。例: --Surface-Inverse-Hover)は上書きではないので対象外(issue #377)。DS の CSS が読めない環境では接頭辞一致だけで判定する。shadcn 互換の小文字ブリッジ(--primary / --border 等)は消費側アプリが同名変数を持つことがあるため対象外。",
|
|
138
182
|
"wiredComponents": {
|
|
139
183
|
"Button": [
|
|
140
184
|
"--Control-Height-*",
|
|
@@ -166,7 +210,9 @@
|
|
|
166
210
|
"許可リストに無い変数(--Hover-* / --glass-* / --Z-* などの内部変数)は消費側から上書きしない。必要なら DS 側に issue を立てて公開変数を増やす。",
|
|
167
211
|
"DS 側で新しくサイズ・角丸を持つコンポーネントを作るときは、固定の h-* / px-* / rounded-* ではなく Control / Field 変数を arbitrary value(h-[var(--Control-Height-Md)])で参照する。",
|
|
168
212
|
"Control(Button 系)と Field(Input / Textarea / SelectTrigger)はスケールが別。ksk のフィールドは元々ボタンより背が高いため、片方に畳むと実寸が変わる。",
|
|
169
|
-
"
|
|
213
|
+
"primitive を直接上書きしてよいのは Brand(--Primitive-Brand-*)と Neutral(--Primitive-Gray-* / --Primitive-White / --Primitive-Black)の2つのアイデンティティパレットだけ。status 系(Red / Green / Blue / Yellow / Orange)の色調整は semantic 層(--Surface-Caution / --Text-Success / --Caution-Base 等)で行う。semantic を直接上書きするほうが、上書き1箇所で済み、DS 側が primitive の割り当てを変えても壊れない。",
|
|
214
|
+
"neutralPalette(Gray 10段 + White / Black)は Brand と並ぶアイデンティティのパレット層。warm gray など DS 既定の cool gray と違う世界観を持つプロダクトはここを差し替える。ただし Gray は --Text-High-Emphasis(本文色)や --Surface-Secondary を駆動するため、差し替えたら本文×背景のコントラストが WCAG AA(4.5:1)を満たすか消費側で必ず確認すること(light / dark 両方)。DS 側のコントラスト検査は DS 既定パレットしか見ていない。",
|
|
215
|
+
"色は semantic トークン経由でのみ上書きする。Primitive を直接参照するのは Brand ランプ(--Primitive-Brand-*)と Neutral ランプ(--Primitive-Gray-* / White / Black)だけが例外。",
|
|
170
216
|
"Checkbox / RadioGroup / Switch(20px の size-5 トグル)は意図的にこの契約の対象外。「コントロールの高さ」という概念が当てはまらない固定サイズのため、Control 変数を当てると意味が壊れる。",
|
|
171
217
|
"Chip は角丸(--Chip-Radius)だけを配線し、高さ・横 padding は配線しない。sm/md/lg の縦 margin(my-*)は「44px タッチターゲット - 本体高さ」ちょうどの手計算値で、height を変数化すると当たり判定の math が壊れる。",
|
|
172
218
|
"AppShell / MarketingShell / MobileAppShell はページ本文の padding をシェル自身が持たない(Container の gutter か呼び出し側の contentClassName に委ねている)ため product theme の対象外。AdminShell の <main> だけが --Product-Page-Padding-Y を持つ。",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"name": "KSK Design System — Semantic Token Hex Cache",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.60.0",
|
|
5
5
|
"description": "semantic / semanticDark トークン(var(--Primitive-*) 参照)を実 hex に解決したサイドカー生成物。hex はデフォルト(Blue)テーマでの解決値であり、Brand 系(meta.themeDependentKeys に列挙)はテーマ差し替え(orange/green/violet 等)で実色が変わる。テーマ別の完全解決値は `ksk-design-system/native` エクスポート(バンドル済み native トークンモジュール)の themes を参照。tokens.json 本体のスキーマは変更せず、AI がこのファイルだけで実色を把握できるようにし、primitive 値の変更による semantic 実色のドリフトを --check で機械検出する。",
|
|
6
6
|
"generatedBy": "scripts/generate-token-hex-cache.mjs",
|
|
7
7
|
"theme": "default",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ksk-design-system",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.60.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "KSK Design System — フリーランス向けマルチテーマ対応デザインシステム",
|
|
6
6
|
"license": "MIT",
|
|
@@ -174,7 +174,6 @@
|
|
|
174
174
|
"test:a11y": "vitest run --config vitest.a11y.config.ts",
|
|
175
175
|
"check": "tsc -p tsconfig.app.json --noEmit && tsc -p tsconfig.type-tests.json --noEmit && bash scripts/lint-scratch.sh && bash scripts/check-deps.sh && node scripts/check-published-deps.mjs && node scripts/check-css-setup.mjs && bash scripts/check-drift.sh && node scripts/check-screen-contracts.mjs && node scripts/check-docs-drift.mjs && node scripts/check-agents-docs-sync.mjs && node scripts/check-contrast.mjs && node scripts/check-design-md.mjs && node scripts/check-tailwind-v4.mjs && node scripts/check-prefix-order.mjs && node scripts/check-flex-shrink.mjs && node scripts/check-deprecations.mjs && node scripts/generate-migration-doc.mjs --check && bash scripts/check-responsive.sh && node scripts/generate-component-lookup.mjs --check && node scripts/generate-platform-tokens.mjs --check && node scripts/generate-token-hex-cache.mjs --check && node scripts/generate-source-safelist.mjs --check && tsc -p tsconfig.native.json --noEmit && node scripts/check-native-parity.mjs && node scripts/generate-native-component-lookup.mjs --check",
|
|
176
176
|
"check:agent": "npm run lint && npm run check && npm test && npm run lint:story-reuse",
|
|
177
|
-
"postinstall": "node bin/init.js postinstall",
|
|
178
177
|
"version": "node scripts/sync-version.mjs",
|
|
179
178
|
"mcp:build": "cd mcp-server && npm install && npm run build",
|
|
180
179
|
"mcp:start": "node mcp-server/dist/index.js"
|
package/templates/AGENTS.md
CHANGED
package/templates/CLAUDE.md
CHANGED