@cakejp/design-system 0.17.1 → 0.20.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 (101) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/CLAUDE.md +16 -0
  3. package/bin/init.js +191 -28
  4. package/contracts/components.json +176 -17
  5. package/contracts/composition.json +1 -1
  6. package/contracts/rules.json +5 -1
  7. package/contracts/screen-patterns.json +1 -1
  8. package/dist/components/ec/admin-shell.js +7 -6
  9. package/dist/components/ec/order-summary.js +16 -15
  10. package/dist/components/ec/product-card.js +107 -102
  11. package/dist/components/ec/product-option.js +1 -0
  12. package/dist/components/ec/review-card.js +22 -20
  13. package/dist/components/ec/review-form.js +1 -0
  14. package/dist/components/foundations/accordion.js +23 -22
  15. package/dist/components/foundations/alert-dialog.js +7 -7
  16. package/dist/components/foundations/alert.js +1 -0
  17. package/dist/components/foundations/aspect-ratio.js +3 -2
  18. package/dist/components/foundations/avatar.js +1 -0
  19. package/dist/components/foundations/button.js +7 -1
  20. package/dist/components/foundations/calendar.js +1 -0
  21. package/dist/components/foundations/checkbox-group.js +1 -0
  22. package/dist/components/foundations/checkbox.js +1 -0
  23. package/dist/components/foundations/command.js +1 -0
  24. package/dist/components/foundations/context-menu.js +1 -0
  25. package/dist/components/foundations/data-table.js +489 -16
  26. package/dist/components/foundations/dialog.js +13 -13
  27. package/dist/components/foundations/drawer.js +6 -5
  28. package/dist/components/foundations/file-input.js +1 -0
  29. package/dist/components/foundations/form.js +26 -25
  30. package/dist/components/foundations/pagination.js +49 -46
  31. package/dist/components/foundations/popover.js +9 -8
  32. package/dist/components/foundations/radio-group.js +3 -2
  33. package/dist/components/foundations/responsive-dialog.js +1 -0
  34. package/dist/components/foundations/separator.js +7 -6
  35. package/dist/components/foundations/sheet.js +41 -34
  36. package/dist/components/foundations/slider.js +7 -6
  37. package/dist/components/foundations/social-icon.js +30 -10
  38. package/dist/components/foundations/sonner.js +5 -4
  39. package/dist/components/foundations/tabs.js +45 -25
  40. package/dist/components/foundations/toggle-group.js +6 -5
  41. package/dist/components/primitives/admin-header.js +1 -1
  42. package/dist/components/primitives/admin-side-nav.js +196 -86
  43. package/dist/components/primitives/bottom-tab-bar.js +20 -20
  44. package/dist/components/primitives/chat-composer.js +1 -0
  45. package/dist/components/primitives/combobox.js +1 -0
  46. package/dist/components/primitives/content-card.js +111 -0
  47. package/dist/components/primitives/content-carousel.js +9 -8
  48. package/dist/components/primitives/date-picker.js +9 -8
  49. package/dist/components/primitives/footer.js +169 -100
  50. package/dist/components/primitives/form-field.js +1 -0
  51. package/dist/components/primitives/header.js +226 -176
  52. package/dist/components/primitives/image-carousel.js +8 -7
  53. package/dist/components/primitives/image-gallery.js +15 -14
  54. package/dist/components/primitives/image-input.js +9 -8
  55. package/dist/components/primitives/list-item.js +9 -8
  56. package/dist/components/primitives/menu-drawer.js +60 -56
  57. package/dist/components/primitives/multi-select.js +7 -6
  58. package/dist/components/primitives/number-input.js +1 -0
  59. package/dist/components/primitives/prose.js +52 -0
  60. package/dist/components/primitives/search-bar.js +81 -66
  61. package/dist/components/primitives/section-header.js +58 -55
  62. package/dist/components/primitives/share-buttons.js +93 -85
  63. package/dist/components/primitives/status-count-tabs.js +8 -7
  64. package/dist/components/primitives/sticky-action-bar.js +2 -1
  65. package/dist/components/primitives/sub-nav.js +52 -52
  66. package/dist/index.css +1 -1
  67. package/dist/index.js +2027 -752
  68. package/dist/lib/field-labelling.js +7 -6
  69. package/dist/lib/link-component.js +6 -0
  70. package/dist/lib/use-value-length.js +1 -0
  71. package/dist/types/components/ec/product-card.d.ts +12 -19
  72. package/dist/types/components/ec/review-card.d.ts +6 -1
  73. package/dist/types/components/foundations/button.d.ts +1 -1
  74. package/dist/types/components/foundations/chip-variants.d.ts +1 -1
  75. package/dist/types/components/foundations/data-table.d.ts +9 -1
  76. package/dist/types/components/foundations/pagination.d.ts +7 -1
  77. package/dist/types/components/foundations/sheet.d.ts +6 -1
  78. package/dist/types/components/foundations/social-icon.d.ts +1 -1
  79. package/dist/types/components/foundations/tabs.d.ts +2 -1
  80. package/dist/types/components/primitives/admin-side-nav.d.ts +3 -0
  81. package/dist/types/components/primitives/content-card.d.ts +67 -0
  82. package/dist/types/components/primitives/footer.d.ts +21 -1
  83. package/dist/types/components/primitives/header.d.ts +30 -2
  84. package/dist/types/components/primitives/menu-drawer.d.ts +7 -1
  85. package/dist/types/components/primitives/prose.d.ts +35 -0
  86. package/dist/types/components/primitives/search-bar.d.ts +14 -1
  87. package/dist/types/components/primitives/section-header.d.ts +2 -1
  88. package/dist/types/components/primitives/share-buttons.d.ts +26 -4
  89. package/dist/types/components/primitives/sub-nav.d.ts +16 -1
  90. package/dist/types/index.d.ts +7 -2
  91. package/dist/types/lib/link-component.d.ts +35 -0
  92. package/package.json +5 -4
  93. package/src/components/COMPONENT_LOOKUP.md +8 -6
  94. package/src/preset-standalone.css +3 -0
  95. package/src/preset.css +21 -3
  96. package/src/styles/semantic.css +11 -0
  97. package/templates/AGENTS.md +4 -2
  98. package/templates/AGENTS.stub.md +25 -0
  99. package/templates/CLAUDE.md +4 -2
  100. package/templates/CLAUDE.stub.md +25 -0
  101. package/tokens.json +17 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "meta": {
3
3
  "name": "Cake.jp Design System — Component Contracts",
4
- "version": "0.17.1",
4
+ "version": "0.20.0",
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 に旧名を残す。",
@@ -9,9 +9,9 @@
9
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)。",
10
10
  "counts": {
11
11
  "foundations": 51,
12
- "primitives": 49,
12
+ "primitives": 51,
13
13
  "ec": 24,
14
- "total": 124,
14
+ "total": 126,
15
15
  "note": "counts は各ティアの実装ファイル数(scripts/check-drift.sh が src/components/<tier>/*.tsx と突き合わせる)。subcomponents / deprecatedAliases は含まない。"
16
16
  }
