@cakejp/design-system 0.22.0 → 0.22.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/CHANGELOG.md +14 -0
- package/CLAUDE.md +2 -0
- package/UPDATING.md +2 -0
- package/bin/check-tokens.mjs +116 -0
- package/bin/init.js +9 -0
- package/contracts/components.json +1 -1
- package/contracts/composition.json +1 -1
- package/contracts/rules.json +1 -1
- package/contracts/screen-patterns.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## [0.22.1] - 2026-10-26
|
|
15
|
+
|
|
16
|
+
### ✨ Added
|
|
17
|
+
|
|
18
|
+
- **`npx cakejp-ds check-tokens [DIR]`**: consumer の色が「DS を上げるだけで変わる」状態かを検査する CLI。v0.22.0 のブランドカラー変更で、DS を上げてもオレンジのまま残った原因 4 つを検出し、検出時 exit 1 にする
|
|
19
|
+
- `brand-hex`: ブランド色の値の直書き(`#` / SVG data URL の `%23` / `rgb()`)。色の一覧は `src/styles/primitive.css` の `--Primitive-Orange-*` / `--Primitive-Berry-*` から毎回読むので、DS 側でパレットを変えても検査の更新は要らない
|
|
20
|
+
- `brand-override`: DS のブランド系トークン(`--Brand-*` / `--*-Accent-*` / Primary・Secondary ボタンの hover 等)の上書き
|
|
21
|
+
- `undefined-token`: DS にもプロジェクトにも無い変数の参照(`var(--X, #ffecd9)` のフォールバックが出続ける)
|
|
22
|
+
- `brand-primitive`: `--Primitive-Orange-*` / `--Primitive-Berry-*` の直接参照
|
|
23
|
+
- 例外は同じ行か直前の行に `design-tokens-allow: <理由>`(理由必須)。テスト・docs・ビルド成果物・node_modules は走査しない
|
|
24
|
+
- 原材料・人材・ケーキ屋さん.jp に個別に入れていた同等のスクリプトを置き換える想定
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
14
28
|
## [0.22.0] - 2026-10-26
|
|
15
29
|
|
|
16
30
|
### 🎨 Changed
|
package/CLAUDE.md
CHANGED
|
@@ -65,6 +65,8 @@ PdM や社長が AI(Claude 等)で HTML モックを作る際、このパッ
|
|
|
65
65
|
|
|
66
66
|
**意図的にローカル実装を残すとき(check-duplicates の抑制)**: 対象 export の直前のコメント行(または行末コメント)に `// cakejp-ds-allow-duplicate: <理由>` を書くと、その 1 件だけ検出から外れる。**理由が空なら抑制されない**(形骸化防止)。抑制した件数と理由はサマリに出る。
|
|
67
67
|
|
|
68
|
+
**色は DS のトークンで書き、`npx cakejp-ds check-tokens` を lint / build に組み込む。** DS のバージョンを上げても色が追従しない書き方(ブランド色の値の直書き・DS のブランド系トークンの上書き・DS に無い変数の参照・`--Primitive-Orange-*` / `--Primitive-Berry-*` の直接参照)を検出して exit 1 にする(実例: v0.22.0 のブランドカラー変更で、これらが原因で DS を上げてもオレンジのまま残る画面が各プロダクトにあった)。例外は同じ行か直前の行に `design-tokens-allow: <理由>`(理由必須。例: CSS 変数を使えない `themeColor`)。
|
|
69
|
+
|
|
68
70
|
---
|
|
69
71
|
|
|
70
72
|
## 見た目のフィードバックを受けたときのゲート(DS 起因か consumer 起因かを先に切り分ける)
|
package/UPDATING.md
CHANGED
|
@@ -169,6 +169,8 @@ npm run test # あれば
|
|
|
169
169
|
- **マイナー更新では CHANGELOG の「✨ Added」を読み、ローカルに持っている同等実装の削除候補を確認する**。
|
|
170
170
|
DS に新しく追加された部品(例: 0.10.0 の `ImageUploader` / `ResponsiveDialog` / `DataTable`)と同名のローカル実装が残っていないか、`npx cakejp-ds check-duplicates ./src` を流すと機械検出できる(`--strict` で検出時 exit 1)。
|
|
171
171
|
検出されたローカル実装は DS の import に置き換え、不要になったファイルを削除する。
|
|
172
|
+
- **`npx cakejp-ds check-tokens` を流し、DS を上げても色が追従しない箇所が無いか確認する**(ブランド色の直書き・DS のブランド系トークンの上書き・DS に無い変数・ブランド原色の直接参照を検出、検出時 exit 1)。
|
|
173
|
+
lint / build のスクリプトに組み込んでおくと、色の変更が告知されたときに DS を上げるだけで済む。
|
|
172
174
|
|
|
173
175
|
### 5. PR 作成・マージ
|
|
174
176
|
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* npx cakejp-ds check-tokens [DIR]
|
|
4
|
+
*
|
|
5
|
+
* consumer の色が「DS のバージョンを上げるだけで変わる」状態かを検査する。
|
|
6
|
+
* 2026-10 のブランドカラー変更(オレンジ → ベリー、v0.22.0)で、DS を上げても色が変わらない箇所が
|
|
7
|
+
* 各プロダクトに残った。原因は次の 4 つだったので、それぞれを検出して exit 1 にする。
|
|
8
|
+
*
|
|
9
|
+
* brand-hex ブランド色の値の直書き(--Primitive-Orange-* / --Primitive-Berry-* の値。# / %23 / rgb() 表記)
|
|
10
|
+
* brand-override DS のブランド系トークン(--Brand-* / --*-Accent-* / Primary・Secondary ボタンの hover 等)の上書き定義
|
|
11
|
+
* undefined-token DS の命名(大文字始まり)で、DS にも consumer にも定義が無い変数の参照
|
|
12
|
+
* (var(--X, #ffecd9) のフォールバックが表示され続ける)
|
|
13
|
+
* brand-primitive ブランドの原色(--Primitive-Orange-* / --Primitive-Berry-*)の直接参照
|
|
14
|
+
*
|
|
15
|
+
* ブランド色の一覧は、この package の src/styles/primitive.css から毎回読み直す。
|
|
16
|
+
* そのため DS 側でパレットを変えても、この検査の更新は要らない。
|
|
17
|
+
*
|
|
18
|
+
* 例外: 同じ行か直前の行に「design-tokens-allow: <理由>」を書く(理由が空なら例外にならない)。
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"
|
|
21
|
+
import { dirname, extname, join, relative, resolve } from "node:path"
|
|
22
|
+
import { fileURLToPath } from "node:url"
|
|
23
|
+
|
|
24
|
+
const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..")
|
|
25
|
+
const args = process.argv.slice(2)
|
|
26
|
+
|
|
27
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
28
|
+
console.log(`使い方: npx cakejp-ds check-tokens [DIR]
|
|
29
|
+
|
|
30
|
+
DIR(既定: カレントディレクトリ)以下の .css / .scss / .ts / .tsx / .js / .jsx / .mjs を走査し、
|
|
31
|
+
DS のバージョンを上げても色が追従しない書き方を検出する(検出時 exit 1)。
|
|
32
|
+
|
|
33
|
+
brand-hex ブランド色の値の直書き → var(--Brand-Cake-Orange) / var(--Text-Accent-Primary) などを使う
|
|
34
|
+
brand-override DS のブランド系トークンの上書き → 上書きせず DS の値を使う
|
|
35
|
+
undefined-token DS にも DIR にも無い変数の参照 → DS にある名前を使う
|
|
36
|
+
brand-primitive --Primitive-Orange-* / --Primitive-Berry-* の直接参照 → 意味のあるトークンを使う
|
|
37
|
+
|
|
38
|
+
除外: node_modules / .next / out / dist / build / coverage / __tests__ / tests / docs / public / scripts と、
|
|
39
|
+
*.test.* / *.spec.* / *.stories.* / *.d.ts
|
|
40
|
+
例外: 同じ行か直前の行に「design-tokens-allow: <理由>」(理由必須)`)
|
|
41
|
+
process.exit(0)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const target = resolve(args.find((a) => !a.startsWith("-")) ?? process.cwd())
|
|
45
|
+
const EXTS = new Set([".css", ".scss", ".ts", ".tsx", ".js", ".jsx", ".mjs"])
|
|
46
|
+
const IGNORE_DIRS = new Set(["node_modules", ".next", "out", "dist", "build", "coverage", ".turbo", "__tests__", "tests", "docs", "public", "scripts"])
|
|
47
|
+
const IGNORE_FILE = /\.(test|spec|stories)\.[cm]?[jt]sx?$|\.d\.ts$/
|
|
48
|
+
const ALLOW_RE = /design-tokens-allow:\s*\S/
|
|
49
|
+
const BRAND_TOKEN_RE = /^(Brand-|Primitive-(Orange|Berry)-|(Surface|Text|Object|Border)-Accent-|(Hover|Active)-(Primary|Secondary)-Button$|Object-Favorite$|Border-Favorite$)/
|
|
50
|
+
// CSS の宣言(--X:)に加え、JS/TS のオブジェクトのキー('--X': / "--X":)も宣言として扱う
|
|
51
|
+
const DECL_RE = /(?:^|[\s;{,("'`])--([A-Za-z][\w-]*)["'`]?\s*:/g
|
|
52
|
+
const VAR_RE = /var\(\s*--([A-Z][\w-]*)/g
|
|
53
|
+
|
|
54
|
+
function walk(dir, filter, skip, out = []) {
|
|
55
|
+
for (const name of readdirSync(dir)) {
|
|
56
|
+
if (skip && (skip.has(name) || name.startsWith("."))) continue
|
|
57
|
+
const p = join(dir, name)
|
|
58
|
+
if (statSync(p).isDirectory()) walk(p, filter, skip, out)
|
|
59
|
+
else if (filter(name)) out.push(p)
|
|
60
|
+
}
|
|
61
|
+
return out
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// DS が定義するトークン名(src/styles と dist の CSS)
|
|
65
|
+
const dsNames = new Set()
|
|
66
|
+
for (const sub of ["src/styles", "src", "dist"]) {
|
|
67
|
+
const d = join(pkgRoot, sub)
|
|
68
|
+
if (!existsSync(d)) continue
|
|
69
|
+
for (const f of walk(d, (n) => n.endsWith(".css"), null)) {
|
|
70
|
+
for (const m of readFileSync(f, "utf8").matchAll(DECL_RE)) dsNames.add(m[1])
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ブランドのパレット(旧オレンジ・現行ベリー)を primitive.css から取り出す
|
|
75
|
+
const palette = []
|
|
76
|
+
const primitive = readFileSync(join(pkgRoot, "src/styles/primitive.css"), "utf8")
|
|
77
|
+
for (const m of primitive.matchAll(/--Primitive-(?:Orange|Berry)-\d+\s*:\s*#([0-9a-f]{6})(?:[0-9a-f]{2})?/gi)) palette.push(m[1].toUpperCase())
|
|
78
|
+
if (palette.length === 0) {
|
|
79
|
+
console.error("DS の src/styles/primitive.css からブランドのパレットを読めませんでした")
|
|
80
|
+
process.exit(2)
|
|
81
|
+
}
|
|
82
|
+
const HEX_RE = new RegExp(`(?:#|%23)(${palette.join("|")})(?:[0-9a-f]{2})?(?![0-9a-f])`, "i")
|
|
83
|
+
// rgb(224, 75, 0) / rgba(224,75,0,.5) に加え、空白区切り・スラッシュ区切りの透明度 rgb(224 75 0 / 0.5) も見る
|
|
84
|
+
const rgbAlts = palette.map((h) => [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16)).join("(?:\\s*,\\s*|\\s+)"))
|
|
85
|
+
const RGB_RE = new RegExp(`rgba?\\(\\s*(${rgbAlts.join("|")})\\s*[,)/]`, "i")
|
|
86
|
+
|
|
87
|
+
const files = walk(target, (n) => EXTS.has(extname(n)) && !IGNORE_FILE.test(n), IGNORE_DIRS)
|
|
88
|
+
const texts = files.map((f) => [f, readFileSync(f, "utf8")])
|
|
89
|
+
const localNames = new Set()
|
|
90
|
+
for (const [, text] of texts) for (const m of text.matchAll(DECL_RE)) localNames.add(m[1])
|
|
91
|
+
|
|
92
|
+
const problems = []
|
|
93
|
+
for (const [file, text] of texts) {
|
|
94
|
+
const lines = text.split("\n")
|
|
95
|
+
lines.forEach((line, i) => {
|
|
96
|
+
if (ALLOW_RE.test(line) || (i > 0 && ALLOW_RE.test(lines[i - 1]))) return
|
|
97
|
+
const where = `${relative(target, file)}:${i + 1}`
|
|
98
|
+
const hex = line.match(HEX_RE) || line.match(RGB_RE)
|
|
99
|
+
if (hex) problems.push([where, "brand-hex", `ブランド色の直書き ${hex[0]} → var(--Brand-Cake-Orange) / var(--Text-Accent-Primary) などの DS トークンを使う`])
|
|
100
|
+
for (const m of line.matchAll(DECL_RE)) {
|
|
101
|
+
if (dsNames.has(m[1]) && BRAND_TOKEN_RE.test(m[1])) problems.push([where, "brand-override", `DS のブランド系トークン --${m[1]} を上書きしている → 上書きせず DS の値を使う`])
|
|
102
|
+
}
|
|
103
|
+
for (const m of line.matchAll(VAR_RE)) {
|
|
104
|
+
if (!dsNames.has(m[1]) && !localNames.has(m[1])) problems.push([where, "undefined-token", `--${m[1]} は DS にもこのプロジェクトにも無い → DS にある名前を使う`])
|
|
105
|
+
else if (/^Primitive-(Orange|Berry)-\d+$/.test(m[1])) problems.push([where, "brand-primitive", `ブランドの原色 --${m[1]} を直接参照している → --Brand-Cake-Orange / --Brand-Cake-Orange-Light / --Hover-Primary-Button などを使う`])
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (problems.length) {
|
|
111
|
+
console.error(`check-tokens: ${problems.length} 件`)
|
|
112
|
+
for (const [where, rule, msg] of problems) console.error(` ${where} [${rule}] ${msg}`)
|
|
113
|
+
console.error("例外にする行には「design-tokens-allow: <理由>」を同じ行か直前の行に書く")
|
|
114
|
+
process.exit(1)
|
|
115
|
+
}
|
|
116
|
+
console.log(`check-tokens: OK(${files.length} ファイル、DS トークン ${dsNames.size} 件、ブランドの色 ${palette.length} 件)`)
|
package/bin/init.js
CHANGED
|
@@ -51,6 +51,9 @@ if (cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
|
51
51
|
意図的に残すローカル実装は、対象 export の直前行に
|
|
52
52
|
「// cakejp-ds-allow-duplicate: <理由>」を書くと抑制できる(理由必須)
|
|
53
53
|
詳細: npx cakejp-ds check-duplicates --help
|
|
54
|
+
npx cakejp-ds check-tokens [DIR] DS を上げても色が追従しない書き方(ブランド色の直書き・DS トークンの
|
|
55
|
+
上書き・DS に無い変数・ブランド原色の直接参照)を検出(検出時 exit 1)
|
|
56
|
+
例外は行に「design-tokens-allow: <理由>」。詳細: npx cakejp-ds check-tokens --help
|
|
54
57
|
`)
|
|
55
58
|
process.exit(0)
|
|
56
59
|
}
|
|
@@ -87,6 +90,12 @@ if (cmd === "check-duplicates") {
|
|
|
87
90
|
process.exit(0)
|
|
88
91
|
}
|
|
89
92
|
|
|
93
|
+
if (cmd === "check-tokens") {
|
|
94
|
+
process.argv = [process.argv[0], process.argv[1], ...args.slice(1)]
|
|
95
|
+
await import(join(pkgRoot, "bin/check-tokens.mjs"))
|
|
96
|
+
process.exit(0)
|
|
97
|
+
}
|
|
98
|
+
|
|
90
99
|
if (cmd !== "init" && cmd !== "postinstall") {
|
|
91
100
|
console.error(`未知のコマンド: ${cmd}`)
|
|
92
101
|
console.error(`npx cakejp-ds help を参照してください`)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"name": "Cake.jp Design System — Component Contracts",
|
|
4
|
-
"version": "0.22.
|
|
4
|
+
"version": "0.22.1",
|
|
5
5
|
"description": "全コンポーネントの構造化定義。バリアント・アクセシビリティ要件・使用ルールを機械可読形式で管理。",
|
|
6
6
|
"kind": "kind:\"shell\" は複数ページで再利用されるレイアウト枠(AdminShell / WorkspaceShell / MypageShell 等)。無印は部品コンポーネント。固有ページを組むときは該当 shell から始める。src/components/pages/ 配下のストーリーは特定ページ専用・非配布のため contracts の対象外。",
|
|
7
7
|
"naming": "コンポーネント名は**用途ではなく機能**で付ける。用途名(QuantitySelector = カート数量)にすると、同じ機能を別の文脈で探した人が見つけられず二重実装が生まれる(実例: 用途名だったため NumberInput が別途作られ、意匠の違うステッパーが 2 つになった)。「〜Selector」「〜Picker」等の用途接尾辞を付ける前に、その部品が何をするものかで名前が付けられないかを先に検討する。既存名を機能名へ変える場合は deprecatedAliases に旧名を残す。",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"name": "Cake.jp Design System — Composition Contracts",
|
|
4
|
-
"version": "0.22.
|
|
4
|
+
"version": "0.22.1",
|
|
5
5
|
"description": "ページ骨格・視覚階層・余白リズムの機械可読な正本。画面パターンを選んだ後の「実際の組み方」を規定する。ページの外枠(モバイル/デスクトップ/管理)、商品グリッドの列数、余白の4段リズム、Surface のネスト階層、テキスト階層、CTA の優先順位を構造化する。人間向けの解説は guidelines/composition/layout.md・guidelines/composition/hierarchy.md・guidelines/overview.md。",
|
|
6
6
|
"source": "guidelines/composition/layout.md, guidelines/composition/hierarchy.md, guidelines/overview.md, guidelines/components/button.md(CTA 密度ルール)を正本化",
|
|
7
7
|
"roleSeparation": "screen-patterns.json はページ種別(Standard Page / Focused Workspace / ResponsiveDialog / Dialog)を選ぶための契約。composition.json は選んだ後、その骨格をどう組むか(外枠構造・余白リズム・階層)の契約。前者=何を使うか、後者=どう並べるか。",
|
package/contracts/rules.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"name": "Cake.jp Design System — Screen Patterns Contracts",
|
|
4
|
-
"version": "0.22.
|
|
4
|
+
"version": "0.22.1",
|
|
5
5
|
"description": "CRUD 機能でどの画面パターンを使うかの機械可読な判断基準。5パターンの定義・CRUD 対応・決定木・編集UIの観点比較を構造化する。人間向けの解説は guidelines/foundations/screen-patterns.md。",
|
|
6
6
|
"source": "GitHub issue #37 CRUDの画面設計の型化と基本パターンの整備",
|
|
7
7
|
"relatedContract": "本契約はページ種別の選択(何を使うか)を担う。選んだ後の骨格の組み方(外枠構造・余白リズム・視覚階層)は contracts/composition.json を参照する。",
|