@cakejp/design-system 0.12.0 → 0.14.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/CLAUDE.md +12 -5
  3. package/MIGRATION.md +45 -0
  4. package/UPDATING.md +13 -0
  5. package/bin/check-duplicates.mjs +133 -9
  6. package/bin/exports-parser.mjs +54 -0
  7. package/bin/init.js +3 -0
  8. package/contracts/components.json +504 -209
  9. package/contracts/composition.json +11 -8
  10. package/contracts/rules.json +354 -113
  11. package/contracts/screen-patterns.json +1 -1
  12. package/dist/index.css +1 -1
  13. package/dist/index.js +4285 -3422
  14. package/dist/types/components/ec/admin-shell.d.ts +30 -2
  15. package/dist/types/components/ec/product-detail-sidebar.d.ts +1 -1
  16. package/dist/types/components/foundations/card.d.ts +27 -3
  17. package/dist/types/components/foundations/field.d.ts +26 -3
  18. package/dist/types/components/foundations/input.d.ts +3 -1
  19. package/dist/types/components/foundations/social-icon.d.ts +33 -0
  20. package/dist/types/components/foundations/textarea.d.ts +3 -1
  21. package/dist/types/components/primitives/chat-composer.d.ts +87 -0
  22. package/dist/types/components/primitives/combobox.d.ts +24 -0
  23. package/dist/types/components/primitives/content-carousel.d.ts +7 -1
  24. package/dist/types/components/primitives/date-picker.d.ts +15 -3
  25. package/dist/types/components/primitives/funnel-shell.d.ts +2 -0
  26. package/dist/types/components/primitives/image-input.d.ts +119 -0
  27. package/dist/types/components/primitives/multi-select.d.ts +24 -0
  28. package/dist/types/components/primitives/notification-badge.d.ts +1 -1
  29. package/dist/types/components/primitives/number-input.d.ts +84 -0
  30. package/dist/types/components/primitives/section-header.d.ts +9 -2
  31. package/dist/types/components/primitives/section.d.ts +35 -2
  32. package/dist/types/components/primitives/social-login-button.d.ts +3 -1
  33. package/dist/types/components/primitives/timeline.d.ts +23 -0
  34. package/dist/types/components/primitives/workspace-shell.d.ts +2 -0
  35. package/dist/types/index.d.ts +25 -7
  36. package/dist/types/lib/pagination-range.d.ts +16 -0
  37. package/dist/types/lib/use-value-length.d.ts +31 -0
  38. package/eslint/deprecated.js +44 -0
  39. package/package.json +8 -3
  40. package/scripts/codemod/README.md +14 -0
  41. package/scripts/codemod/template.mjs +62 -0
  42. package/src/components/COMPONENT_LOOKUP.md +166 -0
  43. package/src/preset-standalone.css +16 -0
  44. package/src/preset.css +17 -0
  45. package/src/styles/typography.css +7 -0
  46. package/templates/AGENTS.md +4 -0
  47. package/templates/CLAUDE.md +4 -0
  48. package/tokens.json +933 -87
  49. package/dist/types/components/primitives/quantity-selector.d.ts +0 -50
@@ -1,14 +1,18 @@
1
1
  {
2
2
  "meta": {
3
3
  "name": "Cake.jp Design System — Component Contracts",
4
- "version": "0.1.0",
4
+ "version": "0.14.0",
5
5
  "description": "全コンポーネントの構造化定義。バリアント・アクセシビリティ要件・使用ルールを機械可読形式で管理。",
6
6
  "kind": "kind:\"shell\" は複数ページで再利用されるレイアウト枠(AdminShell / WorkspaceShell / MypageShell 等)。無印は部品コンポーネント。固有ページを組むときは該当 shell から始める。src/components/pages/ 配下のストーリーは特定ページ専用・非配布のため contracts の対象外。",
7
+ "naming": "コンポーネント名は**用途ではなく機能**で付ける。用途名(QuantitySelector = カート数量)にすると、同じ機能を別の文脈で探した人が見つけられず二重実装が生まれる(実例: 用途名だったため NumberInput が別途作られ、意匠の違うステッパーが 2 つになった)。「〜Selector」「〜Picker」等の用途接尾辞を付ける前に、その部品が何をするものかで名前が付けられないかを先に検討する。既存名を機能名へ変える場合は deprecatedAliases に旧名を残す。",
8
+ "exportNaming": "name は原則そのまま @cakejp/design-system から import できる export 名。例外は機械可読に印を付ける — exported:false=name は実装ファイル/グループ名で import できない(実 export は subcomponents 側。例: CheckboxCard → CheckboxCardGroup / CheckboxCardItem)、exportedAs=別名で export される(例: FormField → CakeFormField)、deprecatedAliases=互換のため残す旧 export 名(新規利用禁止)。この対応は __tests__/contracts-export-integrity.test.ts が src/index.ts と突き合わせて固定し、bin/check-duplicates.mjs の照合対象もこの規約に従う。",
9
+ "formBinding": "react-hook-form との繋ぎ方。formBinding:\"register\"=ネイティブ form control に props/ref を透過するので {...register(\"name\")} を直接 spread できる(Input / Textarea)。formBinding:\"controller\"=Radix ベースで ref が <button> 等に着地する、または値が Date/File など非文字列のため Controller の field.value / field.onChange に繋ぐ。formBinding:\"layout\"=値を持たないレイアウト部品でバインド対象ではない(Field / Form / FormField / ProductOption)。formBinding:\"none\"=値は扱うが単一フィールドのバインド対象ではない自己完結フォーム・複合ウィジェット(DataTable / SearchBar / ReviewForm)。この切り分けは __tests__/react-hook-form-integration.test.tsx が実挙動で固定する(issue #151 / #152)。",
7
10
  "counts": {
8
- "foundations": 50,
9
- "primitives": 44,
11
+ "foundations": 51,
12
+ "primitives": 49,
10
13
  "ec": 24,
11
- "total": 118
14
+ "total": 124,
15
+ "note": "counts は各ティアの実装ファイル数(scripts/check-drift.sh が src/components/<tier>/*.tsx と突き合わせる)。subcomponents / deprecatedAliases は含まない。"
12
16
  }
13
17
  },
14
18
  "foundations": [
@@ -16,13 +20,6 @@
16
20
  "name": "Accordion",
17
21
  "path": "src/components/foundations/accordion.tsx"
18
22
  },
19
- {
20
- "name": "AlertDialog",
21
- "path": "src/components/foundations/alert-dialog.tsx",
22
- "rules": [
23
- "破壊的操作の確認に使用。削除ボタン押下後に必ず挟む"
24
- ]
25
- },
26
23
  {
27
24
  "name": "Alert",
28
25
  "path": "src/components/foundations/alert.tsx",
@@ -44,6 +41,18 @@
44
41
  "inline variant(inline-info/inline-caution/inline-warning)も同じ prop-based API に対応(#109)。フォーム内・セクション内の軽量な補足情報用。アイコンは20pxで自動選択"
45
42
  ]
46
43
  },
44
+ {
45
+ "name": "AlertDialog",
46
+ "path": "src/components/foundations/alert-dialog.tsx",
47
+ "sizes": [
48
+ "default",
49
+ "sm"
50
+ ],
51
+ "rules": [
52
+ "破壊的操作の確認に使用。削除ボタン押下後に必ず挟む",
53
+ "幅上限は default=480px / sm=320px。SP では画面端から上下左右 40px の余白を保つ(コンポーネントが持つので consumer 側で margin を足さない)"
54
+ ]
55
+ },
47
56
  {
48
57
  "name": "AspectRatio",
49
58
  "path": "src/components/foundations/aspect-ratio.tsx",
@@ -104,6 +113,10 @@
104
113
  ],
105
114
  "rules": [
106
115
  "<button>を直接使わない。必ずButtonコンポーネントを使う",
116
+ "高さは size='default'=44px(h-11) / xs=24px / sm=32px / lg=48px / xl=56px / hero=min 56px。default はフォーム入力コントロール(Input / SelectTrigger default / DatePicker trigger)と同じ 44px なので、入力行にボタンを並置しても揃う",
117
+ "⚠️ variant='secondary' はブランドアクセント意匠(Surface-Accent-Primary-Light地+Brand-Cake-Orange文字)。Material/Bootstrap/shadcn の secondary(グレー・控えめ)とは別物なので、他DSの感覚で選ばない",
118
+ "「戻る」「キャンセル」「閉じる」「スキップ」「あとで」等の後退・中立アクションは variant='tertiary'(Surface-Tertiary地+Text-High-Emphasis文字のニュートラルなグレー)を使う。主CTAの隣に置く2番目のボタンでも、役割が後退なら secondary ではなく tertiary",
119
+ "variant='secondary' を使うのは、主CTAと別方向の“前進”を促す副CTAのとき(例: 主='購入する' に対し 副='サンプルを見る')",
107
120
  "variant='overlay'は画像・メディアの上に載せる操作ボタン専用(半透過白のSurface-Overlay-Light下地+shadow-md+backdrop-blur-sm)。ImageCarousel矢印と同系の意匠。size='icon'+className='rounded-full'で円形にできる"
108
121
  ]