17
17
  },
@@ -117,7 +117,12 @@
117
117
  "default",
118
118
  "lg",
119
119
  "xl",
120
- "hero"
120
+ "hero",
121
+ "icon-xs",
122
+ "icon-sm",
123
+ "icon",
124
+ "icon-lg",
125
+ "icon-xl"
121
126
  ],
122
127
  "accessibility": [
123
128
  "aria-label必須(アイコンのみの場合)"
@@ -125,6 +130,7 @@
125
130
  "rules": [
126
131
  "<button>を直接使わない。必ずButtonコンポーネントを使う",
127
132
  "高さは size='default'=44px(h-11) / xs=24px / sm=32px / lg=48px / xl=56px / hero=min 56px。default はフォーム入力コントロール(Input / SelectTrigger default / DatePicker trigger)と同じ 44px なので、入力行にボタンを並置しても揃う",
133
+ "アイコンのみは icon-xs=24px / icon-sm=32px / icon=36px / icon-lg=40px / icon-xl=44px。入力コントロール(Input / SearchBar / SelectTrigger)の横に並べるときは icon-xl を使う(他は高さが揃わず、44px のタップ領域も満たさない)",
128
134
  "⚠️ variant='secondary' はブランドアクセント意匠(Surface-Accent-Primary-Light地+Brand-Cake-Orange文字)。Material/Bootstrap/shadcn の secondary(グレー・控えめ)とは別物なので、他DSの感覚で選ばない",
129
135
  "「戻る」「キャンセル」「閉じる」「スキップ」「あとで」等の後退・中立アクションは variant='tertiary'(Surface-Tertiary地+Text-High-Emphasis文字のニュートラルなグレー)を使う。主CTAの隣に置く2番目のボタンでも、役割が後退なら secondary ではなく tertiary",
130
136
  "variant='secondary' を使うのは、主CTAと別方向の“前進”を促す副CTAのとき(例: 主='購入する' に対し 副='サンプルを見る')",
@@ -208,9 +214,35 @@
208
214
  "name": "DataTable",
209
215
  "path": "src/components/foundations/data-table.tsx",
210
216
  "formBinding": "none",
217
+ "subcomponents": [
218
+ "DataTableTable",
219
+ "DataTableHeader",
220
+ "DataTableBody",
221
+ "DataTableRow",
222
+ "DataTableHead",
223
+ "DataTableCell",
224
+ "DataTableAvatarCell",
225
+ "DataTableImageCell",
226
+ "DataTableLinkCell",
227
+ "DataTableCheckboxCell",
228
+ "DataTableActionCell",
229
+ "DataTableInputCell",
230
+ "DataTableSelectCell",
231
+ "DataTableNumberCell",
232
+ "DataTableDragHandleCell",
233
+ "DataTableBulkActions",
234
+ "DataTableSectionRow",
235
+ "DataTableAddRow",
236
+ "DataTableEmptyState"
237
+ ],
211
238
  "rules": [
212
239
  "管理画面のテーブルはui/tableでなくこちらを使う。ソート・選択行・アクションメニュー対応",
213
- "ActionCellのメニュー操作は行のonClickへ伝播しない(本体でstopPropagation済み)。行クリック選択との併用時も利用側ガードは不要"
240
+ "ActionCellのメニュー操作は行のonClickへ伝播しない(本体でstopPropagation済み)。行クリック選択との併用時も利用側ガードは不要",
241
+ "見出し文字を出さない列(選択・操作・並び替えハンドル)は DataTableHead の srLabel で読み上げ用ラベルを付ける。空の <th> は列の意味が支援技術に伝わらない(axe: empty-table-header)"
242
+ ],
243
+ "accessibility": [
244
+ "sortable な列の押下は <button> が担い、Tab / Enter / Space で操作できる。並び替え状態は <th aria-sort>(none / ascending / descending)で伝わる。th に onClick を直接書かない",
245
+ "方向アイコンは装飾なので aria-hidden。列名は button の中に置き、読み上げ名にする"
214
246
  ]
215
247
  },
216
248
  {
@@ -344,7 +376,11 @@
344
376
  },
345
377
  {
346
378
  "name": "Pagination",
347
- "path": "src/components/foundations/pagination.tsx"
379
+ "path": "src/components/foundations/pagination.tsx",
380
+ "rules": [
381
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。この部品の項目に external フラグは無く、渡した href はすべて linkComponent で描かれる(外部 URL を出し分けたい場合は consumer 側でラップする)(#236)",
382
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
383
+ ]
348
384
  },
349
385
  {
350
386
  "name": "Popover",
@@ -420,6 +456,15 @@
420
456
  "SheetDescription",
421
457
  "SheetFooter",
422
458
  "SheetClose"
459
+ ],
460
+ "props": [
461
+ "SheetContent: side?: top | right | bottom | left",
462
+ "SheetContent: showCloseButton?: boolean",
463
+ "SheetContent: autoFocus?: boolean"
464
+ ],
465
+ "accessibility": [
466
+ "SheetTitle は必須(ダイアログの読み上げ名)",
467
+ "フォーカスは既定で自動移動する(開く=Sheet の中へ / 閉じる=トリガーへ戻る)。WCAG 2.4.3。autoFocus={false} は止める必要がある特別な場合だけ使う(issue #233)"
423
468
  ]
424
469
  },
425
470
  {
@@ -460,7 +505,7 @@
460
505
  "name": "SocialIcon",
461
506
  "path": "src/components/foundations/social-icon.tsx",
462
507
  "props": [
463
- "platform",
508
+ "platform: line | x | facebook | hatena | amazon | google | apple",
464
509
  "tone?: brand | mono",
465
510
  "size?: number"
466
511
  ],
@@ -497,6 +542,15 @@
497
542
  "TabsList",
498
543
  "TabsTrigger",
499
544
  "TabsContent"
545
+ ],
546
+ "variants": [
547
+ "TabsList variant: underbar | segment",
548
+ "TabsList scrollable: boolean",
549
+ "TabsList size: md | sm"
550
+ ],
551
+ "rules": [
552
+ "サイドバー等の狭い場所に segment を置くときは TabsList size=\"sm\" を使う。既定は 1 項目 100px の最小幅を持ち、3 つ並べると 266px 幅ではみ出す(issue #235)",
553
+ "underbar のトリガーは左右 8px の padding を持つ。className=\"px-2\" を利用側で足さない"
500
554
  ]
501
555
  },
502
556
  {
@@ -565,7 +619,9 @@
565
619
  "サイドバーは 1 枚のパネルとして読ませる。ヘッダー行に border-bottom、フッター(ユーザーブロック・ヘルプ導線等)に border-top を引かない。内部を横線で区切ると分割されて見えるため、分離は余白で担う",
566
620
  "画面に見える線は①サイドバー右端の縦線②コンテンツヘッダー下の横線の 2 本に絞る。サイドバー内で横線を引くと 2 本が十字に交差して見える(正しくは T 字で突き当たる)",
567
621
  "折りたたみ(開閉)を持たせる場合、トリガーはサイドバー内ヘッダー行の右端に置く。コンテンツヘッダーの左端に置かない。動かす対象と操作する場所が離れると、そのボタンが何に属するか読めなくなる",
568
- "ナビ項目は項目高 44px(WCAG 2.5.5 の最小ターゲット)を確保する。メイン項目・サブメニュー項目とも min-h-11 で下限を固定する(行高の小さいテキストは縦 padding だけだと割り込む)"
622
+ "ナビ項目は項目高 44px(WCAG 2.5.5 の最小ターゲット)を確保する。メイン項目・サブメニュー項目とも min-h-11 で下限を固定する(行高の小さいテキストは縦 padding だけだと割り込む)",
623
+ "PC のサブメニューは DropdownMenu の Portal / collision handling を使い、side=right / align=start / collisionPadding=8 で viewport 内に収める。絶対配置の flyout を手組みしない",
624
+ "PC のサブメニューは hover とクリックに加え、Enter / Space / ArrowDown で開き Escape で閉じてトリガーへフォーカスを戻す。hover 起動時はメニューへフォーカスを移さず、トリガーと Portal 間は短い close delay で橋渡しする"
569
625
  ]