109
122
  },
@@ -133,24 +146,37 @@
133
146
  "rules": [
134
147
  "paddingはCard本体のp-4で一括管理。全幅コンテンツは-mx-4で突き破る",
135
148
  "子要素の縦間隔はCard本体のflex-col gap-4(16px)で一括管理。直下の子にmargin-top/bottom/space-yを足さない(gapと二重がけになり間延びする)。丸アイコンはIconBadgeを使う(外側marginを持たない)",
136
- "surface='default'(既定)はbg-card(Surface-Secondary)。Surface-Tertiary系(グレーSection)の地の上ではコントラスト不足になるためsurface='primary'(白=Surface-Primary)を使う"
149
+ "surface='default'(既定)は --card-surface(親から伝播したサーフェス文脈)→無ければ --card(Surface-Secondary)。surface='primary'は文脈に関係なく白(Surface-Primary)で固定する",
150
+ "Section background='subtle'|'accent-subtle' と、地がSurface-Secondaryのシェル(AdminShell / WorkspaceShell / FunnelShell)の配下では --card-surface が伝播するため、Card は surface 未指定のまま自動で白になる(surface='primary' の明示指定は従来通り有効で優先)",
151
+ "DSのSectionを使わない自前のグレー地では、親要素に className='[--card-surface:var(--Surface-Primary)]' を宣言すれば配下Cardが白になる(darkにも配慮するなら dark:[--card-surface:var(--Surface-Secondary)] を併記)。1枚だけ地に馴染ませたいときは className='bg-card' で上書きする"
137
152
  ]
138
153
  },
139
- {
140
- "name": "CheckboxCard",
141
- "path": "src/components/foundations/checkbox-card.tsx"
142
- },
143
- {
144
- "name": "CheckboxGroup",
145
- "path": "src/components/foundations/checkbox-group.tsx"
146
- },
147
154
  {
148
155
  "name": "Checkbox",
149
156
  "path": "src/components/foundations/checkbox.tsx",
157
+ "formBinding": "controller",
150
158
  "accessibility": [
151
159
  "必ずLabelと紐付ける"
152
160
  ]
153
161
  },