570
626
  },
571
627
  {
@@ -646,6 +702,36 @@
646
702
  "name": "CoachMark",
647
703
  "path": "src/components/primitives/coach-mark.tsx"
648
704
  },
705
+ {
706
+ "name": "ContentCard",
707
+ "path": "src/components/primitives/content-card.tsx",
708
+ "subcomponents": [
709
+ "ContentList"
710
+ ],
711
+ "props": [
712
+ "title: string",
713
+ "href: string",
714
+ "imageSlot?: ReactNode",
715
+ "tag?: { label: string; variant?: TagProps[\"variant\"] }",
716
+ "meta?: ReactNode",
717
+ "orientation?: vertical | horizontal | list",
718
+ "aspect?: 16/10 | 16/9 | 1/1",
719
+ "linkComponent?: LinkComponent"
720
+ ],
721
+ "variants": [
722
+ "orientation: vertical | horizontal | list",
723
+ "aspect: 16/10 | 16/9 | 1/1"
724
+ ],
725
+ "rules": [
726
+ "記事・特集・お知らせなど「画像 + タイトル + メタ」のカード。ProductCard(商品)/ ShopCard(店舗)の兄弟。Card + CardContent で組まない(枠線・タイトル行数・カード高さが揃わない。実測で高さ 268 / 312 に割れた事例あり)(issue #241)",
727
+ "タイトルは 2 行で省略し常に 2 行ぶんの高さを取る。文字装飾を利用側で足さない(font-bold にしない。既定は font-medium)",
728
+ "meta は 1 行に収める。複数行にすると一覧でカード高さが割れる",
729
+ "画像は imageSlot に渡す(next/image・CMS・図版を吸収する)。縦横比とクリップはカード側が持つ",
730
+ "Medium 型の縦リストは orientation=\"list\" + ContentList(区切り線つき)を使う",
731
+ "カード全体がリンク。linkComponent で next/link 等を差し込める",
732
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
733
+ ]
734
+ },
649
735
  {
650
736
  "name": "Combobox",
651
737
  "path": "src/components/primitives/combobox.tsx",
@@ -745,7 +831,16 @@
745
831
  },
746
832
  {
747
833
  "name": "Footer",
748
- "path": "src/components/primitives/footer.tsx"
834
+ "path": "src/components/primitives/footer.tsx",
835
+ "rules": [
836
+ "variant='brand-first' でロゴ・説明文を先頭カラムに置き、その右にリンク群を並べる。下部バーはコピーライト左・bottomRightSlot 右の左右振り分けになる。linkGroups が空でもロゴ・説明文は落ちず、逆に logo/description が無いときは nav を 2 カラムへ押し込まない。この構成をカスタムマークアップで自作しない(#246)",
837
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。external:true のリンクは framework のリンク部品へ通す意味が無いため常に素の <a target=_blank> にフォールバックする(#236)",
838
+ "この部品は Client Component のため、React Server Components では Server Component から linkComponent を渡せない(実行時に Functions cannot be passed directly to Client Components で落ちる)。Next.js App Router では呼び出し側のファイルを \"use client\" にする。エラーメッセージは \"use server\" を示唆するが正解は逆(#264)"
839
+ ],
840
+ "variants": [
841
+ "default",
842
+ "brand-first"
843
+ ]
749
844
  },
750
845
  {
751
846
  "name": "FormField",
@@ -801,7 +896,10 @@
801
896
  "actions(HeaderAction)はカート・マイページ等の本来リンクである導線には href を指定する。href 指定時は Button asChild + <a> で描画され、中クリック/新規タブ・リンクコピーが効く。onClick と併用可(両方付与される)",
802
897
  "navItems は幅に入り切らない項目を自動で「その他」ドロップダウンへ退避する(#200)。consumer 側で項目数を手で slice して減らさない。ラベルを増やしても中幅でロゴ・actions と衝突しない",
803
898
  "navBreakpoint でインラインナビ行の開始コンテナ幅を選べる: md(既定 = @[768px]+)/ lg(@[1024px]+)。ロゴが多段で横幅を食うケースは lg にして中幅での同居自体を避ける",
804
- "「その他」のラベルは navOverflowLabel で差し替えられる(既定 その他)。ハンバーガーを自前で組まない"
899
+ "「その他」のラベルは navOverflowLabel で差し替えられる(既定 その他)。ハンバーガーを自前で組まない",
900
+ "HeaderAction の showLabel=true でアイコン+可視ラベルのピル型(h-10・枠線)になる。「ログイン」等アイコンだけでは伝わらない導線に使い、consumer 側でアイコンにテキストを差し込まない(#245)",
901
+ "searchBreakpoint で searchSlot の器を出し始めるコンテナ幅を選ぶ(md=768 / lg=960 の切替段のみ。xl=1280 は上限段でレイアウト切替に使わない)。狭い帯で検索を出さないときは consumer の CSS で中身を隠さずこの prop を使う。中身だけ隠しても器が flex-1 の幅を確保し、インラインナビを潰す(#243)",
902
+ "本文を Container size=\"page\" で中央寄せしているサイトは containerSize=\"page\" を指定する(Container と同じ段: page / wide / fluid)。背景・border・sticky は全幅のまま中身だけ中央寄せされ、ロゴ・ナビの左端が本文と揃う。consumer 側で className に max-w を当てると背景と線まで中央幅になり両脇が抜けるので使わない(issue #234)"
805
903
  ]
806
904
  },
807
905
  {
@@ -890,7 +988,11 @@
890
988
  },
891
989
  {
892
990
  "name": "MenuDrawer",
893
- "path": "src/components/primitives/menu-drawer.tsx"
991
+ "path": "src/components/primitives/menu-drawer.tsx",
992
+ "rules": [
993
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。この部品の項目に external フラグは無く、渡した href はすべて linkComponent で描かれる(外部 URL を出し分けたい場合は consumer 側でラップする)(#236)",
994
+ "この部品は Client Component のため、React Server Components では Server Component から linkComponent を渡せない(実行時に Functions cannot be passed directly to Client Components で落ちる)。Next.js App Router では呼び出し側のファイルを \"use client\" にする。エラーメッセージは \"use server\" を示唆するが正解は逆(#264)"
995
+ ]
894
996
  },
895
997
  {
896
998
  "name": "MultiSelect",
@@ -942,6 +1044,21 @@
942
1044
  "name": "ProgressSteps",
943
1045
  "path": "src/components/primitives/progress-steps.tsx"
944
1046
  },
1047
+ {
1048
+ "name": "Prose",
1049
+ "path": "src/components/primitives/prose.tsx",
1050
+ "props": [
1051
+ "html?: string",
1052
+ "as?: div | article | section"
1053
+ ],
1054
+ "rules": [
1055
+ "CMS(microCMS 等)のリッチテキスト HTML を描くときは、consumer 側で h2 / p / ul の CSS を書かずこれを使う。ArticleBody.module.css のような独自 CSS を作らない(issue #239)",
1056
+ "html に渡す HTML は利用側でサニタイズ済みであること。この部品は体裁を当てるだけで中身の無害化はしない",
1057
+ "記事幅は Container size=\"narrow\"(760px)で決める。この部品は幅を持たない",
1058
+ "見出しレベルは CMS の出力に従う(体裁だけを当て、階層は変えない)",
1059
+ "幅の広い表は表自身が縮む/横スクロールするので、ページは横に伸びない"
1060
+ ]
1061
+ },
945
1062
  {
946
1063
  "name": "NumberInput",
947
1064
  "path": "src/components/primitives/number-input.tsx",
@@ -987,7 +1104,20 @@
987
1104
  {
988
1105
  "name": "SearchBar",
989
1106
  "path": "src/components/primitives/search-bar.tsx",
990
- "formBinding": "none"
1107
+ "formBinding": "none",
1108
+ "props": [
1109
+ "size?: sm | md",
1110
+ "onSearch?: (query: string) => void",
1111
+ "searchHref?: string",
1112
+ "action?: string",
1113
+ "name?: string"
1114
+ ],
1115
+ "rules": [
1116
+ "JS 無効環境・クローラでも動かす必要がある画面(SEO 重視の記事メディア等)は action を指定する。<form method=\"get\" action> として描画され、Enter も検索ボタンも素のフォーム送信になる(issue #237)",
1117
+ "action 指定時のクエリキーは name(既定 q)",
1118
+ "Input + Button で <form method=get> を手組みしない",
1119
+ "searchHref は window.location を書き換える JS 前提の導線。action と併用しない(action が優先)"
1120
+ ]
991
1121
  },
992
1122
  {
993
1123
  "name": "SearchPanel",
@@ -1027,7 +1157,8 @@
1027
1157
  "rules": [
1028
1158
  "タイトル+もっと見るボタンをCardTitle+Button手書き禁止。SectionHeaderを使う",
1029
1159
  "中央寄せ大見出し(ランディング/ガイド系)は align=\"center\" を使う。eyebrow プロップに Badge 等を載せられる。手書きの中央寄せ見出し禁止",
1030
- "3xl(28px)は通常ページの主見出しにも使う。4xl/5xlはLP・マーケティング系ヒーロー専用で、size=\"3xl\" に desktopSize=\"4xl\" または desktopSize=\"5xl\" を加えてPC時だけ拡大する"
1160
+ "3xl(28px)は通常ページの主見出しにも使う。4xl/5xlはLP・マーケティング系ヒーロー専用で、size=\"3xl\" に desktopSize=\"4xl\" または desktopSize=\"5xl\" を加えてPC時だけ拡大する",
1161
+ "align=\"start\" では subtitle はタイトル行の下に全幅で置かれ、右側 action の下の空きも使って折り返す。moreHref 併用時にリンクになるのはタイトル行だけで、subtitle はリンクの外(issue #232)"
1031
1162
  ]
1032
1163
  },
1033
1164
  {
@@ -1051,7 +1182,17 @@
1051
1182
  },
1052
1183
  {
1053
1184
  "name": "ShareButtons",
1054
- "path": "src/components/primitives/share-buttons.tsx"
1185
+ "path": "src/components/primitives/share-buttons.tsx",
1186
+ "props": [
1187
+ "url?: string",
1188
+ "text?: string",
1189
+ "services?: Array<'line' | 'x' | 'facebook' | 'hatena' | 'copy' | ShareCustomService>"
1190
+ ],
1191
+ "rules": [
1192
+ "共有先は services で選ぶ(既定 line / x / copy)。同じ意匠を Button asChild で組み直さない(issue #238)",
1193
+ "組み込みに無い共有先は services に { key, label, href, icon } を混ぜて足す。href の {url} / {text} が URL エンコード済みの値に置換される",
1194
+ "共有先のロゴは SocialIcon が持つ。SVG を手書きしない"
1195
+ ]
1055
1196
  },
1056
1197
  {
1057
1198
  "name": "SideNav",
@@ -1101,7 +1242,17 @@
1101
1242
  },
1102
1243
  {
1103
1244
  "name": "SubNav",
1104
- "path": "src/components/primitives/sub-nav.tsx"
1245
+ "path": "src/components/primitives/sub-nav.tsx",
1246
+ "variants": [
1247
+ "variant: text | chip",
1248
+ "containerSize: page | wide | fluid"
1249
+ ],
1250
+ "rules": [
1251
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。この部品の項目に external フラグは無く、渡した href はすべて linkComponent で描かれる(外部 URL を出し分けたい場合は consumer 側でラップする)(#236)",
1252
+ "左右の余白は Container と同じ SP 16 / PC 24。利用側で px-* を足さない",
1253
+ "本文を Container size=\"page\" で中央寄せしているサイトは containerSize=\"page\" を指定する。下線は全幅のまま中身だけ中央寄せされる(issue #234)",
1254
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
1255
+ ]
1105
1256
  },
1106
1257
  {
1107
1258
  "name": "Tag",
@@ -1251,6 +1402,9 @@
1251
1402
  {
1252
1403
  "name": "ProductCard",
1253
1404
  "path": "src/components/ec/product-card.tsx",
1405
+ "subcomponents": [
1406
+ "BookmarkRibbonIcon"
1407
+ ],
1254
1408
  "layouts": [
1255
1409
  "vertical",
1256
1410
  "horizontal"
@@ -1263,8 +1417,10 @@
1263
1417
  "rules": [
1264
1418
  "captionは規格・仕様の短い自由記述(例: \"1kg / 冷蔵\")。価格表示の直下にtypo-body-xs / Text-Medium-Emphasisで表示。tags(配送方法等のenum variant)とは別物で代替不可",
1265
1419
  "カード幅は列(親グリッド/flex)が決める。カード自身は min-w-0 で、固定の min-width を持たない。自前のカードを組む場合もテキスト列は grid-template-columns: minmax(0, 1fr) にし、auto にしない(nowrapのメタ行のmin-content幅が列を押し広げ、サムネイルが列からはみ出す)",
1266
- "商品名は line-clamp-2 + min-h-[2lh] で常に2行分の高さを確保する。1行の商品名でも下に続く価格の縦位置が一覧内で揃う"
1267
- ]
1420
+ "商品名は line-clamp-2 + min-h-[2lh] で常に2行分の高さを確保する。1行の商品名でも下に続く価格の縦位置が一覧内で揃う",
1421
+ "hoverEffect でホバー時の画像演出を選ぶ: zoom / fade(拡大せず明るく・暗テーマ対応のため brightness を使う)/ none。未指定時の既定は orientation ごと(vertical=zoom / horizontal=none)で従来の見た目を維持し、明示指定すれば horizontal でも効く。consumer 側の CSS で scale を打ち消さない。Tailwind v4 の scale-* は transform ではなく scale プロパティのため打ち消しは壊れやすい(#244)"
1422
+ ],
1423
+ "variants": {}
1268
1424
  },
1269
1425
  {
1270
1426
  "name": "ProductCarousel",
@@ -1343,6 +1499,9 @@
1343
1499
  "features": [
1344
1500
  "helpful-count",
1345
1501
  "like"
1502
+ ],
1503
+ "rules": [
1504
+ "bordered={false} で枠線を外せる。商品詳細等ページ全体をフラットに統一する画面で consumer が border-0 を上書きしない(#247)"
1346
1505
  ]
1347
1506
  },
1348
1507
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "meta": {
3
3
  "name": "Cake.jp Design System — Composition Contracts",
4
- "version": "0.17.1",
4
+ "version": "0.20.0",
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 は選んだ後、その骨格をどう組むか(外枠構造・余白リズム・階層)の契約。前者=何を使うか、後者=どう並べるか。",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "meta": {
3
3
  "name": "Cake.jp Design System — Rules Contracts",
4
- "version": "0.17.1",
4
+ "version": "0.20.0",
5
5
  "description": "禁止パターン・アクセシビリティ要件・ライティングルールの構造化定義。lint-scratch.sh と check-drift.sh で自動検証。"
6
6
  },
7
7
  "prohibited": [
@@ -655,6 +655,10 @@
655
655
  "id": "A007",
656
656
  "rule": "全フォーム入力に <Label> または aria-label を紐付ける"
657
657
  },
658
+ {
659
+ "id": "A014",
660
+ "rule": "React Server Components 対応: client 専用 API(useState / useEffect / createContext 等)を使うモジュールは先頭に \"use client\" を宣言する。バレル(src/index.ts)には付けない — 付けるとパッケージ全体が client 扱いになり、cn / buttonVariants を Server Component から呼べなくなる。__tests__/client-boundary.test.ts が固定する"
661
+ },
658
662
  {
659
663
  "id": "A013",
660
664
  "rule": "Radix ベースのコントロール(Select / Switch / Checkbox / Combobox / Slider 等)は <button> や <span role=\"slider\"> として描画されるため、<Label htmlFor> や外側の <label> ラップでは名前が付かない。FormField でラップして aria-labelledby を自動付与させるか、単体利用なら日本語の aria-label をコントロール自身に書く(issue #202)"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "meta": {
3
3
  "name": "Cake.jp Design System — Screen Patterns Contracts",
4
- "version": "0.17.1",
4
+ "version": "0.20.0",
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 を参照する。",
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsxs as r, jsx as e, Fragment as N } from "react/jsx-runtime";
2
3
  import * as d from "react";
3
4
  import { cn as k } from "../../lib/utils.js";
@@ -130,7 +131,7 @@ function ne({
130
131
  mainId: f = "main-content",
131
132
  skipLink: g
132
133
  }) {
133
- const [y, h] = d.useState(!1), S = /* @__PURE__ */ r(N, { children: [
134
+ const [y, c] = d.useState(!1), S = /* @__PURE__ */ r(N, { children: [
134
135
  /* @__PURE__ */ r("button", { className: "relative flex items-center justify-center size-10 shrink-0 rounded-lg hover:bg-[var(--Surface-Secondary)]", "aria-label": "通知", children: [
135
136
  /* @__PURE__ */ e(j, { size: 20, className: "text-[var(--Text-High-Emphasis)]" }),
136
137
  /* @__PURE__ */ e("span", { className: "absolute top-1.5 right-1.5 size-2 rounded-full bg-[var(--Surface-Caution)]" })
@@ -154,7 +155,7 @@ function ne({
154
155
  activeKey: t,
155
156
  drawerTitle: p ?? s,
156
157
  open: y,
157
- onOpenChange: h
158
+ onOpenChange: c
158
159
  }
159
160
  ),
160
161
  /* @__PURE__ */ r("div", { className: "@container/main flex flex-1 flex-col @md:pl-18", children: [
@@ -162,7 +163,7 @@ function ne({
162
163
  L,
163
164
  {
164
165
  brand: b ?? `${s}管理`,
165
- onMenuClick: () => h(!0),
166
+ onMenuClick: () => c(!0),
166
167
  actions: v ?? S
167
168
  }
168
169
  ),
@@ -182,10 +183,10 @@ function ne({
182
183
  }
183
184
  ),
184
185
  (n || l) && /* @__PURE__ */ r("div", { className: "mb-4 space-y-1", children: [
185
- n && n.length > 0 && /* @__PURE__ */ e("nav", { className: "flex items-center gap-1 typo-body-sm text-[var(--Text-Low-Emphasis)]", "aria-label": "パンくず", children: n.map((o, c) => /* @__PURE__ */ r(d.Fragment, { children: [
186
- c > 0 && /* @__PURE__ */ e(F, { size: 12 }),
186
+ n && n.length > 0 && /* @__PURE__ */ e("nav", { className: "flex items-center gap-1 typo-body-sm text-[var(--Text-Low-Emphasis)]", "aria-label": "パンくず", children: n.map((o, h) => /* @__PURE__ */ r(d.Fragment, { children: [
187
+ h > 0 && /* @__PURE__ */ e(F, { size: 12 }),
187
188
  o.href ? /* @__PURE__ */ e("a", { href: o.href, className: "hover:text-[var(--Text-Accent-Primary)] transition-colors", children: o.label }) : /* @__PURE__ */ e("span", { className: "text-[var(--Text-High-Emphasis)]", children: o.label })
188
- ] }, c)) }),
189
+ ] }, h)) }),
189
190
  l && /* @__PURE__ */ e("h1", { className: "typo-heading-2xl text-[var(--Text-High-Emphasis)]", children: l })
190
191
  ] }),
191
192
  i
@@ -1,14 +1,14 @@
1
- import { jsxs as a, jsx as e } from "react/jsx-runtime";
1
+ import { jsxs as r, jsx as e } from "react/jsx-runtime";
2
2
  import "react";
3
- import y from "iconsax-reactjs/dist/esm/ShieldTick.js";
3
+ import v from "iconsax-reactjs/dist/esm/ShieldTick.js";
4
4
  import { cn as l } from "../../lib/utils.js";
5
5
  import { Button as u } from "../foundations/button.js";
6
- import { Separator as v } from "../foundations/separator.js";
6
+ import { Separator as y } from "../foundations/separator.js";
7
7
  function T({
8
8
  className: t,
9
- lineItems: r,
10
- totalLabel: o = "合計(税込)",
11
- totalValue: d,
9
+ lineItems: a,
10
+ totalLabel: d = "合計(税込)",
11
+ totalValue: o,
12
12
  ctaLabel: c,
13
13
  onCTAClick: n,
14
14
  showSecurityBadge: p = !1,
@@ -17,8 +17,8 @@ function T({
17
17
  ctaDisabled: f = !1,
18
18
  ...i
19
19
  }) {
20
- const m = /* @__PURE__ */ a("div", { className: "space-y-2 px-4 py-3", children: [
21
- r && r.map((s) => /* @__PURE__ */ a(
20
+ const m = /* @__PURE__ */ r("div", { className: "space-y-2 px-4 py-3", children: [
21
+ a && a.map((s) => /* @__PURE__ */ r(
22
22
  "div",
23
23
  {
24
24
  className: "flex items-center justify-between typo-body-md",
@@ -29,10 +29,10 @@ function T({
29
29
  },
30
30
  s.label
31
31
  )),
32
- r && r.length > 0 && /* @__PURE__ */ e(v, {}),
33
- /* @__PURE__ */ a("div", { className: "flex items-center justify-between", children: [
34
- /* @__PURE__ */ e("span", { className: "typo-heading-md text-[var(--Text-High-Emphasis)]", children: o }),
35
- /* @__PURE__ */ e("span", { className: "typo-heading-lg text-[var(--Text-High-Emphasis)]", children: d })
32
+ a && a.length > 0 && /* @__PURE__ */ e(y, {}),
33
+ /* @__PURE__ */ r("div", { className: "flex items-center justify-between", children: [
34
+ /* @__PURE__ */ e("span", { className: "typo-heading-md text-[var(--Text-High-Emphasis)]", children: d }),
35
+ /* @__PURE__ */ e("span", { className: "typo-heading-lg text-[var(--Text-High-Emphasis)]", children: o })
36
36
  ] }),
37
37
  /* @__PURE__ */ e(
38
38
  u,
@@ -44,8 +44,8 @@ function T({
44
44
  children: c
45
45
  }
46
46
  ),
47
- p && /* @__PURE__ */ a("p", { className: "flex items-center justify-center gap-1 typo-body-sm text-[var(--Text-Low-Emphasis)]", children: [
48
- /* @__PURE__ */ e(y, { size: 12 }),
47
+ p && /* @__PURE__ */ r("p", { className: "flex items-center justify-center gap-1 typo-body-sm text-[var(--Text-Low-Emphasis)]", children: [
48
+ /* @__PURE__ */ e(v, { size: 12 }),
49
49
  h
50
50
  ] })
51
51
  ] });
@@ -65,7 +65,8 @@ function T({
65
65
  {
66
66
  "data-slot": "order-summary",
67
67
  className: l(
68
- "fixed inset-x-0 bottom-[52px] z-[var(--Z-Sticky)] mx-auto max-w-[430px] border-t border-[var(--Border-Low-Emphasis)] bg-[var(--Surface-Primary)]",
68
+ // fixed 表示はスクロール内容の上に重なるため地は Surface-Elevated(disableFixed 側はページ内フローなので Surface-Primary のまま)
69
+ "fixed inset-x-0 bottom-[52px] z-[var(--Z-Sticky)] mx-auto max-w-[430px] border-t border-[var(--Border-Low-Emphasis)] bg-[var(--Surface-Elevated)]",
69
70
  t
70
71
  ),
71
72
  ...i,