162
+ {
163
+ "name": "CheckboxCard",
164
+ "path": "src/components/foundations/checkbox-card.tsx",
165
+ "formBinding": "controller",
166
+ "exported": false,
167
+ "subcomponents": [
168
+ "CheckboxCardGroup",
169
+ "CheckboxCardItem"
170
+ ]
171
+ },
172
+ {
173
+ "name": "CheckboxGroup",
174
+ "path": "src/components/foundations/checkbox-group.tsx",
175
+ "formBinding": "controller",
176
+ "subcomponents": [
177
+ "CheckboxGroupItem"
178
+ ]
179
+ },
154
180
  {
155
181
  "name": "Collapsible",
156
182
  "path": "src/components/foundations/collapsible.tsx",
@@ -170,6 +196,7 @@
170
196
  {
171
197
  "name": "DataTable",
172
198
  "path": "src/components/foundations/data-table.tsx",
199
+ "formBinding": "none",
173
200
  "rules": [
174
201
  "管理画面のテーブルはui/tableでなくこちらを使う。ソート・選択行・アクションメニュー対応",
175
202
  "ActionCellのメニュー操作は行のonClickへ伝播しない(本体でstopPropagation済み)。行クリック選択との併用時も利用側ガードは不要"
@@ -202,7 +229,9 @@
202
229
  {
203
230
  "name": "Field",
204
231
  "path": "src/components/foundations/field.tsx",
232
+ "formBinding": "layout",
205
233
  "subcomponents": [
234
+ "FieldLabel",
206
235
  "FieldSet",
207
236
  "FieldLegend",
208
237
  "FieldGroup",
@@ -211,14 +240,41 @@
211
240
  "FieldSeparator"
212
241
  ],
213
242
  "rules": [
214
- "フォーム全体の骨格を組む補助群。個々の入力欄は FormField を使う",
243
+ "フォーム全体の骨格を組む補助群。ラベル+入力+エラーが一体で済むなら FormField が最短。ラベル・入力・説明・エラーを個別に組むときは Field + FieldLabel を使う",
244
+ "Field は 1 フィールド分の箱(縦リズムの最小単位)。orientation='vertical'(既定)/'horizontal'/'responsive'。responsive は FieldGroup の @container/field-group を基準に狭い=縦・広い=横へ切り替わる",
215
245
  "余白は縦リズムに従う(FieldSet=gap-6 / FieldGroup=gap-4)。手で mt-/mb- を足さない",
216
246
  "見出しは FieldLegend、グループ横断の区切りは FieldSeparator を使う"
217
247
  ]
218
248
  },
249
+ {
250
+ "name": "FileInput",
251
+ "path": "src/components/foundations/file-input.tsx",
252
+ "formBinding": "controller",
253
+ "accessibility": [
254
+ "idをLabel/FormFieldのhtmlForと揃えて紐付ける",
255
+ "ネイティブ<input type='file'>をvisually-hiddenで内包しキーボード・SR操作可能",
256
+ "エラー表示はaria-describedbyでFieldErrorと接続"
257
+ ],
258
+ "rules": [
259
+ "<input type='file'>を直接書かない。汎用の添付(PDF/CSV/画像混在)はFileInputを使う",
260
+ "画像1枚の入力(プレビュー+形式/サイズ検証+値のバインド)はImageInputを使う",
261
+ "複数枚の画像を並べて管理するグリッドはImageUploaderを使う",
262
+ "複数選択時(multiple)は選択済みを「n件選択」で集約表示",
263
+ "maxSize(バイト)超過ファイルは受理せずonErrorを呼ぶ"
264
+ ]
265
+ },
219
266
  {
220
267
  "name": "Form",
221
268
  "path": "src/components/foundations/form.tsx",
269
+ "formBinding": "layout",
270
+ "subcomponents": [
271
+ "FormField",
272
+ "FormItem",
273
+ "FormLabel",
274
+ "FormControl",
275
+ "FormDescription",
276
+ "FormMessage"
277
+ ],
222
278
  "accessibility": [
223
279
  "aria-describedbyでエラーメッセージを紐付ける"
224
280
  ]
@@ -245,12 +301,16 @@
245
301
  {
246
302
  "name": "Input",
247
303
  "path": "src/components/foundations/input.tsx",
304
+ "formBinding": "register",
248
305
  "accessibility": [
249
306
  "必ずLabelと紐付ける"
250
307
  ],
251
308
  "rules": [
252
309
  "<input>を直接使わない。type='email'/'tel'を適切に指定する",
253
- "文字数カウンタは showCount + maxLength で有効化(右下に {現在}/{max}、上限で Text-Caution)。制御/非制御両対応"
310
+ "高さは 44px(h-11)。フォーム入力コントロールと Button の既定(Input / SelectTrigger default / DatePicker trigger / Button size='default')は 44px で統一されており、検索+絞り込みセレクト+ボタンのような並置でも揃う",
311
+ "44px は rules.json の touchTarget『Text input』の min を満たす値であり、DS の既定として採用している。recommended の 48px まで上げたい画面は consumer が className='h-12' で上書きする",
312
+ "文字数カウンタは showCount + maxLength で有効化(右下に {現在}/{max}、上限で Text-Caution)。制御/非制御両対応",
313
+ "非制御は react-hook-form の register() / reset() で ref 経由に値が入る実利用経路まで story とテストで検証する"
254
314
  ]
255
315
  },
256
316
  {
@@ -288,33 +348,29 @@
288
348
  {
289
349
  "name": "RadioGroup",
290
350
  "path": "src/components/foundations/radio-group.tsx",
351
+ "formBinding": "controller",
352
+ "subcomponents": [
353
+ "RadioGroupItem"
354
+ ],
291
355
  "variants": [
292
356
  "tile",
293
357
  "chip"
294
358
  ],
295
359
  "accessibility": [
296
- "各RadioGroupItemにLabel必須"
360
+ "各RadioGroupItemにラベルテキスト必須。ラベルは children として RadioGroupItem の内側に書く(<RadioGroupItem value='x'>ラベル</RadioGroupItem>)。RadioGroupItem が内部で <label> 相当の関連付けまで行うため、外側に <Label htmlFor> を併置してはいけない(ラベル重複になる)"
361
+ ],
362
+ "props": [
363
+ "children: ラベル本文(必須。tile/chip 共通)",
364
+ "description?: ReactNode — ラベル下の補足説明(tile のみ)",
365
+ "expandedContent?: ReactNode — 選択時に展開する追加コンテンツ(tile のみ)"
297
366
  ],
298
367
  "rules": [
299
368
  "variant='tile'(既定)=ラジオ丸+ラベル+補足説明+展開コンテンツのタイル型",
369
+ "⚠️ タイルの枠(border/角丸/余白/選択状態の意匠)は RadioGroupItem 自身が描画する。consumer 側で <div className='border rounded-lg ...'> 等で囲むと枠が二重になる。囲まずにそのまま並べる",
370
+ "ラベルは children で渡す。<RadioGroupItem/> と <Label htmlFor> を兄弟に並べる書き方は誤り(枠の二重化+ラベル重複を招く)",
300
371
  "variant='chip'=ラベルを内包したChoice Chip意匠の排他選択(タグ選択・絞り込み等)。ラジオ丸は描画せず、Chip本体と同じchipVariantsを再利用して選択状態をaccent意匠(静止=薄オレンジ/hover=ベタ塗り)で示す。description/expandedContentは持たない。Radixのキーボード操作・a11yは維持"
301
372
  ]
302
373
  },
303
- {
304
- "name": "FileInput",
305
- "path": "src/components/foundations/file-input.tsx",
306
- "accessibility": [
307
- "idをLabel/FormFieldのhtmlForと揃えて紐付ける",
308
- "ネイティブ<input type='file'>をvisually-hiddenで内包しキーボード・SR操作可能",
309
- "エラー表示はaria-describedbyでFieldErrorと接続"
310
- ],
311
- "rules": [
312
- "<input type='file'>を直接書かない。汎用の添付(PDF/CSV/画像混在)はFileInputを使う",
313
- "プレビュー付きの画像管理(並び替え・複数サムネイル)はImageUploaderを使う",
314
- "複数選択時(multiple)は選択済みを「n件選択」で集約表示",
315
- "maxSize(バイト)超過ファイルは受理せずonErrorを呼ぶ"
316
- ]
317
- },
318
374
  {
319
375
  "name": "ResponsiveDialog",
320
376
  "path": "src/components/foundations/responsive-dialog.tsx",
@@ -329,8 +385,10 @@
329
385
  {
330
386
  "name": "Select",
331
387
  "path": "src/components/foundations/select.tsx",
388
+ "formBinding": "controller",
332
389
  "rules": [
333
- "<select>HTMLネイティブを使わない"
390
+ "<select>HTMLネイティブを使わない",
391
+ "SelectTrigger の高さは size='default'=44px(h-11)で Input / DatePicker trigger / Button default と揃う。size='sm'=36px。高さは素のクラスで当てているため className='h-12' 等で上書きできる(data 属性セレクタではない)"
334
392
  ]
335
393
  },
336
394
  {
@@ -373,13 +431,22 @@
373
431
  },
374
432
  {
375
433
  "name": "Slider",
376
- "path": "src/components/foundations/slider.tsx"
434
+ "path": "src/components/foundations/slider.tsx",
435
+ "formBinding": "controller"
377
436
  },
378
437
  {
379
- "name": "Sonner",
380
- "path": "src/components/foundations/sonner.tsx",
438
+ "name": "SocialIcon",
439
+ "path": "src/components/foundations/social-icon.tsx",
440
+ "props": [
441
+ "platform",
442
+ "tone?: brand | mono",
443
+ "size?: number"
444
+ ],
381
445
  "rules": [
382
- "トースト内にアクションリンクを入れない。左ボーダーではなく背景色全体で状態を表現"
446
+ "SNS・外部サービスのロゴSVGは手書きせずSocialIconへ集約する",
447
+ "ブランド色はThird-Partyトークンを使う",
448
+ "ブランド色の地(LINEの緑・Appleの黒)に載せるときはtone=\"mono\"を指定する。brandのままだとアイコンが地と同色になって消える",
449
+ "サイズはsize(px)で指定する。classNameのsize-*はinline styleに負ける"
383
450
  ]
384
451
  },
385
452
  {
@@ -389,6 +456,7 @@
389
456
  {
390
457
  "name": "Switch",
391
458
  "path": "src/components/foundations/switch.tsx",
459
+ "formBinding": "controller",
392
460
  "accessibility": [
393
461
  "Label必須"
394
462
  ]
@@ -412,23 +480,35 @@
412
480
  {
413
481
  "name": "Textarea",
414
482
  "path": "src/components/foundations/textarea.tsx",
483
+ "formBinding": "register",
415
484
  "accessibility": [
416
485
  "Label必須"
417
486
  ],
418
487
  "rules": [
419
- "文字数カウンタは showCount + maxLength で有効化(右下に {現在}/{max}、上限で Text-Caution)。制御/非制御両対応"
488
+ "文字数カウンタは showCount + maxLength で有効化(右下に {現在}/{max}、上限で Text-Caution)。制御/非制御両対応",
489
+ "非制御は react-hook-form の register() / reset() で ref 経由に値が入る実利用経路まで story とテストで検証する"
420
490
  ]
421
491
  },
422
492
  {
423
- "name": "ToggleGroup",
424
- "path": "src/components/foundations/toggle-group.tsx",
493
+ "name": "Toaster",
494
+ "path": "src/components/foundations/sonner.tsx",
425
495
  "rules": [
426
- "水平パディングはコンポーネント内に入れない。親が管理する"
496
+ "トースト内にアクションリンクを入れない。左ボーダーではなく背景色全体で状態を表現",
497
+ "アプリのルートに <Toaster /> を1つ置き、表示は toast()(sonner の再 export)で呼ぶ"
427
498
  ]
428
499
  },
429
500
  {
430
501
  "name": "Toggle",
431
- "path": "src/components/foundations/toggle.tsx"
502
+ "path": "src/components/foundations/toggle.tsx",
503
+ "formBinding": "controller"
504
+ },
505
+ {
506
+ "name": "ToggleGroup",
507
+ "path": "src/components/foundations/toggle-group.tsx",
508
+ "formBinding": "controller",
509
+ "rules": [
510
+ "水平パディングはコンポーネント内に入れない。親が管理する"
511
+ ]
432
512
  },
433
513
  {
434
514
  "name": "Tooltip",
@@ -440,35 +520,30 @@
440
520
  ],
441
521
  "primitives": [
442
522
  {
443
- "name": "CoachMark",
444
- "path": "src/components/primitives/coach-mark.tsx"
445
- },
446
- {
447
- "name": "BannerCarousel",
448
- "path": "src/components/primitives/banner-carousel.tsx"
449
- },
450
- {
451
- "name": "ContentCarousel",
452
- "path": "src/components/primitives/content-carousel.tsx",
453
- "features": [
454
- "scroll-snap",
455
- "dots",
456
- "arrows",
457
- "autoPlay"
458
- ],
523
+ "name": "AdminHeader",
524
+ "path": "src/components/primitives/admin-header.tsx",
459
525
  "rules": [
460
- "ReactNode スライド対応(slides: ReactNode[])。テキスト主体のヒーローバナー用",
461
- "画像だけのバナーは ImageCarousel を使う",
462
- "矢印・ドットの意匠は ImageCarousel と同一"
526
+ "管理画面向けの上端固定ヘッダーの汎用枠。左に SP ハンバーガー+brand、右に actions slot",
527
+ "文言合成(『{shopName}管理』等)はしない。brand / actions を利用側が組み立てて渡す",
528
+ "onMenuClick 省略時はハンバーガー非表示"
463
529
  ]
464
530
  },
465
531
  {
466
- "name": "StickyActionBar",
467
- "path": "src/components/primitives/sticky-action-bar.tsx",
532
+ "name": "AdminSideNav",
533
+ "path": "src/components/primitives/admin-side-nav.tsx",
534
+ "category": "navigation",
468
535
  "rules": [
469
- "モバイル固定フッターCTA"
536
+ "管理画面向けサイドナビの汎用枠。PC は左端固定アイコンレール(ホバーでサブメニュー)、SP は制御ドロワー(サブメニューはアコーディオン)",
537
+ "項目データ(メニュー定義・文言)は全て items prop で受ける。特定事業のメニューを部品側に焼き込まない(Cake.jp 既定は AdminShell が持つ)",
538
+ "PC レールのラベルは max-w-12 + typo-label-xs + 中央揃えを本体が所有する。5文字超も語のまとまりで折り返せるため、consumer が data-slot セレクタで内部上書きしない",
539
+ "SP ドロワー開閉は open / onOpenChange で制御する(内部 state を持たない)",
540
+ "icon は ReactNode か描画コンテキスト(active / placement)で出し分ける関数を渡す"
470
541
  ]
471
542
  },
543
+ {
544
+ "name": "BannerCarousel",
545
+ "path": "src/components/primitives/banner-carousel.tsx"
546
+ },
472
547
  {
473
548
  "name": "BottomTabBar",
474
549
  "path": "src/components/primitives/bottom-tab-bar.tsx",
@@ -483,13 +558,42 @@
483
558
  "テーブル選択時の一括操作バー。div手書き禁止"
484
559
  ]
485
560
  },
561
+ {
562
+ "name": "CategoryScroll",
563
+ "path": "src/components/primitives/category-scroll.tsx"
564
+ },
486
565
  {
487
566
  "name": "CategoryShortcuts",
488
567
  "path": "src/components/primitives/category-shortcuts.tsx"
489
568
  },
490
569
  {
491
- "name": "CategoryScroll",
492
- "path": "src/components/primitives/category-scroll.tsx"
570
+ "name": "ChatComposer",
571
+ "path": "src/components/primitives/chat-composer.tsx",
572
+ "formBinding": "controller",
573
+ "variants": [
574
+ "submitKey:enter",
575
+ "submitKey:mod-enter"
576
+ ],
577
+ "features": [
578
+ "attachments",
579
+ "maxFiles",
580
+ "maxSizeMb",
581
+ "maxLength",
582
+ "isSubmitting"
583
+ ],
584
+ "accessibility": [
585
+ "入力欄はvisually-hiddenのlabel(label prop)とhtmlForで紐付ける",
586
+ "送信ボタンのaria-labelは日本語(送信 / 送信中)",
587
+ "添付削除はChipのremovableで操作でき、キーボードで到達できる"
588
+ ],
589
+ "rules": [
590
+ "チャット・問い合わせ・返信の入力欄をTextarea+Buttonで手組みしない",
591
+ "既定はEnter送信・Shift+Enter改行。長文が主なsubmitKey='mod-enter'を使う",
592
+ "IME変換中のEnterでは送信しない(日本語入力の誤送信防止)",
593
+ "添付は内蔵のFileInputで選択し、選択済みはChipのremovableで表示・削除する",
594
+ "送信中はisSubmittingを立てる(送信ボタン無効化で二重送信を防ぐ)",
595
+ "<form>を内包しないため、フォーム内に置いても入れ子にならない"
596
+ ]
493
597
  },
494
598
  {
495
599
  "name": "Chip",
@@ -507,9 +611,62 @@
507
611
  "Tagと違い押せる・削除できる"
508
612
  ]
509
613
  },
614
+ {
615
+ "name": "CoachMark",
616
+ "path": "src/components/primitives/coach-mark.tsx"
617
+ },
618
+ {
619
+ "name": "Combobox",
620
+ "path": "src/components/primitives/combobox.tsx",
621
+ "formBinding": "controller",
622
+ "props": [
623
+ "options: ComboboxOption[]",
624
+ "value?: string",
625
+ "onValueChange?: (value: string) => void",
626
+ "id?: string",
627
+ "aria-invalid?: boolean",
628
+ "aria-describedby?: string"
629
+ ],
630
+ "rules": [
631
+ "検索が必要な単一選択に使う。少数候補はSelectまたはRadioGroupを使う",
632
+ "Popover + Command の検索・キーボード操作を内包する",
633
+ "制御コンポーネント。value と onValueChange を必ず渡す(非制御では選択が反映されない)",
634
+ "FormField と繋ぐときは id を FieldLabel の htmlFor と揃え、エラーは aria-invalid / aria-describedby で FieldError に接続する"
635
+ ]
636
+ },
637
+ {
638
+ "name": "Container",
639
+ "path": "src/components/primitives/container.tsx",
640
+ "variants": [
641
+ "size: page | narrow | wide | fluid",
642
+ "gutter: default | tight | none"
643
+ ],
644
+ "rules": [
645
+ "ページ最大幅と左右paddingの唯一の担い手。これを使う親(Section等)は自身で左右paddingを持たない(二重がけ防止)",
646
+ "縦余白・背景色は持たない(Sectionの責務)"
647
+ ]
648
+ },
649
+ {
650
+ "name": "ContentCarousel",
651
+ "path": "src/components/primitives/content-carousel.tsx",
652
+ "features": [
653
+ "scroll-snap",
654
+ "dots",
655
+ "arrows",
656
+ "autoPlay",
657
+ "itemsPerView"
658
+ ],
659
+ "rules": [
660
+ "ReactNode スライド対応(slides: ReactNode[])。テキスト主体のヒーローバナー用",
661
+ "itemsPerView=2|3|4 で PC の複数カード表示に対応。SP は1枚表示を維持し、EC 商品以外の求人・記事カード等にも使う",
662
+ "画像だけのバナーは ImageCarousel を使う",
663
+ "矢印・ドットの意匠は ImageCarousel と同一"
664
+ ]
665
+ },
510
666
  {
511
667
  "name": "DatePicker",
512
668
  "path": "src/components/primitives/date-picker.tsx",
669
+ "formBinding": "controller",
513
670
  "subcomponents": [
514
671
  "DateTimePicker"
515
672
  ],
@@ -562,6 +719,8 @@
562
719
  {
563
720
  "name": "FormField",
564
721
  "path": "src/components/primitives/form-field.tsx",
722
+ "formBinding": "layout",
723
+ "exportedAs": "CakeFormField",
565
724
  "variants": {
566
725
  "descriptionPosition": [
567
726
  "above",
@@ -570,7 +729,40 @@
570
729
  },
571
730
  "rules": [
572
731
  "Label+Input+エラーメッセージをセットで管理",
573
- "グリッド横並び時は descriptionPosition='below' で高さを揃える"
732
+ "グリッド横並び時は descriptionPosition='below' で高さを揃える",
733
+ "npm 経由では CakeFormField として import する(foundations/form.tsx の FormField と名前が衝突するため別名で export)"
734
+ ]
735
+ },
736
+ {
737
+ "name": "FunnelShell",
738
+ "path": "src/components/primitives/funnel-shell.tsx",
739
+ "kind": "shell",
740
+ "variants": [],
741
+ "rules": [
742
+ "EC 購入導線等のファネル用集中モード(Full Page)のシェル。集中ヘッダー+進捗インジケータ(steps/activeStep)+ステップ本体+任意の bottomAction で構成。min-h-dvh flex flex-col の全高コンテナ",
743
+ "ヘッダーの基本形は中央ロゴ(logo prop → Header variant=logo-center)。ライブ本番の購入導線(cart_layout.ctp の cartHeader)準拠で離脱導線をロゴのみに絞る。狙いは戻り・離脱をしづらくして途中離脱を抑制すること(=Header の logo-center は決済・申込ページ用の既定バリアント、本番 cartHeader もロゴクリックが /cart/・thanks に限定)。title+back(Header variant=back)は多段申込等でタイトル・戻るが要る場合の代替形。logo と title はどちらか一方を渡す(logo 優先)",
744
+ "あくまで枠。ステップ定義(名前・数)・文言・商品データを焼き込まない。steps / activeStep は利用側(ドメイン)が渡す",
745
+ "グローバルナビ・BottomTabBar・パンくずを持たない(構造的に置けない)。離脱を誘う導線を出さず購入に集中させる",
746
+ "bottomAction(StickyActionBar 等)を渡すと本文の下余白はシェルが管理する。ページ側で pb を書かない(付け忘れ/二重がけの構造的防止)",
747
+ "左右 padding は持たない(Container の責務)。管理画面の複雑な集中編集は WorkspaceShell を使う(EC 購入導線=FunnelShell)",
748
+ "地がSurface-Secondaryなので、シェルが配下Cardのサーフェス文脈(--card-surface: Surface-Primary)を宣言する → Cardはsurface未指定のまま白で地から浮く(#150)。darkは据え置き",
749
+ "a11y: DOM 先頭に SkipLink を標準内蔵し <main id> を制御できる(mainId 既定 main-content)。sr-only で視覚無影響。skipLink={false} で無効化"
750
+ ]
751
+ },
752
+ {
753
+ "name": "Header",
754
+ "path": "src/components/primitives/header.tsx",
755
+ "variants": [
756
+ "logo",
757
+ "logo-center",
758
+ "back",
759
+ "back-search"
760
+ ],
761
+ "rules": [
762
+ "variant=\"logo\" の PC 行(@[768px]+)では searchSlot で navItems と actions の間に常設検索を inline 配置できる(flex-1 max-w-[400px] で伸縮)。navItems 無し+searchSlot だけでも PC 行を描画する",
763
+ "SP 行に searchSlot は出さない。SP の常設検索は従来どおり bottomSlot で運用する",
764
+ "bottomSlot はヘッダー下段の全幅スロット(ProductDetailBar 等)。PC 常設検索の置き場に使わない(PC 二段ヘッダーになる)",
765
+ "actions(HeaderAction)はカート・マイページ等の本来リンクである導線には href を指定する。href 指定時は Button asChild + <a> で描画され、中クリック/新規タブ・リンクコピーが効く。onClick と併用可(両方付与される)"
574
766
  ]
575
767
  },
576
768
  {
@@ -587,6 +779,52 @@
587
779
  "name": "ImageGallery",
588
780
  "path": "src/components/primitives/image-gallery.tsx"
589
781
  },
782
+ {
783
+ "name": "ImageInput",
784
+ "path": "src/components/primitives/image-input.tsx",
785
+ "formBinding": "controller",
786
+ "variants": [
787
+ "aspect:square",
788
+ "aspect:landscape",
789
+ "size:sm",
790
+ "size:md",
791
+ "size:lg",
792
+ "objectFit:cover",
793
+ "objectFit:contain"
794
+ ],
795
+ "features": [
796
+ "preview",
797
+ "accept",
798
+ "maxSizeMb",
799
+ "clear",
800
+ "formBinding"
801
+ ],
802
+ "props": [
803
+ "value: File | string | null — Fileは新規選択・stringは保存済み画像のURL(必須)",
804
+ "onChange: (file: File | null) => void — 検証を通ったファイルのみ渡る(必須)",
805
+ "onBlur?: () => void — フォーカスが部品外へ抜けたとき(React Hook Form の touched 連携)",
806
+ "id? / name? — 内包 input に付与。FormField の htmlFor と揃える",
807
+ "accept?: string — 既定 'image/png,image/jpeg'",
808
+ "maxSizeMb?: number — 既定 2",
809
+ "error?: string — 外部(RHF等)のエラー。内蔵検証のエラーより優先して表示",
810
+ "onValidationError?: (error) => void — 検証エラーをモーダル等で出したい場合のみ"
811
+ ],
812
+ "accessibility": [
813
+ "検証エラーはFieldError(role='alert')にインライン表示し、色だけで伝えない",
814
+ "内包inputにaria-invalid / aria-describedbyを付けてエラーと紐付ける",
815
+ "クリアボタンのaria-labelは日本語(画像を削除)",
816
+ "ファイル選択の口はFileInputを内包し、キーボード・SRで操作できる"
817
+ ],
818
+ "rules": [
819
+ "画像1枚の入力を<input type='file'>+<img>で手組みしない",
820
+ "value/onChangeの制御コンポーネント。保存済み画像はURL文字列をそのまま渡す",
821
+ "プレビューのobjectURLは内部でrevokeされる。consumer側でcreateObjectURLしない",
822
+ "検証エラーの既定はインライン表示。モーダルは強すぎるため既定にしない",
823
+ "表示サイズはaspect(square/landscape)×size(sm/md/lg)で指定し、任意のw-[Npx]を足さない",
824
+ "非同期検証(画像寸法チェック等)は持たない。onChange後にconsumer側で検証しerrorで戻す",
825
+ "汎用の添付はFileInput(foundations)、複数枚のグリッド管理はImageUploader(primitives)"
826
+ ]
827
+ },
590
828
  {
591
829
  "name": "ImageUploader",
592
830
  "path": "src/components/primitives/image-uploader.tsx",
@@ -594,6 +832,10 @@
594
832
  "upload",
595
833
  "delete",
596
834
  "grid"
835
+ ],
836
+ "rules": [
837
+ "複数枚の画像を並べて管理するグリッド。ファイル選択の口は持たず、追加はonAddでconsumerが実装する",
838
+ "画像1枚の入力(プレビュー+検証+値のバインド)はImageInputを使う"
597
839
  ]
598
840
  },
599
841
  {
@@ -612,8 +854,23 @@
612
854
  "path": "src/components/primitives/menu-drawer.tsx"
613
855
  },
614
856
  {
615
- "name": "SubNav",
616
- "path": "src/components/primitives/sub-nav.tsx"
857
+ "name": "MultiSelect",
858
+ "path": "src/components/primitives/multi-select.tsx",
859
+ "formBinding": "controller",
860
+ "props": [
861
+ "options: MultiSelectOption[]",
862
+ "value?: string[]",
863
+ "onValueChange?: (value: string[]) => void",
864
+ "id?: string",
865
+ "aria-invalid?: boolean",
866
+ "aria-describedby?: string"
867
+ ],
868
+ "rules": [
869
+ "3件以上を複数選択させる、または候補が多い場合に使う。少数候補はCheckboxGroupを使う",
870
+ "選択済みは削除可能なChipで表示する",
871
+ "制御コンポーネント。value と onValueChange を必ず渡す(非制御では選択が反映されない)",
872
+ "FormField と繋ぐときは id を FieldLabel の htmlFor と揃え、エラーは aria-invalid / aria-describedby で FieldError に接続する"
873
+ ]
617
874
  },
618
875
  {
619
876
  "name": "NotificationBadge",
@@ -647,17 +904,51 @@
647
904
  "path": "src/components/primitives/progress-steps.tsx"
648
905
  },
649
906
  {
650
- "name": "QuantitySelector",
651
- "path": "src/components/primitives/quantity-selector.tsx",
907
+ "name": "NumberInput",
908
+ "path": "src/components/primitives/number-input.tsx",
909
+ "formBinding": "controller",
910
+ "deprecatedAliases": [
911
+ "QuantitySelector"
912
+ ],
652
913
  "features": [
653
914
  "increment",
654
915
  "decrement",
655
- "trash-icon"
916
+ "trash-icon",
917
+ "number-format"
918
+ ],
919
+ "sizes": [
920
+ "sm",
921
+ "md"
922
+ ],
923
+ "props": [
924
+ "value?: number",
925
+ "defaultValue?: number",
926
+ "onChange?: (value: number) => void",
927
+ "min?: number",
928
+ "max?: number",
929
+ "step?: number",
930
+ "size?: sm | md",
931
+ "editable?: boolean",
932
+ "error?: boolean",
933
+ "showTrash?: boolean",
934
+ "onDelete?: () => void",
935
+ "formatOptions?: Intl.NumberFormatOptions",
936
+ "locale?: string",
937
+ "inputId?: string"
938
+ ],
939
+ "rules": [
940
+ "DSの数値入力はすべてこれを使う。生の input type=\"number\" は使わない(カート数量・価格・在庫・ポイント等すべて)",
941
+ "意匠は Figma DS の Adjust 準拠。sm=コンパクト型(カート内)/ md=サークル型(商品詳細・管理画面)。この2つ以外の見た目を新規に作らない",
942
+ "カート数量では min=1 が標準(0 にしない — 削除は showTrash + onDelete で行う)",
943
+ "既定は min=1 / max=99 とカート数量向け。価格・在庫など汎用の数値入力では min / max / step を必ず明示する",
944
+ "桁区切り・通貨表示は formatOptions を渡す(編集中は素の数値になる)。JPY の全角¥は半角¥に正規化される",
945
+ "旧名 QuantitySelector は用途名だったため機能名に改名した。旧名は deprecated alias として残るが新規利用は不可"
656
946
  ]
657
947
  },
658
948
  {
659
949
  "name": "SearchBar",
660
- "path": "src/components/primitives/search-bar.tsx"
950
+ "path": "src/components/primitives/search-bar.tsx",
951
+ "formBinding": "none"
661
952
  },
662
953
  {
663
954
  "name": "SearchPanel",
@@ -667,29 +958,23 @@
667
958
  "flex"
668
959
  ]
669
960
  },
670
- {
671
- "name": "Container",
672
- "path": "src/components/primitives/container.tsx",
673
- "variants": [
674
- "size: page | narrow | wide | fluid",
675
- "gutter: default | tight | none"
676
- ],
677
- "rules": [
678
- "ページ最大幅と左右paddingの唯一の担い手。これを使う親(Section等)は自身で左右paddingを持たない(二重がけ防止)",
679
- "縦余白・背景色は持たない(Sectionの責務)"
680
- ]
681
- },
682
961
  {
683
962
  "name": "Section",
684
963
  "path": "src/components/primitives/section.tsx",
685
964
  "variants": [
686
965
  "spacing: sm | md | lg | xl",
687
- "background: none | subtle | accent-subtle"
966
+ "background: none | subtle | accent-subtle",
967
+ "scrim: none | dark | light | bottom-dark"
688
968
  ],
689
969
  "rules": [
690
970
  "全幅の縦リズム+背景帯。左右paddingを持たない(左右余白はContainerが唯一の担い手)",
971
+ "背景画像上に文字を載せる場合は backgroundImage + scrim を使う。consumer が gradient overlay を ::before / ::after で手組みしない",
972
+ "scrim は Surface-Overlay-Light / Dark を使い、dark / light / bottom-dark の意匠を DS が所有する",
691
973
  "背景色はSection(全幅)が持ち中央寄せはContainerが持つ → 背景帯は画面端まで・コンテンツは中央寄せが二重がけなしで成立",
692
- "縦余白はSP/PCペアで --Space-Section-* トークンを消費(rem直書き撲滅)"
974
+ "縦余白はSP/PCペアで --Space-Section-* トークンを消費(rem直書き撲滅)",
975
+ "background='subtle'|'accent-subtle'(色地)は配下に --card-surface: var(--Surface-Primary) を流す → 配下のCardはsurface未指定でも白になる(グレー地にカードが沈む事故を構造で防ぐ)。伝播はCSSカスタムプロパティのみでReact context不使用=Server Componentのまま使える",
976
+ "background='none'は文脈をリセットしない。色地Sectionの内側に入れ子にしたnoneは背景が透過して外側の色地が見えるため、配下Cardも白のままが正しい",
977
+ "darkでは伝播値をSurface-Secondaryに据え置く(darkのSurface-Secondaryは半透明白=色地に重なって一段明るく浮くのでそもそも沈まない。Surface-Primaryを強制すると地より暗い穴になりelevationが反転する)"
693
978
  ]
694
979
  },
695
980
  {
@@ -697,42 +982,13 @@
697
982
  "path": "src/components/primitives/section-header.tsx",
698
983
  "variants": [
699
984
  "size: 3xl | 2xl | xl | lg | md | sm",
985
+ "desktopSize: 5xl | 4xl",
700
986
  "align: start | center"
701
987
  ],
702
988
  "rules": [
703
989
  "タイトル+もっと見るボタンをCardTitle+Button手書き禁止。SectionHeaderを使う",
704
- "中央寄せ大見出し(ランディング/ガイド系)は align=\"center\" を使う。eyebrow プロップに Badge 等を載せられる。手書きの中央寄せ見出し禁止"
705
- ]
706
- },
707
- {
708
- "name": "ShareButtons",
709
- "path": "src/components/primitives/share-buttons.tsx"
710
- },
711
- {
712
- "name": "SocialLoginButton",
713
- "path": "src/components/primitives/social-login-button.tsx"
714
- },
715
- {
716
- "name": "StatusCountTabs",
717
- "path": "src/components/primitives/status-count-tabs.tsx",
718
- "rules": [
719
- "ToggleGroup+Badge手書き禁止。Chip count propを使う"
720
- ]
721
- },
722
- {
723
- "name": "Tag",
724
- "path": "src/components/primitives/tag.tsx",
725
- "rules": [
726
- "表示専用。onClickを付けない(押せるものはChip)"
727
- ]
728
- },
729
- {
730
- "name": "SideNav",
731
- "path": "src/components/primitives/side-nav.tsx",
732
- "category": "navigation",
733
- "rules": [
734
- "ページ間遷移(別 URL へのナビゲーション)に使う。チェブロン付きで「別ページへ移動する」アフォーダンスを持つ",
735
- "ページ内アンカー移動(href=\"#...\")に流用しない → SectionNav を使う(見た目と挙動の不一致を防ぐ)"
990
+ "中央寄せ大見出し(ランディング/ガイド系)は align=\"center\" を使う。eyebrow プロップに Badge 等を載せられる。手書きの中央寄せ見出し禁止",
991
+ "3xl(28px)は通常ページの主見出しにも使う。4xl/5xlはLP・マーケティング系ヒーロー専用で、size=\"3xl\" に desktopSize=\"4xl\" または desktopSize=\"5xl\" を加えてPC時だけ拡大する"
736
992
  ]
737
993
  },
738
994
  {
@@ -754,10 +1010,30 @@
754
1010
  "IntersectionObserver は内包しない。activeKey は利用側が制御する(AnchorNav の新設は却下、本コンポーネントの variant で担う)"
755
1011
  ]
756
1012
  },
1013
+ {
1014
+ "name": "ShareButtons",
1015
+ "path": "src/components/primitives/share-buttons.tsx"
1016
+ },
1017
+ {
1018
+ "name": "SideNav",
1019
+ "path": "src/components/primitives/side-nav.tsx",
1020
+ "category": "navigation",
1021
+ "rules": [
1022
+ "ページ間遷移(別 URL へのナビゲーション)に使う。チェブロン付きで「別ページへ移動する」アフォーダンスを持つ",
1023
+ "ページ内アンカー移動(href=\"#...\")に流用しない → SectionNav を使う(見た目と挙動の不一致を防ぐ)"
1024
+ ]
1025
+ },
1026
+ {
1027
+ "name": "SocialLoginButton",
1028
+ "path": "src/components/primitives/social-login-button.tsx"
1029
+ },
757
1030
  {
758
1031
  "name": "StandardPageShell",
759
1032
  "path": "src/components/primitives/standard-page-shell.tsx",
760
1033
  "kind": "shell",
1034
+ "deprecatedAliases": [
1035
+ "EcPageShell"
1036
+ ],
761
1037
  "variants": [],
762
1038
  "rules": [
763
1039
  "消費者向け標準ページ(Standard Page(View / Edit))の骨格。composition.json pageSkeletons(mobile / desktop)の実装体。min-h-dvh flex flex-col の全高コンテナに header / main / bottomTab / footer を積む",
@@ -770,72 +1046,65 @@
770
1046
  ]
771
1047
  },
772
1048
  {
773
- "name": "WorkspaceShell",
774
- "path": "src/components/primitives/workspace-shell.tsx",
775
- "kind": "shell",
776
- "variants": [
777
- "headerLayout=start",
778
- "headerLayout=center"
779
- ],
1049
+ "name": "StatusCountTabs",
1050
+ "path": "src/components/primitives/status-count-tabs.tsx",
780
1051
  "rules": [
781
- "Focused Workspace(全画面集中編集)のシェル。編集ヘッダー(Header variant=back)+ 任意のセクションナビ + 編集本体で構成",
782
- "headerLayout(既定 start): centerでヘッダーを左=戻る/中央=タイトル/右=primaryActionの3カラムgridに固定し、タイトルを真の中央に揃える(#112)。consumer 側の data-slot セレクタ上書きは不要",
783
- "グローバルナビ・パンくずを持たない(構造的に置けない)。集中を妨げる導線は出さない",
784
- "primaryActionはワークスペース全体の上位アクション(公開申請・承認依頼等)がある場合のみ。セクション/行単位の操作はコンテンツ内に置く",
785
- "標準の編集はAdminShellのStandard Page(Edit)(旧Form Page)。複雑・複数情報をまたぐ集中編集・没入執筆のみWorkspaceShellを使う",
786
- "sectionNavは編集内ナビ(アンカー/ステップ)のみ。PCでサイド表示・SPで非表示",
787
- "a11y: DOM 先頭に SkipLink を標準内蔵し <main id> を制御できる(mainId 既定 main-content)。sr-only で視覚無影響。skipLink={false} で無効化"
1052
+ "ToggleGroup+Badge手書き禁止。Chip count propを使う",
1053
+ "activeIndex は初期値だけでなく、マウント後の外部変更にも表示が追従する"
788
1054
  ]
789
1055
  },
790
1056
  {
791
- "name": "FunnelShell",
792
- "path": "src/components/primitives/funnel-shell.tsx",
793
- "kind": "shell",
794
- "variants": [],
1057
+ "name": "StickyActionBar",
1058
+ "path": "src/components/primitives/sticky-action-bar.tsx",
795
1059
  "rules": [
796
- "EC 購入導線等のファネル用集中モード(Full Page)のシェル。集中ヘッダー+進捗インジケータ(steps/activeStep)+ステップ本体+任意の bottomAction で構成。min-h-dvh flex flex-col の全高コンテナ",
797
- "ヘッダーの基本形は中央ロゴ(logo prop → Header variant=logo-center)。ライブ本番の購入導線(cart_layout.ctp の cartHeader)準拠で離脱導線をロゴのみに絞る。狙いは戻り・離脱をしづらくして途中離脱を抑制すること(=Header の logo-center は決済・申込ページ用の既定バリアント、本番 cartHeader もロゴクリックが /cart/・thanks に限定)。title+back(Header variant=back)は多段申込等でタイトル・戻るが要る場合の代替形。logo と title はどちらか一方を渡す(logo 優先)",
798
- "あくまで枠。ステップ定義(名前・数)・文言・商品データを焼き込まない。steps / activeStep は利用側(ドメイン)が渡す",
799
- "グローバルナビ・BottomTabBar・パンくずを持たない(構造的に置けない)。離脱を誘う導線を出さず購入に集中させる",
800
- "bottomAction(StickyActionBar 等)を渡すと本文の下余白はシェルが管理する。ページ側で pb を書かない(付け忘れ/二重がけの構造的防止)",
801
- "左右 padding は持たない(Container の責務)。管理画面の複雑な集中編集は WorkspaceShell を使う(EC 購入導線=FunnelShell)",
802
- "a11y: DOM 先頭に SkipLink を標準内蔵し <main id> を制御できる(mainId 既定 main-content)。sr-only で視覚無影響。skipLink={false} で無効化"
1060
+ "モバイル固定フッターCTA"
803
1061
  ]
804
1062
  },
805
1063
  {
806
- "name": "Header",
807
- "path": "src/components/primitives/header.tsx",
808
- "variants": [
809
- "logo",
810
- "logo-center",
811
- "back",
812
- "back-search"
813
- ],
1064
+ "name": "SubNav",
1065
+ "path": "src/components/primitives/sub-nav.tsx"
1066
+ },
1067
+ {
1068
+ "name": "Tag",
1069
+ "path": "src/components/primitives/tag.tsx",
814
1070
  "rules": [
815
- "variant=\"logo\" の PC 行(@[768px]+)では searchSlot で navItems と actions の間に常設検索を inline 配置できる(flex-1 max-w-[400px] で伸縮)。navItems 無し+searchSlot だけでも PC 行を描画する",
816
- "SP 行に searchSlot は出さない。SP の常設検索は従来どおり bottomSlot で運用する",
817
- "bottomSlot はヘッダー下段の全幅スロット(ProductDetailBar 等)。PC 常設検索の置き場に使わない(PC 二段ヘッダーになる)",
818
- "actions(HeaderAction)はカート・マイページ等の本来リンクである導線には href を指定する。href 指定時は Button asChild + <a> で描画され、中クリック/新規タブ・リンクコピーが効く。onClick と併用可(両方付与される)"
1071
+ "表示専用。onClickを付けない(押せるものはChip)"
819
1072
  ]
820
1073
  },
821
1074
  {
822
- "name": "AdminSideNav",
823
- "path": "src/components/primitives/admin-side-nav.tsx",
824
- "category": "navigation",
1075
+ "name": "Timeline",
1076
+ "path": "src/components/primitives/timeline.tsx",
1077
+ "features": [
1078
+ "vertical",
1079
+ "custom-marker",
1080
+ "connector"
1081
+ ],
825
1082
  "rules": [
826
- "管理画面向けサイドナビの汎用枠。PC は左端固定アイコンレール(ホバーでサブメニュー)、SP は制御ドロワー(サブメニューはアコーディオン)",
827
- "項目データ(メニュー定義・文言)は全て items prop で受ける。特定事業のメニューを部品側に焼き込まない(Cake.jp 既定は AdminShell が持つ)",
828
- "SP ドロワー開閉は open / onOpenChange で制御する(内部 state を持たない)",
829
- "icon は ReactNode か描画コンテキスト(active / placement)で出し分ける関数を渡す"
1083
+ "本文付きの縦タイムラインに使う。現在位置を示す横型インジケータは ProgressSteps を使う",
1084
+ "marker 省略時は連番。IconBadge 等の ReactNode を渡せる",
1085
+ "コネクター線は Timeline が所有するため consumer が ::before / ::after で手組みしない"
830
1086
  ]
831
1087
  },
832
1088
  {
833
- "name": "AdminHeader",
834
- "path": "src/components/primitives/admin-header.tsx",
1089
+ "name": "WorkspaceShell",
1090
+ "path": "src/components/primitives/workspace-shell.tsx",
1091
+ "kind": "shell",
1092
+ "deprecatedAliases": [
1093
+ "WorkspaceLayout"
1094
+ ],
1095
+ "variants": [
1096
+ "headerLayout=start",
1097
+ "headerLayout=center"
1098
+ ],
835
1099
  "rules": [
836
- "管理画面向けの上端固定ヘッダーの汎用枠。左に SP ハンバーガー+brand、右に actions slot",
837
- "文言合成(『{shopName}管理』等)はしない。brand / actions を利用側が組み立てて渡す",
838
- "onMenuClick 省略時はハンバーガー非表示"
1100
+ "Focused Workspace(全画面集中編集)のシェル。編集ヘッダー(Header variant=back)+ 任意のセクションナビ + 編集本体で構成",
1101
+ "headerLayout(既定 start): centerでヘッダーを左=戻る/中央=タイトル/右=primaryActionの3カラムgridに固定し、タイトルを真の中央に揃える(#112)。consumer 側の data-slot セレクタ上書きは不要",
1102
+ "グローバルナビ・パンくずを持たない(構造的に置けない)。集中を妨げる導線は出さない",
1103
+ "primaryActionはワークスペース全体の上位アクション(公開申請・承認依頼等)がある場合のみ。セクション/行単位の操作はコンテンツ内に置く",
1104
+ "標準の編集はAdminShellのStandard Page(Edit)(旧Form Page)。複雑・複数情報をまたぐ集中編集・没入執筆のみWorkspaceShellを使う",
1105
+ "sectionNavは編集内ナビ(アンカー/ステップ)のみ。PCでサイド表示・SPで非表示",
1106
+ "地がSurface-Secondaryなので、シェルが配下Cardのサーフェス文脈(--card-surface: Surface-Primary)を宣言する → Cardはsurface未指定のまま白で地から浮く(#150)。darkは据え置き",
1107
+ "a11y: DOM 先頭に SkipLink を標準内蔵し <main id> を制御できる(mainId 既定 main-content)。sr-only で視覚無影響。skipLink={false} で無効化"
839
1108
  ]
840
1109
  }
841
1110
  ],
@@ -844,36 +1113,23 @@
844
1113
  "name": "AdminShell",
845
1114
  "path": "src/components/ec/admin-shell.tsx",
846
1115
  "kind": "shell",
1116
+ "deprecatedAliases": [
1117
+ "AdminLayout"
1118
+ ],
1119
+ "subcomponents": [
1120
+ "MissingComponent"
1121
+ ],
847
1122
  "rules": [
848
1123
  "管理画面のレイアウトシェル。枠は primitives の AdminSideNav / AdminHeader を組み込む",
849
1124
  "Cake.jp 管理メニュー8項目+サブメニュー・通知ベル・プロフィール・『{shopName}管理』を既定値として同梱する(ec 層)",
850
1125
  "navItems で既定メニューを差し替え可、headerActions でヘッダー右アクションを差し替え可",
1126
+ "⚠️ PC の左サイドナビは 72px のアイコンレール(アイコン+短ラベル、サブメニューは hover ポップアップ)。バッジ+見出し+説明文のようなブランドブロックをレール上部に置く領域は無い。幅広サイドバー前提の画面はこの骨格では置き換えられない",
1127
+ "他ブランドで使う場合、ヘッダー左のブランド表示は brand(ReactNode。既定=『{shopName}管理』の文字列)で差し替える。SP ドロワー上部の見出しは sidebarHeader(ReactNode。既定=shopName)で差し替える",
851
1128
  "戻る導線(onBack)はパンくずの上に置く。onBack を渡すとシェルが main 内・パンくずより上に戻るボタンを描画する(backLabel で文言変更・既定『戻る』)。子コンテンツ内に戻るリンクを手書きしない",
852
1129
  "タイトル直下の上位アクション(AI商品作成・PDF出力等)は h1 と同じ左端に左寄せする。右端フロートにしない",
853
1130
  "既定値は props で差し替え可能で、骨格(Header + SideNav + Main)自体は他事業(ジョブボードの管理画面等)でも再利用できる",
854
- "a11y: DOM 先頭に SkipLink を標準内蔵し <main id> を制御できる(mainId 既定 main-content)。sr-only で視覚無影響。skipLink={false} で無効化"
855
- ]
856
- },
857
- {
858
- "name": "MypageShell",
859
- "path": "src/components/ec/mypage-shell.tsx",
860
- "kind": "shell",
861
- "variants": [],
862
- "features": [
863
- "sp-header-back",
864
- "pc-logo-header",
865
- "pc-breadcrumb",
866
- "pc-2column-sidenav",
867
- "sp-bottom-tab-bar"
868
- ],
869
- "rules": [
870
- "マイページ配下の固有ページ(注文履歴 / ポイント / クーポン / 会員情報 / お届け先 / ブックマーク / 口コミ / 記念日 等)はこの shell で組む",
871
- "本番の mypage_layout.ctp + menu.ctp 準拠。SP は Header(variant=back)+h1+コンテンツ+BottomTabBar、PC は Header(variant=logo)+Breadcrumb+2カラム(SideNav 256px + main)",
872
- "サイドナビは既定で ec/_mypage-menu-data の mypageMenuGroups を使用。activeMenu で現在ページを強調する",
873
- "既定値は props で差し替え可能(menuGroups / breadcrumbs / bottomNavItems / headerActions)で、骨格(ヘッダー+2カラム+BottomTabBar)自体は他事業(ジョブボードのマイページ等)でも再利用できる",
874
- "breadcrumbs 省略時は「ホーム > {ユーザー}さまのマイページ > {title}」を自動生成する",
875
- "マイページトップ(Dashboard)はメインカラムを白背景で持たずセクションごとにカード化する別パターンなので、この shell を使わない",
876
- "a11y: DOM 先頭に SkipLink を標準内蔵し <main id> を制御できる(mainId 既定 main-content)。sr-only で視覚無影響。skipLink={false} で無効化"
1131
+ "a11y: DOM 先頭に SkipLink を標準内蔵し <main id> を制御できる(mainId 既定 main-content)。sr-only で視覚無影響。skipLink={false} で無効化",
1132
+ "地がSurface-Secondaryなので、シェルが配下Cardのサーフェス文脈(--card-surface: Surface-Primary)を宣言する → Cardはsurface未指定のまま白で地から浮く(#150)。darkは据え置き"
877
1133
  ]
878
1134
  },
879
1135
  {
@@ -909,6 +1165,28 @@
909
1165
  "name": "DeliveryCalendar",
910
1166
  "path": "src/components/ec/delivery-calendar.tsx"
911
1167
  },
1168
+ {
1169
+ "name": "MypageShell",
1170
+ "path": "src/components/ec/mypage-shell.tsx",
1171
+ "kind": "shell",
1172
+ "variants": [],
1173
+ "features": [
1174
+ "sp-header-back",
1175
+ "pc-logo-header",
1176
+ "pc-breadcrumb",
1177
+ "pc-2column-sidenav",
1178
+ "sp-bottom-tab-bar"
1179
+ ],
1180
+ "rules": [
1181
+ "マイページ配下の固有ページ(注文履歴 / ポイント / クーポン / 会員情報 / お届け先 / ブックマーク / 口コミ / 記念日 等)はこの shell で組む",
1182
+ "本番の mypage_layout.ctp + menu.ctp 準拠。SP は Header(variant=back)+h1+コンテンツ+BottomTabBar、PC は Header(variant=logo)+Breadcrumb+2カラム(SideNav 256px + main)",
1183
+ "サイドナビは既定で ec/_mypage-menu-data の mypageMenuGroups を使用。activeMenu で現在ページを強調する",
1184
+ "既定値は props で差し替え可能(menuGroups / breadcrumbs / bottomNavItems / headerActions)で、骨格(ヘッダー+2カラム+BottomTabBar)自体は他事業(ジョブボードのマイページ等)でも再利用できる",
1185
+ "breadcrumbs 省略時は「ホーム > {ユーザー}さまのマイページ > {title}」を自動生成する",
1186
+ "マイページトップ(Dashboard)はメインカラムを白背景で持たずセクションごとにカード化する別パターンなので、この shell を使わない",
1187
+ "a11y: DOM 先頭に SkipLink を標準内蔵し <main id> を制御できる(mainId 既定 main-content)。sr-only で視覚無影響。skipLink={false} で無効化"
1188
+ ]
1189
+ },
912
1190
  {
913
1191
  "name": "OrderSection",
914
1192
  "path": "src/components/ec/order-section.tsx",
@@ -923,8 +1201,11 @@
923
1201
  "path": "src/components/ec/order-summary.tsx"
924
1202
  },
925
1203
  {
926
- "name": "PaymentMethod",
927
- "path": "src/components/ec/payment-method.tsx"
1204
+ "name": "PaymentMethodDisplay",
1205
+ "path": "src/components/ec/payment-method.tsx",
1206
+ "subcomponents": [
1207
+ "CreditCardIcon"
1208
+ ]
928
1209
  },
929
1210
  {
930
1211
  "name": "ProductCard",
@@ -967,9 +1248,26 @@
967
1248
  "name": "ProductDetailSidebar",
968
1249
  "path": "src/components/ec/product-detail-sidebar.tsx"
969
1250
  },
1251
+ {
1252
+ "name": "ProductListItem",
1253
+ "path": "src/components/ec/product-list-item.tsx",
1254
+ "category": "display",
1255
+ "rules": [
1256
+ "カート/注文履歴/レビュー variant を持つ商品リスト行",
1257
+ "captionは規格・仕様の短い自由記述(例: \"1kg / 冷蔵\")。価格表示の直下にtypo-body-xs / Text-Medium-Emphasisで表示。tags(配送方法等のenum variant)とは別物",
1258
+ "subtotal/subtotalLabel(既定\"小計\")はcart variant専用。数量セレクタ行の右側(収まらなければ下に折り返し)に¥{カンマ区切り}で表示。BtoB/法人ECのロット購入等、商品ごとの単価×数量の内訳を明示したい場合に使う"
1259
+ ]
1260
+ },
970
1261
  {
971
1262
  "name": "ProductOption",
972
1263
  "path": "src/components/ec/product-option.tsx",
1264
+ "formBinding": "layout",
1265
+ "subcomponents": [
1266
+ "OptionQuantity",
1267
+ "OptionSelect",
1268
+ "OptionText",
1269
+ "OptionImageUpload"
1270
+ ],
973
1271
  "variants": [
974
1272
  "quantity",
975
1273
  "select",
@@ -983,6 +1281,9 @@
983
1281
  {
984
1282
  "name": "RatingStars",
985
1283
  "path": "src/components/ec/rating-stars.tsx",
1284
+ "subcomponents": [
1285
+ "SparkleIcon"
1286
+ ],
986
1287
  "features": [
987
1288
  "sparkle-icon",
988
1289
  "review-count"
@@ -1004,6 +1305,7 @@
1004
1305
  {
1005
1306
  "name": "ReviewForm",
1006
1307
  "path": "src/components/ec/review-form.tsx",
1308
+ "formBinding": "none",
1007
1309
  "features": [
1008
1310
  "rating",
1009
1311
  "text",
@@ -1013,6 +1315,9 @@
1013
1315
  {
1014
1316
  "name": "ReviewSummary",
1015
1317
  "path": "src/components/ec/review-summary.tsx",
1318
+ "subcomponents": [
1319
+ "ReviewItem"
1320
+ ],
1016
1321
  "features": [
1017
1322
  "average-score",
1018
1323
  "star-gauge"
@@ -1030,16 +1335,6 @@
1030
1335
  {
1031
1336
  "name": "StatCard",
1032
1337
  "path": "src/components/ec/stat-card.tsx"
1033
- },
1034
- {
1035
- "name": "ProductListItem",
1036
- "path": "src/components/ec/product-list-item.tsx",
1037
- "category": "display",
1038
- "rules": [
1039
- "カート/注文履歴/レビュー variant を持つ商品リスト行",
1040
- "captionは規格・仕様の短い自由記述(例: \"1kg / 冷蔵\")。価格表示の直下にtypo-body-xs / Text-Medium-Emphasisで表示。tags(配送方法等のenum variant)とは別物",
1041
- "subtotal/subtotalLabel(既定\"小計\")はcart variant専用。数量セレクタ行の右側(収まらなければ下に折り返し)に¥{カンマ区切り}で表示。BtoB/法人ECのロット購入等、商品ごとの単価×数量の内訳を明示したい場合に使う"
1042
- ]
1043
1338
  }
1044
1339
  ]
1045
1340
  }