@cakejp/design-system 0.20.0 → 0.21.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 (161) hide show
  1. package/CHANGELOG.md +179 -0
  2. package/CLAUDE.md +5 -3
  3. package/MIGRATION.md +3 -3
  4. package/README.md +31 -11
  5. package/contracts/components.json +424 -62
  6. package/contracts/composition.json +38 -4
  7. package/contracts/rules.json +27 -5
  8. package/contracts/screen-patterns.json +6 -4
  9. package/dist/components/ec/admin-shell.js +62 -60
  10. package/dist/components/ec/anniversary-card.js +13 -12
  11. package/dist/components/ec/coupon-card.js +11 -11
  12. package/dist/components/ec/delivery-calendar.js +51 -45
  13. package/dist/components/ec/mypage-shell.js +80 -69
  14. package/dist/components/ec/product-card.js +93 -76
  15. package/dist/components/ec/product-carousel.js +65 -60
  16. package/dist/components/ec/product-detail-bar.js +13 -11
  17. package/dist/components/ec/product-detail-sidebar.js +60 -58
  18. package/dist/components/ec/product-list-item.js +64 -63
  19. package/dist/components/ec/product-option.js +30 -26
  20. package/dist/components/ec/rating-stars.js +27 -27
  21. package/dist/components/ec/review-card.js +32 -32
  22. package/dist/components/ec/review-form.js +37 -37
  23. package/dist/components/ec/shop-card.js +21 -19
  24. package/dist/components/foundations/accordion.js +33 -31
  25. package/dist/components/foundations/alert-dialog.js +4 -5
  26. package/dist/components/foundations/badge.js +5 -5
  27. package/dist/components/foundations/breadcrumb.js +27 -26
  28. package/dist/components/foundations/button.js +144 -25
  29. package/dist/components/foundations/calendar.js +28 -26
  30. package/dist/components/foundations/checkbox-card.js +71 -47
  31. package/dist/components/foundations/checkbox-group.js +30 -27
  32. package/dist/components/foundations/checkbox.js +69 -52
  33. package/dist/components/foundations/chip-variants.js +13 -0
  34. package/dist/components/foundations/command.js +81 -79
  35. package/dist/components/foundations/confirm-dialog.js +136 -0
  36. package/dist/components/foundations/context-menu.js +43 -38
  37. package/dist/components/foundations/data-table.js +88 -84
  38. package/dist/components/foundations/dialog.js +45 -36
  39. package/dist/components/foundations/dropdown-menu.js +47 -42
  40. package/dist/components/foundations/file-input.js +97 -67
  41. package/dist/components/foundations/hover-card.js +21 -21
  42. package/dist/components/foundations/menubar.js +75 -69
  43. package/dist/components/foundations/navigation-menu.js +31 -26
  44. package/dist/components/foundations/pagination.js +54 -50
  45. package/dist/components/foundations/popover.js +7 -7
  46. package/dist/components/foundations/progress.js +10 -6
  47. package/dist/components/foundations/radio-group.js +6 -3
  48. package/dist/components/foundations/responsive-dialog.js +73 -55
  49. package/dist/components/foundations/scroll-area.js +18 -14
  50. package/dist/components/foundations/select.js +44 -39
  51. package/dist/components/foundations/sheet.js +45 -37
  52. package/dist/components/foundations/skeleton.js +6 -6
  53. package/dist/components/foundations/skip-link.js +10 -8
  54. package/dist/components/foundations/slider.js +73 -48
  55. package/dist/components/foundations/sonner.js +5 -4
  56. package/dist/components/foundations/spinner.js +7 -5
  57. package/dist/components/foundations/switch.js +11 -10
  58. package/dist/components/foundations/tabs.js +54 -39
  59. package/dist/components/foundations/toggle-group.js +29 -21
  60. package/dist/components/foundations/toggle.js +20 -16
  61. package/dist/components/primitives/admin-side-nav.js +65 -62
  62. package/dist/components/primitives/banner-carousel.js +32 -29
  63. package/dist/components/primitives/bottom-tab-bar.js +16 -15
  64. package/dist/components/primitives/category-scroll.js +39 -29
  65. package/dist/components/primitives/category-shortcuts.js +9 -9
  66. package/dist/components/primitives/chat-composer.js +77 -74
  67. package/dist/components/primitives/chip.js +84 -61
  68. package/dist/components/primitives/combobox.js +63 -47
  69. package/dist/components/primitives/container.js +9 -9
  70. package/dist/components/primitives/content-card.js +15 -17
  71. package/dist/components/primitives/content-carousel.js +79 -68
  72. package/dist/components/primitives/date-picker.js +137 -99
  73. package/dist/components/primitives/footer.js +36 -37
  74. package/dist/components/primitives/header.js +175 -168
  75. package/dist/components/primitives/image-carousel.js +107 -87
  76. package/dist/components/primitives/image-gallery.js +32 -27
  77. package/dist/components/primitives/image-uploader.js +43 -22
  78. package/dist/components/primitives/list-item.js +59 -51
  79. package/dist/components/primitives/menu-drawer.js +36 -38
  80. package/dist/components/primitives/multi-select.js +89 -58
  81. package/dist/components/primitives/notification-list.js +10 -9
  82. package/dist/components/primitives/number-input.js +116 -105
  83. package/dist/components/primitives/scroll-row.js +21 -0
  84. package/dist/components/primitives/search-bar.js +31 -28
  85. package/dist/components/primitives/section-header.js +54 -53
  86. package/dist/components/primitives/side-nav.js +57 -56
  87. package/dist/components/primitives/sub-nav.js +13 -14
  88. package/dist/components/primitives/tag.js +4 -2
  89. package/dist/index.css +1 -1
  90. package/dist/index.js +3076 -929
  91. package/dist/lib/field-labelling.js +31 -21
  92. package/dist/lib/focus-ring.js +22 -4
  93. package/dist/lib/high-contrast.js +4 -0
  94. package/dist/lib/image-index.js +8 -0
  95. package/dist/lib/use-prefers-reduced-motion.js +21 -0
  96. package/dist/types/components/ec/admin-shell.d.ts +11 -1
  97. package/dist/types/components/ec/anniversary-card.d.ts +9 -1
  98. package/dist/types/components/ec/mypage-shell.d.ts +12 -1
  99. package/dist/types/components/ec/product-card.d.ts +13 -1
  100. package/dist/types/components/ec/product-carousel.d.ts +13 -1
  101. package/dist/types/components/ec/product-detail-bar.d.ts +12 -1
  102. package/dist/types/components/ec/product-detail-sidebar.d.ts +11 -1
  103. package/dist/types/components/ec/product-list-item.d.ts +9 -1
  104. package/dist/types/components/ec/rating-stars.d.ts +2 -0
  105. package/dist/types/components/ec/review-card.d.ts +1 -1
  106. package/dist/types/components/ec/shop-card.d.ts +8 -1
  107. package/dist/types/components/foundations/badge.d.ts +1 -1
  108. package/dist/types/components/foundations/breadcrumb.d.ts +8 -1
  109. package/dist/types/components/foundations/button.d.ts +38 -3
  110. package/dist/types/components/foundations/checkbox-card.d.ts +7 -1
  111. package/dist/types/components/foundations/checkbox.d.ts +1 -1
  112. package/dist/types/components/foundations/chip-variants.d.ts +2 -2
  113. package/dist/types/components/foundations/confirm-dialog.d.ts +61 -0
  114. package/dist/types/components/foundations/data-table.d.ts +9 -2
  115. package/dist/types/components/foundations/dialog.d.ts +16 -1
  116. package/dist/types/components/foundations/pagination.d.ts +1 -1
  117. package/dist/types/components/foundations/responsive-dialog.d.ts +2 -1
  118. package/dist/types/components/foundations/sheet.d.ts +12 -1
  119. package/dist/types/components/foundations/skeleton.d.ts +6 -0
  120. package/dist/types/components/foundations/slider.d.ts +15 -1
  121. package/dist/types/components/foundations/tabs.d.ts +7 -0
  122. package/dist/types/components/primitives/admin-side-nav.d.ts +22 -2
  123. package/dist/types/components/primitives/banner-carousel.d.ts +9 -1
  124. package/dist/types/components/primitives/bottom-tab-bar.d.ts +13 -2
  125. package/dist/types/components/primitives/category-scroll.d.ts +10 -1
  126. package/dist/types/components/primitives/category-shortcuts.d.ts +11 -2
  127. package/dist/types/components/primitives/chip.d.ts +22 -3
  128. package/dist/types/components/primitives/combobox.d.ts +7 -2
  129. package/dist/types/components/primitives/container.d.ts +3 -0
  130. package/dist/types/components/primitives/content-card.d.ts +1 -1
  131. package/dist/types/components/primitives/content-carousel.d.ts +12 -4
  132. package/dist/types/components/primitives/date-picker.d.ts +10 -3
  133. package/dist/types/components/primitives/footer.d.ts +1 -1
  134. package/dist/types/components/primitives/header.d.ts +14 -2
  135. package/dist/types/components/primitives/image-carousel.d.ts +23 -5
  136. package/dist/types/components/primitives/list-item.d.ts +10 -1
  137. package/dist/types/components/primitives/menu-drawer.d.ts +1 -1
  138. package/dist/types/components/primitives/multi-select.d.ts +7 -2
  139. package/dist/types/components/primitives/notification-list.d.ts +10 -1
  140. package/dist/types/components/primitives/number-input.d.ts +18 -5
  141. package/dist/types/components/primitives/scroll-row.d.ts +43 -0
  142. package/dist/types/components/primitives/section-header.d.ts +9 -2
  143. package/dist/types/components/primitives/side-nav.d.ts +13 -1
  144. package/dist/types/components/primitives/sub-nav.d.ts +1 -1
  145. package/dist/types/components/primitives/tag.d.ts +1 -1
  146. package/dist/types/index.d.ts +7 -1
  147. package/dist/types/lib/field-labelling.d.ts +23 -8
  148. package/dist/types/lib/focus-ring.d.ts +70 -3
  149. package/dist/types/lib/high-contrast.d.ts +25 -0
  150. package/dist/types/lib/image-index.d.ts +2 -0
  151. package/dist/types/lib/link-component.d.ts +26 -21
  152. package/dist/types/lib/use-prefers-reduced-motion.d.ts +9 -0
  153. package/package.json +11 -3
  154. package/src/components/COMPONENT_LOOKUP.md +19 -17
  155. package/src/preset-standalone.css +42 -0
  156. package/src/preset.css +20 -1
  157. package/src/styles/semantic.css +57 -0
  158. package/templates/AGENTS.md +5 -1
  159. package/templates/CLAUDE.md +3 -1
  160. package/tokens.json +49 -0
  161. package/dist/lib/link-component.js +0 -6
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "meta": {
3
3
  "name": "Cake.jp Design System — Component Contracts",
4
- "version": "0.20.0",
4
+ "version": "0.21.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 に旧名を残す。",
8
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
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
- "foundations": 51,
12
- "primitives": 51,
11
+ "foundations": 52,
12
+ "primitives": 52,
13
13
  "ec": 24,
14
- "total": 126,
14
+ "total": 128,
15
15
  "note": "counts は各ティアの実装ファイル数(scripts/check-drift.sh が src/components/<tier>/*.tsx と突き合わせる)。subcomponents / deprecatedAliases は含まない。"
16
16
  }
17
17
  },
@@ -58,7 +58,8 @@
58
58
  ],
59
59
  "rules": [
60
60
  "破壊的操作の確認に使用。削除ボタン押下後に必ず挟む",
61
- "幅上限は default=480px / sm=320px。SP では画面端から上下左右 40px の余白を保つ(コンポーネントが持つので consumer 側で margin を足さない)"
61
+ "幅上限は default=480px / sm=320px。SP では画面端から上下左右 40px の余白を保つ(コンポーネントが持つので consumer 側で margin を足さない)",
62
+ "AlertDialogContent の size(default / sm)は DialogContent の size(sm / md / lg / xl)とは別物。値も意味も異なる(AlertDialog は確認文面向けの 2 段で、広い段は無い)。広い幅が要る入力・表示は AlertDialog ではなく Dialog / ResponsiveDialog にする"
62
63
  ]
63
64
  },
64
65
  {
@@ -95,6 +96,10 @@
95
96
  "BreadcrumbLink",
96
97
  "BreadcrumbPage",
97
98
  "BreadcrumbSeparator"
99
+ ],
100
+ "rules": [
101
+ "BreadcrumbLink は linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。asChild と併用した場合は asChild が優先され、子要素がそのまま描かれる(#236)",
102
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
98
103
  ]
99
104
  },
100
105
  {
@@ -104,6 +109,7 @@
104
109
  "default",
105
110
  "secondary",
106
111
  "tertiary",
112
+ "outline",
107
113
  "destructive",
108
114
  "ghost",
109
115
  "link",
@@ -124,8 +130,17 @@
124
130
  "icon-lg",
125
131
  "icon-xl"
126
132
  ],
133
+ "props": [
134
+ "loading?: boolean(既定 false。未指定 / false の出力は従来と同一)",
135
+ "loadingLabel?: ReactNode(既定「処理中」。loading 中に読み上げ名へ足す文言。画面には出さない。null / 空文字で出さない)"
136
+ ],
127
137
  "accessibility": [
128
- "aria-label必須(アイコンのみの場合)"
138
+ "aria-label必須(アイコンのみの場合)",
139
+ "loading 中は aria-busy='true' + aria-disabled='true'。native の disabled は付けないのでフォーカスを保つ(disabled にするとフォーカスが body へ落ち、処理後にキーボード・SR 利用者が操作位置を見失う)",
140
+ "loading 中のスピナーは aria-hidden。処理中であることは読み上げ名に loadingLabel を含めて伝える(aria-busy を読まない読み上げソフトがあるため): 中身がラベルなら sr-only の文言を足し(例「保存 処理中」)、aria-label 指定時は aria-label に連結し(loadingLabel が文字列のときだけ)、aria-labelledby 指定時は文言の要素の id を参照に足す。同じ aria-labelledby を複数のボタンで共有して loading にする場合は各ボタンに id を付ける(付けないと文言の要素の id が重複する。開発時は console.warn で知らせる)",
141
+ "loading のスピナーは prefers-reduced-motion で回転を止める(motion-reduce:animate-none)",
142
+ "forced-colors(Windows のハイコントラスト)でも loading 中はラベルを消してスピナーだけを出す(loading のときだけ forced-color-adjust: none にし、強制時と同じ見え方を自前で指定する: 文字・枠 ButtonText、背景は塗りの variant が ButtonFace・ghost / link / ghost-inverse が透明・overlay が Canvas の 80% で影なし)",
143
+ "枠のない variant のフォーカスは、緑の枠が出ずリング(FOCUS_RING)だけが手がかりになる。リングの色は高コントラストのときだけ不透明な --Focus-Ring-Control(3:1 以上)に切り替わる(既定は従来の 50% 透過)"
129
144
  ],
130
145
  "rules": [
131
146
  "<button>を直接使わない。必ずButtonコンポーネントを使う",
@@ -134,14 +149,25 @@
134
149
  "⚠️ variant='secondary' はブランドアクセント意匠(Surface-Accent-Primary-Light地+Brand-Cake-Orange文字)。Material/Bootstrap/shadcn の secondary(グレー・控えめ)とは別物なので、他DSの感覚で選ばない",
135
150
  "「戻る」「キャンセル」「閉じる」「スキップ」「あとで」等の後退・中立アクションは variant='tertiary'(Surface-Tertiary地+Text-High-Emphasis文字のニュートラルなグレー)を使う。主CTAの隣に置く2番目のボタンでも、役割が後退なら secondary ではなく tertiary",
136
151
  "variant='secondary' を使うのは、主CTAと別方向の“前進”を促す副CTAのとき(例: 主='購入する' に対し 副='サンプルを見る')",
137
- "variant='overlay'は画像・メディアの上に載せる操作ボタン専用(半透過白のSurface-Overlay-Light下地+shadow-md+backdrop-blur-sm)。ImageCarousel矢印と同系の意匠。size='icon'+className='rounded-full'で円形にできる"
152
+ "variant='outline' は白地(Surface-Primary)+ 1px の黒系の枠(Border-High-Emphasis)+ Text-High-Emphasis 文字で、ブランド色を使わない準主役の前進アクション。「一覧を見る」のような移動ボタン、一覧・カードで1件ごとに繰り返す操作(商品カードの「カートに追加」等)、ブックマーク等の保存トグルのボタン本体の代わりに使う。hover / active は ghost と同じ薄い下地(Hover-Text-Button / Active-Text-Button)。ブランド色を使ってよい場所は contracts/composition.json の brandColorUsage(正本)",
153
+ "variant='link' は補助リンク(「もっと見る」「利用規約を見る」等)の意匠で、ブランド色を使わない。Text-Medium-Emphasis + 常時下線(hover で Text-High-Emphasis)。disabled は Text-Disable",
154
+ "variant='overlay'は画像・メディアの上に載せる操作ボタン専用(半透過白のSurface-Overlay-Light下地+shadow-md+backdrop-blur-sm)。ImageCarousel矢印と同系の意匠。size='icon'+className='rounded-full'で円形にできる",
155
+ "非同期処理中のボタンは disabled + Spinner を自作せず loading を使う。ラベルを透明にしてスピナーを重ねるので幅は変わらない。click 系(クリック・Enter / Space・フォームの送信と暗黙送信)は止まり、onClick / onClickCapture も親やボタン内の要素の onClick も呼ばれない",
156
+ "loading 中の色は variant のまま(disabled のグレー塗りにはしない)。押せないことは aria-disabled、処理中であることはスピナーと読み上げ名(loadingLabel)で伝える",
157
+ "loading が止めるのは click 系だけ。onPointerDown / onMouseDown / onKeyDown は loading 中も呼ばれるので、pointerdown で開く部品(DropdownMenuTrigger asChild 等)の起点にするときは利用側で loading 中は開かないようにする",
158
+ "loading は Client Component の状態から渡す(クリック抑止に関数を使うため、Server Component で loading={true} を静的に描くと直列化できず落ちる)。loading 未指定の Button / buttonVariants は従来どおり Server Component から使える",
159
+ "asChild と loading を併用するとスピナーも読み上げの文言も出ない(見た目は変わらない)。aria-busy・aria-disabled・tabIndex=-1・クリック抑止だけを子へ渡す。リンクの意味を保つため href は外さない"
138
160
  ]
139
161
  },
140
162
  {
141
163
  "name": "Calendar",
142
164
  "path": "src/components/foundations/calendar.tsx",
143
165
  "rules": [
144
- "<input type='date'>を使わない。PopoverとCalendarを組み合わせる"
166
+ "<input type='date'>を使わない。PopoverとCalendarを組み合わせる",
167
+ "年月ドロップダウン(captionLayout=dropdown)は透明な select を枠で包んでリングを描く。forced-colors(Windows のハイコントラスト)ではリング(box-shadow)が消えるため、枠にフォーカス時の輪郭線(outline)を出す。通常表示は変わらない"
168
+ ],
169
+ "accessibility": [
170
+ "月・年のドロップダウンの枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--input))] の形で書く"
145
171
  ]
146
172
  },
147
173
  {
@@ -173,7 +199,8 @@
173
199
  "path": "src/components/foundations/checkbox.tsx",
174
200
  "formBinding": "controller",
175
201
  "accessibility": [
176
- "必ずLabelと紐付ける"
202
+ "必ずLabelと紐付ける",
203
+ "高コントラスト(OS の「コントラストを上げる」/ data-contrast=\"more\")のとき、未選択の枠は --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書き、独自の色を足さない(既定の見た目は変わらない)"
177
204
  ]
178
205
  },
179
206
  {
@@ -184,6 +211,11 @@
184
211
  "subcomponents": [
185
212
  "CheckboxCardGroup",
186
213
  "CheckboxCardItem"
214
+ ],
215
+ "accessibility": [
216
+ "アクセシブルネームはラベル(children + badge)だけを aria-labelledby で指す。description と、選択中だけ見える expandedContent は aria-describedby に分ける(選択の前後で名前が変わらない)",
217
+ "ラベルの無いカードは description が名前になるため、description を aria-describedby に重ねない。aria-label / aria-labelledby を明示した場合はそちらが名前になる",
218
+ "高コントラスト(OS の「コントラストを上げる」/ data-contrast=\"more\")のとき、未選択の枠は --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書き、独自の色を足さない(既定の見た目は変わらない)"
187
219
  ]
188
220
  },
189
221
  {
@@ -192,6 +224,10 @@
192
224
  "formBinding": "controller",
193
225
  "subcomponents": [
194
226
  "CheckboxGroupItem"
227
+ ],
228
+ "accessibility": [
229
+ "CheckboxGroupItem のフォーカス意匠は DS 共通の FOCUS_RING(rules.json A005)",
230
+ "高コントラスト(OS の「コントラストを上げる」/ data-contrast=\"more\")のとき、未選択の枠は --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書き、独自の色を足さない(既定の見た目は変わらない)"
195
231
  ]
196
232
  },
197
233
  {
@@ -204,11 +240,49 @@
204
240
  },
205
241
  {
206
242
  "name": "Command",
207
- "path": "src/components/foundations/command.tsx"
243
+ "path": "src/components/foundations/command.tsx",
244
+ "rules": [
245
+ "forced-colors(Windows のハイコントラスト)では選択中の項目(data-selected)を Highlight の地 + HighlightText で塗り、検索欄は枠(command-input-wrapper。input ではなく枠。input は枠より高く Command 本体の overflow-hidden に切り取られる)にフォーカス時の内側の輪郭線(outline)を出す。通常表示は変わらない"
246
+ ]
247
+ },
248
+ {
249
+ "name": "ConfirmDialog",
250
+ "path": "src/components/foundations/confirm-dialog.tsx",
251
+ "exported": false,
252
+ "subcomponents": [
253
+ "ConfirmDialogProvider",
254
+ "useConfirm"
255
+ ],
256
+ "props": [
257
+ "useConfirm(): (options) => Promise<boolean>",
258
+ "options.title: ReactNode(必須。ダイアログの読み上げ名)",
259
+ "options.description?: ReactNode(判断材料。省略すると Radix が開発時に console.warn を出すので原則渡す)",
260
+ "options.confirmLabel?: ReactNode(既定「確認」。「削除する」等の動詞を推奨)",
261
+ "options.cancelLabel?: ReactNode(既定「キャンセル」)",
262
+ "options.tone?: default | destructive(確定ボタンの Button variant。既定 default)"
263
+ ],
264
+ "accessibility": [
265
+ "AlertDialog 基盤(role=alertdialog)。開くとキャンセルにフォーカスが入り、Esc はキャンセル(false)。背景クリックでは閉じない",
266
+ "閉じた後は confirm() を呼んだ時点でフォーカスがあった要素へ戻す(Trigger を持たないため)。閉じるアニメーション中に次の confirm() が来ても、最後に閉じたときは元の呼び出し元へ戻る。呼び出し時にフォーカスが body で、表示中・閉じかけの確認も無いときは戻さない(以前の無関係な要素へ飛ばさない)",
267
+ "メニュー項目(DropdownMenuItem 等)から呼ぶと項目はメニューと一緒に消えるので、閉じた後はそのメニューを開いたトリガー(メニューの aria-labelledby が指す要素)へ戻す"
268
+ ],
269
+ "rules": [
270
+ "window.confirm() を使わない。アプリのルートに <ConfirmDialogProvider> を 1 つ置き、const confirm = useConfirm(); if (await confirm({ title, description })) { ... } で確認する",
271
+ "削除など取り消せない操作は tone='destructive' と動詞の confirmLabel(例: '削除する')を渡す。キャンセルは AlertDialogCancel 既定の tertiary のまま(secondary にしない)",
272
+ "同時に複数呼ばれたら待ち行列で 1 件ずつ表示する。Provider が unmount されたとき(<Activity> で隠したときを含む)の未解決と、unmount 後の呼び出しは false で resolve し、表示も閉じる",
273
+ "useConfirm を ConfirmDialogProvider の外で使うと例外を投げる(設定漏れを「常にキャンセル」として黙って通さないため)",
274
+ "確定ボタンを押した時点で true に resolve して閉じる。処理完了までダイアログ内で待たせたい場合は AlertDialog を直接組み、確定ボタンに Button の loading を使う",
275
+ "\"use client\" モジュール。Next.js App Router では Server Component の layout にそのまま置ける(ConfirmDialogProvider 自体が client 境界)。useConfirm は Client Component から呼ぶ"
276
+ ]
208
277
  },
209
278
  {
210
279
  "name": "ContextMenu",
211
- "path": "src/components/foundations/context-menu.tsx"
280
+ "path": "src/components/foundations/context-menu.tsx",
281
+ "rules": [
282
+ "中身の最大幅は Radix の available-width(画面内に使える幅)まで。長い項目や className で渡した広い幅でも、幅 390px の画面で右へはみ出さない(収まる幅・既定幅には効かない)",
283
+ "画面の右端・下端の近くで開くと、左へ反転・上へずらして画面内に収まる。Content に position: fixed(className=\"fixed\" 等)を足さない。足すと Radix の配置用ラッパーが 0×0 になり、反転・ずらしが効かなくなる",
284
+ "forced-colors(Windows のハイコントラスト)では薄い地(focus:bg-accent)が Canvas に置き換わって選択位置が見えないため、フォーカス中の項目を Highlight の地 + HighlightText で塗る(中のアイコン・ショートカットも HighlightText。destructive も同じ)。項目は outline-none(outline-hidden は forced-colors で全項目に常時 2px の輪郭線を出す)。通常表示は変わらない"
285
+ ]
212
286
  },
213
287
  {
214
288
  "name": "DataTable",
@@ -238,9 +312,13 @@
238
312
  "rules": [
239
313
  "管理画面のテーブルはui/tableでなくこちらを使う。ソート・選択行・アクションメニュー対応",
240
314
  "ActionCellのメニュー操作は行のonClickへ伝播しない(本体でstopPropagation済み)。行クリック選択との併用時も利用側ガードは不要",
241
- "見出し文字を出さない列(選択・操作・並び替えハンドル)は DataTableHead の srLabel で読み上げ用ラベルを付ける。空の <th> は列の意味が支援技術に伝わらない(axe: empty-table-header)"
315
+ "見出し文字を出さない列(選択・操作・並び替えハンドル)は DataTableHead の srLabel で読み上げ用ラベルを付ける。空の <th> は列の意味が支援技術に伝わらない(axe: empty-table-header)",
316
+ "DataTableLinkCell は linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。external:true のリンクは framework のリンク部品へ通す意味が無いため常に素の <a target=_blank> にフォールバックする(#236)",
317
+ "DataTableLinkCell は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)",
318
+ "DataTableInputCell は forced-colors(Windows のハイコントラスト)では inset の影が消えるため、入力欄にフォーカスがあるときセル全体に内側の輪郭線(outline)を出す。通常表示は変わらない"
242
319
  ],
243
320
  "accessibility": [
321
+ "DataTableCheckboxCell は Space またはセルのクリックで選択を一度だけ通知し、行の onClick へ伝播しない。Enter は Checkbox の標準仕様に従い選択を変えない。indeterminate からの操作は true を通知する",
244
322
  "sortable な列の押下は <button> が担い、Tab / Enter / Space で操作できる。並び替え状態は <th aria-sort>(none / ascending / descending)で伝わる。th に onClick を直接書かない",
245
323
  "方向アイコンは装飾なので aria-hidden。列名は button の中に置き、読み上げ名にする"
246
324
  ]
@@ -257,8 +335,20 @@
257
335
  "DialogFooter",
258
336
  "DialogClose"
259
337
  ],
338
+ "sizes": [
339
+ "sm",
340
+ "md",
341
+ "lg",
342
+ "xl"
343
+ ],
344
+ "props": [
345
+ "DialogContent: size?: sm | md | lg | xl(未指定は従来の幅 sm:max-w-lg)",
346
+ "DialogContent: showCloseButton?: boolean"
347
+ ],
260
348
  "rules": [
261
- "div.fixed.inset-0で自作しない。ESC・Tab・背景スクロールを自動処理"
349
+ "div.fixed.inset-0で自作しない。ESC・Tab・背景スクロールを自動処理",
350
+ "幅は DialogContent の size で選ぶ(sm 400px / md 512px / lg 640px / xl 800px。sm 以上の画面で効く)。SP は段に関係なく画面端から 16px を残して収まる。size 未指定は従来の幅(sm:max-w-lg)のままで、preset が --container-lg を 960px に再定義しているため実幅は 960px(512px ではない)。幅を決めたい画面は size を明示する。段に無い幅のために className で max-w を直書きしない(どうしても要るときだけ sm:max-w-[…] で上書き。size より className が勝つ)。名前付きの max-w-md / max-w-lg / max-w-xl は preset が --container-* を再定義して Tailwind 既定と別の値になるので使わない(px の任意値で書く)",
351
+ "DialogContent の size(sm / md / lg / xl)は AlertDialogContent の size(default / sm)とは別物。値も意味も異なる"
262
352
  ]
263
353
  },
264
354
  {
@@ -270,7 +360,11 @@
270
360
  },
271
361
  {
272
362
  "name": "DropdownMenu",
273
- "path": "src/components/foundations/dropdown-menu.tsx"
363
+ "path": "src/components/foundations/dropdown-menu.tsx",
364
+ "rules": [
365
+ "中身の最大幅は Radix の available-width(画面内に使える幅)まで。長い項目や className で渡した広い幅でも、幅 390px の画面で右へはみ出さない(収まる幅・既定幅には効かない)",
366
+ "forced-colors(Windows のハイコントラスト)では薄い地(focus:bg-accent)が Canvas に置き換わって選択位置が見えないため、フォーカス中の項目を Highlight の地 + HighlightText で塗る(中のアイコン・ショートカットも HighlightText。destructive も同じ)。項目は outline-none(outline-hidden は forced-colors で全項目に常時 2px の輪郭線を出す)。通常表示は変わらない"
367
+ ]
274
368
  },
275
369
  {
276
370
  "name": "Field",
@@ -327,7 +421,10 @@
327
421
  },
328
422
  {
329
423
  "name": "HoverCard",
330
- "path": "src/components/foundations/hover-card.tsx"
424
+ "path": "src/components/foundations/hover-card.tsx",
425
+ "rules": [
426
+ "中身の最大幅は Radix の available-width(画面内に使える幅)まで。長い項目や className で渡した広い幅でも、幅 390px の画面で右へはみ出さない(収まる幅・既定幅には効かない)"
427
+ ]
331
428
  },
332
429
  {
333
430
  "name": "IconBadge",
@@ -368,7 +465,11 @@
368
465
  },
369
466
  {
370
467
  "name": "Menubar",
371
- "path": "src/components/foundations/menubar.tsx"
468
+ "path": "src/components/foundations/menubar.tsx",
469
+ "rules": [
470
+ "中身の最大幅は Radix の available-width(画面内に使える幅)まで。長い項目や className で渡した広い幅でも、幅 390px の画面で右へはみ出さない(収まる幅・既定幅には効かない)",
471
+ "forced-colors(Windows のハイコントラスト)では薄い地(focus:bg-accent)が Canvas に置き換わって選択位置が見えないため、フォーカス中の項目を Highlight の地 + HighlightText で塗る(中のアイコン・ショートカットも HighlightText。destructive も同じ)。項目は outline-none(outline-hidden は forced-colors で全項目に常時 2px の輪郭線を出す)。通常表示は変わらない"
472
+ ]
372
473
  },
373
474
  {
374
475
  "name": "NavigationMenu",
@@ -379,7 +480,9 @@
379
480
  "path": "src/components/foundations/pagination.tsx",
380
481
  "rules": [
381
482
  "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。この部品の項目に external フラグは無く、渡した href はすべて linkComponent で描かれる(外部 URL を出し分けたい場合は consumer 側でラップする)(#236)",
382
- "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
483
+ "PaginationPrevious / PaginationNext も linkComponent を受ける(PaginationLink へ転送される)。矢印だけの SP でも名前が消えないよう aria-label(前のページへ / 次のページへ)は linkComponent 経由でも付く(#236)",
484
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)",
485
+ "ページ番号(size=\"icon\")は 3 桁以下なら SP 36px / PC 40px の正方形(従来どおり)。4 桁以上は文字幅に合わせて横に広がり、5 桁でもはみ出さない(幅はフォント次第)"
383
486
  ]
384
487
  },
385
488
  {
@@ -389,11 +492,17 @@
389
492
  "PopoverTrigger",
390
493
  "PopoverContent",
391
494
  "PopoverAnchor"
495
+ ],
496
+ "rules": [
497
+ "中身の最大幅は Radix の available-width(画面内に使える幅)まで。長い項目や className で渡した広い幅でも、幅 390px の画面で右へはみ出さない(収まる幅・既定幅には効かない)"
392
498
  ]
393
499
  },
394
500
  {
395
501
  "name": "Progress",
396
- "path": "src/components/foundations/progress.tsx"
502
+ "path": "src/components/foundations/progress.tsx",
503
+ "accessibility": [
504
+ "forced-colors(Windows のハイコントラスト)では進んだ部分を Highlight、全体の範囲を CanvasText の輪郭線(outline。レイアウトに影響しない)で示す。通常表示は変わらない"
505
+ ]
397
506
  },
398
507
  {
399
508
  "name": "RadioGroup",
@@ -407,7 +516,8 @@
407
516
  "chip"
408
517
  ],
409
518
  "accessibility": [
410
- "各RadioGroupItemにラベルテキスト必須。ラベルは children として RadioGroupItem の内側に書く(<RadioGroupItem value='x'>ラベル</RadioGroupItem>)。RadioGroupItem が内部で <label> 相当の関連付けまで行うため、外側に <Label htmlFor> を併置してはいけない(ラベル重複になる)"
519
+ "各RadioGroupItemにラベルテキスト必須。ラベルは children として RadioGroupItem の内側に書く(<RadioGroupItem value='x'>ラベル</RadioGroupItem>)。RadioGroupItem が内部で <label> 相当の関連付けまで行うため、外側に <Label htmlFor> を併置してはいけない(ラベル重複になる)",
520
+ "variant=\"chip\" の選択中は、forced-colors(Windows のハイコントラスト)では Highlight の地 + HighlightText で示す(Chip の selected と同じ)。通常表示は変わらない"
411
521
  ],
412
522
  "props": [
413
523
  "children: ラベル本文(必須。tile/chip 共通)",
@@ -418,14 +528,19 @@
418
528
  "variant='tile'(既定)=ラジオ丸+ラベル+補足説明+展開コンテンツのタイル型",
419
529
  "⚠️ タイルの枠(border/角丸/余白/選択状態の意匠)は RadioGroupItem 自身が描画する。consumer 側で <div className='border rounded-lg ...'> 等で囲むと枠が二重になる。囲まずにそのまま並べる",
420
530
  "ラベルは children で渡す。<RadioGroupItem/> と <Label htmlFor> を兄弟に並べる書き方は誤り(枠の二重化+ラベル重複を招く)",
421
- "variant='chip'=ラベルを内包したChoice Chip意匠の排他選択(タグ選択・絞り込み等)。ラジオ丸は描画せず、Chip本体と同じchipVariantsを再利用して選択状態をaccent意匠(静止=薄オレンジ/hover=ベタ塗り)で示す。description/expandedContentは持たない。Radixのキーボード操作・a11yは維持"
531
+ "variant='chip'=ラベルを内包したChoice Chip意匠の排他選択(タグ選択・絞り込み等)。ラジオ丸は描画せず、Chip本体と同じchipVariants(filled)を再利用し、選択状態は入力部品のオン状態としてブランド色の薄い地(Chipのaccent意匠と同じトークン: 静止=薄オレンジ/hover=ベタ塗り)で示す。Chip の selected(グレー地 + 黒系の枠)とは別の見た目。description/expandedContentは持たない。Radixのキーボード操作・a11yは維持",
532
+ "tile の未選択の枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く"
422
533
  ]
423
534
  },
424
535
  {
425
536
  "name": "ResponsiveDialog",
426
537
  "path": "src/components/foundations/responsive-dialog.tsx",
538
+ "props": [
539
+ "ResponsiveDialogContent: size?: sm | md | lg | xl(未指定は従来の幅。PC の Dialog の幅の段で DialogContent の size と同じ)"
540
+ ],
427
541
  "rules": [
428
- "デスクトップ=Dialog、モバイル=Sheetを自動切替"
542
+ "デスクトップ=Dialog、モバイル=Drawer(下部のボトムシート)を自動切替",
543
+ "PC の Dialog の幅は ResponsiveDialogContent の size で選ぶ(sm 400px / md 512px / lg 640px / xl 800px。未指定は従来の幅)。SP の Drawer は全幅のボトムシートなので size は無視される(Drawer には渡らない)"
429
544
  ]
430
545
  },
431
546
  {
@@ -438,7 +553,9 @@
438
553
  "formBinding": "controller",
439
554
  "rules": [
440
555
  "<select>HTMLネイティブを使わない",
441
- "SelectTrigger の高さは size='default'=44px(h-11)で Input / DatePicker trigger / Button default と揃う。size='sm'=36px。高さは素のクラスで当てているため className='h-12' 等で上書きできる(data 属性セレクタではない)"
556
+ "SelectTrigger の高さは size='default'=44px(h-11)で Input / DatePicker trigger / Button default と揃う。size='sm'=36px。高さは素のクラスで当てているため className='h-12' 等で上書きできる(data 属性セレクタではない)",
557
+ "position=\"popper\" のときだけ、一覧の最大幅を Radix の available-width(画面内に使える幅)までにする。長い選択肢があっても幅 390px の画面で右へはみ出さない。既定の item-aligned は Radix が画面内に寄せる",
558
+ "forced-colors(Windows のハイコントラスト)では薄い地(focus:bg-accent)が Canvas に置き換わって選択位置が見えないため、フォーカス中の項目(SelectItem)を Highlight の地 + HighlightText で塗る(項目の文字・選択済みのチェックも HighlightText)。項目は outline-none(outline-hidden は forced-colors で全項目に常時 2px の輪郭線を出す)。通常表示は変わらない"
442
559
  ]
443
560
  },
444
561
  {
@@ -457,11 +574,20 @@
457
574
  "SheetFooter",
458
575
  "SheetClose"
459
576
  ],
577
+ "sizes": [
578
+ "sm",
579
+ "md",
580
+ "lg"
581
+ ],
460
582
  "props": [
461
583
  "SheetContent: side?: top | right | bottom | left",
584
+ "SheetContent: size?: sm | md | lg(既定 sm。side が left / right のときだけ効く)",
462
585
  "SheetContent: showCloseButton?: boolean",
463
586
  "SheetContent: autoFocus?: boolean"
464
587
  ],
588
+ "rules": [
589
+ "幅は SheetContent の size で選ぶ(sm 384px 既定 / md 576px / lg 768px。side が left / right のときだけ効き、top / bottom は全幅なので無視される)。sm 以上の画面で効き、SP は段に関係なく画面の 3/4 幅。段に無い幅のために className で max-w を直書きしない(どうしても要るときだけ sm:max-w-[…] で上書き。size より className が勝つ)"
590
+ ],
465
591
  "accessibility": [
466
592
  "SheetTitle は必須(ダイアログの読み上げ名)",
467
593
  "フォーカスは既定で自動移動する(開く=Sheet の中へ / 閉じる=トリガーへ戻る)。WCAG 2.4.3。autoFocus={false} は止める必要がある特別な場合だけ使う(issue #233)"
@@ -472,6 +598,9 @@
472
598
  "path": "src/components/foundations/skeleton.tsx",
473
599
  "rules": [
474
600
  "アニメーションはpulseのみ許可"
601
+ ],
602
+ "accessibility": [
603
+ "動きを減らす設定(prefers-reduced-motion: reduce)では明滅(pulse)を止め、面だけを残す。読み込み中であることは包んでいる領域の aria-busy や Spinner の文言で伝える"
475
604
  ]
476
605
  },
477
606
  {
@@ -498,7 +627,11 @@
498
627
  "accessibility": [
499
628
  "role=\"slider\" を持つのはつまみ側なので、名前はつまみに付ける。aria-label / aria-labelledby は Slider が各つまみへ転送する(issue #202)",
500
629
  "つまみが 2 つ以上(範囲指定)で aria-label を使うと、読み上げで区別できるよう「(下限)」「(上限)」が自動で補われる。文言を自分で決めたいときは thumbLabels を使う",
501
- "FormField 配下ならラベルの id を自動で aria-labelledby として受け取るので、単体利用のときだけ日本語の aria-label を書く"
630
+ "FormField 配下ならラベルの id を自動で aria-labelledby として受け取るので、単体利用のときだけ日本語の aria-label を書く",
631
+ "範囲スライダーの Home / End はフォーカス中のつまみを、そのつまみが動ける範囲の端(隣のつまみ ± minStepsBetweenThumbs × step、無ければ min / max)へ動かす(WAI-ARIA APG Multi-Thumb Slider)。つまみが 1 つのときは Radix の既定のまま",
632
+ "つまみの数は value / defaultValue の配列の長さ。どちらも無いときは Radix の既定値 [min] に合わせて 1 つ",
633
+ "forced-colors(Windows のハイコントラスト)では選択範囲を Highlight、レールを CanvasText の輪郭線(outline)で示す。通常表示は変わらない",
634
+ "溝は高コントラストのとき --Surface-Control-Track(3:1 以上)に切り替わる。溝の色は bg-[var(--Surface-Control-Track,var(--Surface-Tertiary))] の形で書く。高コントラスト時の溝と選択範囲(オレンジ)は明るさが近く、区別は色相が中心。値はつまみの位置で伝わる(つまみは 3:1 を満たす)"
502
635
  ]
503
636
  },
504
637
  {
@@ -518,14 +651,18 @@
518
651
  },
519
652
  {
520
653
  "name": "Spinner",
521
- "path": "src/components/foundations/spinner.tsx"
654
+ "path": "src/components/foundations/spinner.tsx",
655
+ "accessibility": [
656
+ "role=\"status\" と「読み込み中」の文言を持つ。動きを減らす設定(prefers-reduced-motion: reduce)では回転を止め、上辺だけ色付きの欠けた輪のまま処理中を示す"
657
+ ]
522
658
  },
523
659
  {
524
660
  "name": "Switch",
525
661
  "path": "src/components/foundations/switch.tsx",
526
662
  "formBinding": "controller",
527
663
  "accessibility": [
528
- "Label必須"
664
+ "Label必須",
665
+ "フォーカスをリングだけで示す。色は FOCUS_RING_ONLY(内側に描くなら FOCUS_RING_INSET)を使い、ring-[var(--Focus-High-Emphasis)]/50 を直書きしない。高コントラストのときだけ不透明なリング(--Focus-Ring-Control・3:1 以上)に切り替わる"
529
666
  ]
530
667
  },
531
668
  {
@@ -550,7 +687,17 @@
550
687
  ],
551
688
  "rules": [
552
689
  "サイドバー等の狭い場所に segment を置くときは TabsList size=\"sm\" を使う。既定は 1 項目 100px の最小幅を持ち、3 つ並べると 266px 幅ではみ出す(issue #235)",
553
- "underbar のトリガーは左右 8px の padding を持つ。className=\"px-2\" を利用側で足さない"
690
+ "underbar のトリガーは左右 8px の padding を持つ。className=\"px-2\" を利用側で足さない",
691
+ "orientation=\"vertical\" は設定画面・ヘルプなど、タブが多い・ラベルが長い・パネルが縦に長い PC 幅の画面で使う。幅 390px 前後ではタブの列とパネルが半分ずつになり窮屈なので、横向き(既定)か SectionNav / SubNav を選ぶ",
692
+ "縦並びは data-orientation で DS 側が組むため、利用側で TabsList に flex-col や h-auto を重ねて書かない",
693
+ "TabsContent(Radix は tabindex=0 を付けるので、キーボードでフォーカスできるパネル)は、キーボードでフォーカスしたとき(focus-visible)だけ DS 共通の FOCUS_RING(緑のリング。forced-colors では輪郭線)を出す。静止時・マウスのクリックでは何も出ない",
694
+ "TabsTrigger のフォーカスの輪郭線は内側に描く(outline-offset が負)。外側に描くと TabsList の overflow-x-auto に全辺切り取られてフォーカス位置が見えない。静止時の見た目は変わらない"
695
+ ],
696
+ "props": {
697
+ "orientation": "\"horizontal\" | \"vertical\"(既定 \"horizontal\")— Tabs(Root)に渡す。\"vertical\" では TabsList を左に縦並び(列の幅は Tabs の半分まで、長いラベルはタブの中で折り返す)、TabsContent は右の残り幅を取る。underbar は右端の線、segment は地の色で選択中を示す。矢印キーは上下で移動する。横向きの見た目は変わらない"
698
+ },
699
+ "accessibility": [
700
+ "forced-colors(Windows のハイコントラスト)では選択中のタブを OS の選択色で示す(segment は Highlight の地 + HighlightText、underbar / scrollable は Highlight の線。非選択の線は Canvas で消す)。通常表示は変わらない"
554
701
  ]
555
702
  },
556
703
  {
@@ -571,19 +718,31 @@
571
718
  "rules": [
572
719
  "トースト内にアクションリンクを入れない。左ボーダーではなく背景色全体で状態を表現",
573
720
  "アプリのルートに <Toaster /> を1つ置き、表示は toast()(sonner の再 export)で呼ぶ"
721
+ ],
722
+ "accessibility": [
723
+ "loading トーストの回転アイコンは、動きを減らす設定(prefers-reduced-motion: reduce)では止まる"
574
724
  ]
575
725
  },
576
726
  {
577
727
  "name": "Toggle",
578
728
  "path": "src/components/foundations/toggle.tsx",
579
- "formBinding": "controller"
729
+ "formBinding": "controller",
730
+ "accessibility": [
731
+ "forced-colors(Windows のハイコントラスト)では押されているとき(data-state=on)を Highlight の地 + HighlightText で示す(default / outline 共通、ToggleGroup と同じ)。通常表示は変わらない",
732
+ "outline の枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--input))] の形で書く"
733
+ ]
580
734
  },
581
735
  {
582
736
  "name": "ToggleGroup",
583
737
  "path": "src/components/foundations/toggle-group.tsx",
584
738
  "formBinding": "controller",
585
739
  "rules": [
586
- "水平パディングはコンポーネント内に入れない。親が管理する"
740
+ "水平パディングはコンポーネント内に入れない。親が管理する",
741
+ "ToggleGroupItem のフォーカスは内側に描く(ring-inset / forced-colors は FORCED_COLORS_FOCUS_INSET)。ToggleGroup 本体が overflow-x-auto なので、外側のリングは左右で切り取られる。静止時の見た目は変わらない"
742
+ ],
743
+ "accessibility": [
744
+ "forced-colors(Windows のハイコントラスト)では押されている項目(data-state=on)を Highlight の地 + HighlightText で示す(default / outline 共通)。通常表示は変わらない",
745
+ "outline の未選択の枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Low-Emphasis))] の形で書く"
587
746
  ]
588
747
  },
589
748
  {
@@ -621,18 +780,26 @@
621
780
  "折りたたみ(開閉)を持たせる場合、トリガーはサイドバー内ヘッダー行の右端に置く。コンテンツヘッダーの左端に置かない。動かす対象と操作する場所が離れると、そのボタンが何に属するか読めなくなる",
622
781
  "ナビ項目は項目高 44px(WCAG 2.5.5 の最小ターゲット)を確保する。メイン項目・サブメニュー項目とも min-h-11 で下限を固定する(行高の小さいテキストは縦 padding だけだと割り込む)",
623
782
  "PC のサブメニューは DropdownMenu の Portal / collision handling を使い、side=right / align=start / collisionPadding=8 で viewport 内に収める。絶対配置の flyout を手組みしない",
624
- "PC のサブメニューは hover とクリックに加え、Enter / Space / ArrowDown で開き Escape で閉じてトリガーへフォーカスを戻す。hover 起動時はメニューへフォーカスを移さず、トリガーと Portal 間は短い close delay で橋渡しする"
783
+ "PC のサブメニューは hover とクリックに加え、Enter / Space / ArrowDown で開き Escape で閉じてトリガーへフォーカスを戻す。hover 起動時はメニューへフォーカスを移さず、トリガーと Portal 間は短い close delay で橋渡しする",
784
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。PC レールの項目・サブメニューのポップアップ・SP ドロワーの項目とサブ項目のすべてに効く。ポップアップ内のリンクは Radix の asChild 越しに描かれるため、ref と任意の属性を <a> まで転送する実装を渡す(next/link は転送する)。aria-current=page は linkComponent 経由でも付く(#236)。SP ドロワー内のリンク(項目・サブ項目)を押すと onOpenChange(false) が呼ばれる(クライアント遷移ではページが再読み込みされず、ドロワーが開いたまま新しいページに重なるのを防ぐ)",
785
+ "この部品は 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)"
625
786
  ]
626
787
  },
627
788
  {
628
789
  "name": "BannerCarousel",
629
- "path": "src/components/primitives/banner-carousel.tsx"
790
+ "path": "src/components/primitives/banner-carousel.tsx",
791
+ "rules": [
792
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。「もっと見る」(moreHref)と各バナー(items[].href)のリンクに効く(#236)",
793
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
794
+ ]
630
795
  },
631
796
  {
632
797
  "name": "BottomTabBar",
633
798
  "path": "src/components/primitives/bottom-tab-bar.tsx",
634
799
  "rules": [
635
- "モバイル専用。safe-area-inset-bottom対応必須"
800
+ "モバイル専用。safe-area-inset-bottom対応必須",
801
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。href を持つ項目だけが対象で、href の無い項目は従来どおり <button>。項目の onClick は href の有無によらず呼ばれる(href 付きは遷移を止めない)。aria-current=page は linkComponent 経由でも付く(#236)",
802
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
636
803
  ]
637
804
  },
638
805
  {
@@ -646,12 +813,18 @@
646
813
  "name": "CategoryScroll",
647
814
  "path": "src/components/primitives/category-scroll.tsx",
648
815
  "rules": [
649
- "ルートは w-full 併記で CSS Grid 直下でも列幅いっぱいに広がる(mx-auto による max-content 縮み対策、issue #207)。grid 直下に置くための余分なラッパー div は不要"
816
+ "ルートは w-full 併記で CSS Grid 直下でも列幅いっぱいに広がる(mx-auto による max-content 縮み対策、issue #207)。grid 直下に置くための余分なラッパー div は不要",
817
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。「もっと見る」(moreHref)と各カテゴリ(items[].href)のリンクに効く(#236)",
818
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
650
819
  ]
651
820
  },
652
821
  {
653
822
  "name": "CategoryShortcuts",
654
- "path": "src/components/primitives/category-shortcuts.tsx"
823
+ "path": "src/components/primitives/category-shortcuts.tsx",
824
+ "rules": [
825
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。href を持つ項目だけが対象で、href の無い項目は従来どおり <button>。項目の onClick は href の有無によらず呼ばれる(href 付きは遷移を止めない)(#236)",
826
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
827
+ ]
655
828
  },
656
829
  {
657
830
  "name": "ChatComposer",
@@ -679,7 +852,8 @@
679
852
  "IME変換中のEnterでは送信しない(日本語入力の誤送信防止)",
680
853
  "添付は内蔵のFileInputで選択し、選択済みはChipのremovableで表示・削除する",
681
854
  "送信中はisSubmittingを立てる(送信ボタン無効化で二重送信を防ぐ)",
682
- "<form>を内包しないため、フォーム内に置いても入れ子にならない"
855
+ "<form>を内包しないため、フォーム内に置いても入れ子にならない",
856
+ "フォーカス表示は枠(ラッパー)が FOCUS_RING_WITHIN で描く。forced-colors(Windows のハイコントラスト)では枠に輪郭線(outline)を出し、中の Textarea の輪郭線は二重にならないよう消す。通常表示は変わらない"
683
857
  ]
684
858
  },
685
859
  {
@@ -695,7 +869,19 @@
695
869
  ],
696
870
  "rules": [
697
871
  "フィルター・ステータスタブに使う。ToggleGroup+Badge手書き禁止",
698
- "Tagと違い押せる・削除できる"
872
+ "Tagと違い押せる・削除できる",
873
+ "selected の見た目はブランド色を使わず、Surface-Tertiary 地 + Text-High-Emphasis 文字 + 内側 1px の黒系の枠(Border-High-Emphasis。inset-ring で描くので未選択→選択でサイズは変わらない)。chipVariants の variant='selected' に対応し、selected prop が自動で選ぶ。variant='accent'(薄いブランド色地)は明示指定の互換のために残してあるが、選択中の表示には使わない(contracts/composition.json の brandColorUsage)。count バッジの選択中は Surface-Primary 地 + Text-High-Emphasis",
874
+ "removable の削除ボタンは data-slot=\"chip-remove\"(本体は data-slot=\"chip-trigger\")。読み上げ名は removeLabel(既定「削除」)。削除可能な Chip が複数並ぶ場面では「{ラベル}を削除」のように対象が分かる名前を渡す",
875
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。href 指定時のリンク(removable のときは本体のリンク)に効く。href 付きでも onClick と aria-* / data-* などは <a> に渡る(遷移は止めない)。type / disabled / form* / name / value など button 専用の属性は渡さない(#236)",
876
+ "removable を指定するときは、削除ボタンに onClick を付けるため React Server Components では Server Component から描けない(実行時に Event handlers cannot be passed to Client Component props で落ちる。linkComponent の有無によらない)。Next.js App Router では呼び出し側のファイルを \"use client\" にする。removable でなければ Server Component のまま使える(#264)"
877
+ ],
878
+ "props": [
879
+ "removable?: boolean",
880
+ "onRemove?: () => void",
881
+ "removeLabel?: string"
882
+ ],
883
+ "accessibility": [
884
+ "forced-colors(Windows のハイコントラスト)では選択中(selected)を Highlight の地 + HighlightText で示す(button / リンク / removable 共通)。通常表示は変わらない"
699
885
  ]
700
886
  },
701
887
  {
@@ -748,7 +934,12 @@
748
934
  "検索が必要な単一選択に使う。少数候補はSelectまたはRadioGroupを使う",
749
935
  "Popover + Command の検索・キーボード操作を内包する",
750
936
  "制御コンポーネント。value と onValueChange を必ず渡す(非制御では選択が反映されない)",
751
- "FormField と繋ぐときは id を FieldLabel の htmlFor と揃え、エラーは aria-invalid / aria-describedby で FieldError に接続する"
937
+ "FormField の children に置けば id / aria-labelledby / aria-describedby / aria-invalid を自動で受け取る(FormField の error で aria-invalid=true)。FormField を使わないときは id / aria-invalid / aria-describedby を明示して FieldError に接続する"
938
+ ],
939
+ "accessibility": [
940
+ "aria-invalid のときトリガーに赤 1px(Border-Caution)の内側リング(inset-ring)を出す。トリガーは枠幅 0 の Button tertiary なので border では描かない(高さは変わらない)。キーボードフォーカス時は外側のフォーカスリングと同時に表示される",
941
+ "forced-colors(Windows のハイコントラスト)では box-shadow が消えるため、aria-invalid のときだけ CanvasText の輪郭線(outline。レイアウトに影響しない)で境界を出す。通常表示は変わらない",
942
+ "トリガーは枠を持たない。高コントラストのときだけ内側へ 1px の輪郭(HIGH_CONTRAST_OUTLINE・--Border-Control)が出て 3:1 以上になる(エラー時は輪郭を透明にし、赤の内側リングをそのまま見せる)。トリガーに border を足して直さない"
752
943
  ]
753
944
  },
754
945
  {
@@ -760,7 +951,8 @@
760
951
  ],
761
952
  "rules": [
762
953
  "ページ最大幅と左右paddingの唯一の担い手。これを使う親(Section等)は自身で左右paddingを持たない(二重がけ防止)",
763
- "縦余白・背景色は持たない(Sectionの責務)"
954
+ "縦余白・背景色は持たない(Sectionの責務)",
955
+ "gutter の幅を CSS 変数 --Container-Gutter で子孫に渡す(default: SP 16px / md 以上 24px、tight: 12px / 16px、none: 0)。ScrollRow と ContentCarousel・ImageCarousel の peek がこれを読んで、Container の外へはみ出しつつ端の余白を gutter にそろえる。consumer が自前で横スクロール行を組むときも -mx-4 px-4 を直書きせずこの変数を使う"
764
956
  ]
765
957
  },
766
958
  {
@@ -777,7 +969,13 @@
777
969
  "ReactNode スライド対応(slides: ReactNode[])。テキスト主体のヒーローバナー用",
778
970
  "itemsPerView=2|3|4 で PC の複数カード表示に対応。SP は1枚表示を維持し、EC 商品以外の求人・記事カード等にも使う",
779
971
  "画像だけのバナーは ImageCarousel を使う",
780
- "矢印・ドットの意匠は ImageCarousel と同一"
972
+ "矢印・ドットの意匠は ImageCarousel と同一",
973
+ "peek(SP で隣をのぞかせる)は Container の直下(左右の余白を足さない親の中)に置く。Container の gutter 分だけ外へはみ出して Container の外縁(SP では画面端)までスクロールし、1 枚目の左端は見出しとそろう。外側に -mx-4 等を足さない(二重にはみ出し、ページが横に伸びる)。Card など余白を持つ親の中に peek で置かない。Container の外(全幅)に置いたときの端の余白は 16px",
974
+ "peek の端の余白は scroll-padding も同じ幅なので、スワイプ・ドット・自動再生で移動した後も端に貼り付かず、最後のスライドの右にも余白が残る(issue #320)"
975
+ ],
976
+ "accessibility": [
977
+ "autoPlay は、動きを減らす設定(prefers-reduced-motion: reduce)のときは自動再生しない。矢印・ドットでの移動もスムーズスクロールせず瞬時に切り替える。停止ボタンは持たない",
978
+ "丸いボタンの枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く(既定の見た目は変わらない)"
781
979
  ]
782
980
  },
783
981
  {
@@ -792,7 +990,19 @@
792
990
  "trigger の選択値は M月D日(曜日)表記、カレンダーは日本語ロケール",
793
991
  "選択範囲は min/max、個別不可日は disabledDates で指定する",
794
992
  "DateTimePicker は JST 前提で Date をそのまま扱う(TZ 変換しない)",
795
- "FormField の children に置き、id を htmlFor と揃えて label/エラーと接続する"
993
+ "FormField の children に置くだけで trigger が id(label の htmlFor と一致)・aria-labelledby(FormField のラベル + 表示中の日付 / placeholder)・aria-describedby(説明・エラー)・aria-invalid(error 時 true)を受け取る。id の手書きは不要(明示した id / aria-invalid が優先、aria-describedby は連結)"
994
+ ],
995
+ "props": [
996
+ "id?: string",
997
+ "aria-invalid?: boolean",
998
+ "aria-describedby?: string"
999
+ ],
1000
+ "accessibility": [
1001
+ "aria-invalid のとき trigger の枠は Input / Select と同じ Border-Caution",
1002
+ "clearable の × で値を消すと、× が消える前にフォーカスを trigger へ戻す(body に落ちない)",
1003
+ "読み上げ名は FormField 配下で「ラベル + 表示中の日付 / placeholder」(例「お届け希望日 8月1日(土)」)。FormField の外では従来どおり表示中の日付 / placeholder(aria-label)",
1004
+ "DateTimePicker の時・分 Select は FormField 配下で名前が「ラベル + 時 / 分」になり、id を別に採番する。FormField の外では id を付けない",
1005
+ "DateTimePicker の時・分 Select にも aria-invalid / aria-describedby が届く"
796
1006
  ]
797
1007
  },
798
1008
  {
@@ -857,11 +1067,13 @@
857
1067
  "Label+Input+エラーメッセージをセットで管理",
858
1068
  "グリッド横並び時は descriptionPosition='below' で高さを揃える",
859
1069
  "npm 経由では CakeFormField として import する(foundations/form.tsx の FormField と名前が衝突するため別名で export)",
860
- "ラベルの id を配下のコントロールへ自動で渡す。Select / Switch / Checkbox(Radix の <button> トリガー)は aria-labelledby、Input / Textarea は id ↔ htmlFor で関連付く。description / error があれば aria-describedby も付く(issue #202)",
861
- "そのため Radix ベースの入力を使うときは FormField でラップするのが既定。ラップしない単体利用では日本語の aria-label を利用側で書く(<label htmlFor> は <button> に名前を与えない)"
1070
+ "ラベルの id を配下のコントロールへ自動で渡す。Select / Switch / Checkbox(Radix の <button> トリガー)は aria-labelledby、Input / Textarea は id ↔ htmlFor で関連付く。DatePicker / DateTimePicker は aria-labelledby で「ラベル + 表示中の値」、NumberInput は label 未指定なら FormField のラベルが名前になる。description / error があれば aria-describedby も付き、error があれば aria-invalid=\"true\" も付く(Input / Textarea / Select / Combobox / MultiSelect / DatePicker / DateTimePicker / NumberInput / Switch / label なしの Checkbox。issue #202・KSK #640)",
1071
+ "Radix ベースの単一入力を使うときは FormField の aria-labelledby を使う。1つのFormFieldに複数選択肢を置く場合は各コントロールを native label に関連付けるか、日本語の aria-label / aria-labelledby を明示する。明示ARIAは自動のフィールド名より優先し、description / error は aria-describedby で関連付く"
862
1072
  ],
863
1073
  "accessibility": [
864
- "利用側が aria-label / aria-labelledby / id を明示した場合は必ずそちらが優先される(二重命名を避ける)"
1074
+ "利用側が aria-label / aria-labelledby / id を明示した場合は必ずそちらが優先される(二重命名を避ける)",
1075
+ "aria-describedby は連結する: FormField の説明・エラーの id と利用側が渡した aria-describedby を空白区切りでつなぐ(重複 id は除く)。利用側が指定してもエラー文が読み上げから落ちない",
1076
+ "aria-invalid は利用側の明示値(aria-invalid={false} を含む)が優先。明示が無いときだけ FormField の error の有無で決まり、error が無ければ属性自体を付けない"
865
1077
  ]
866
1078
  },
867
1079
  {
@@ -899,7 +1111,12 @@
899
1111
  "「その他」のラベルは navOverflowLabel で差し替えられる(既定 その他)。ハンバーガーを自前で組まない",
900
1112
  "HeaderAction の showLabel=true でアイコン+可視ラベルのピル型(h-10・枠線)になる。「ログイン」等アイコンだけでは伝わらない導線に使い、consumer 側でアイコンにテキストを差し込まない(#245)",
901
1113
  "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)"
1114
+ "本文を Container size=\"page\" で中央寄せしているサイトは containerSize=\"page\" を指定する(Container と同じ段: page / wide / fluid)。背景・border・sticky は全幅のまま中身だけ中央寄せされ、ロゴ・ナビの左端が本文と揃う。consumer 側で className に max-w を当てると背景と線まで中央幅になり両脇が抜けるので使わない(issue #234)",
1115
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。navItems の href(「その他」メニュー内のリンクを含む)と actions の href に効く。logo は ReactNode をそのまま描くだけで Header は <a> を足さないため対象外(ロゴをリンクにするなら logo に <Link> を含めて渡す)。通常表示のナビ項目の現在地(isActive)には aria-current=page が付く(「その他」メニュー内のリンクと同じ)。href 付きの項目でも onClick は呼ばれる(遷移は止めない)(#236)",
1116
+ "この部品は 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)"
1117
+ ],
1118
+ "accessibility": [
1119
+ "丸いボタンの枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く(既定の見た目は変わらない)"
903
1120
  ]
904
1121
  },
905
1122
  {
@@ -909,7 +1126,19 @@
909
1126
  "scroll-snap",
910
1127
  "dots",
911
1128
  "arrows",
912
- "autoPlay"
1129
+ "autoPlay",
1130
+ "peek"
1131
+ ],
1132
+ "rules": [
1133
+ "peek(SP で隣をのぞかせる)は ContentCarousel と同じ。Container の直下に置くと gutter 分だけ外へはみ出して Container の外縁(SP では画面端)までスクロールし、1 枚目の左端は見出しとそろう。外側に -mx-4 等を足さない。Card など余白を持つ親の中に peek で置かない。Container の外(全幅)に置いたときの端の余白は 16px。scroll-padding も同じ幅なので、移動後・最後のスライドでも端の余白が残る(issue #320)",
1134
+ "peek と centeredSlides を併用すると、タブレット(コンテナ幅 768〜959px)では centeredSlides の表示(Container からはみ出さず中央に 1 枚)になる",
1135
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。images[].href のリンクに効く(#236)",
1136
+ "この部品は 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)"
1137
+ ],
1138
+ "accessibility": [
1139
+ "autoPlay は、動きを減らす設定(prefers-reduced-motion: reduce)のときは自動再生しない。矢印・ドットでの移動もスムーズスクロールせず瞬時に切り替える。停止ボタンは持たない",
1140
+ "フォーカスをリングだけで示す。色は FOCUS_RING_ONLY(内側に描くなら FOCUS_RING_INSET)を使い、ring-[var(--Focus-High-Emphasis)]/50 を直書きしない。高コントラストのときだけ不透明なリング(--Focus-Ring-Control・3:1 以上)に切り替わる",
1141
+ "丸いボタンの枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く(既定の見た目は変わらない)"
913
1142
  ]
914
1143
  },
915
1144
  {
@@ -970,6 +1199,12 @@
970
1199
  "delete",
971
1200
  "grid"
972
1201
  ],
1202
+ "accessibility": [
1203
+ "hover がない端末では削除ボタンを常時表示し、44pxの操作域を確保する。PCでもキーボードフォーカス時に削除操作を表示する",
1204
+ "追加・削除ボタンはフォームの送信を起こさない",
1205
+ "追加ボタンのフォーカスは FOCUS_RING_INSET。高コントラストのときだけ不透明なリング(--Focus-Ring-Control)に切り替わる",
1206
+ "破線の追加ボタンの枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く"
1207
+ ],
973
1208
  "rules": [
974
1209
  "複数枚の画像を並べて管理するグリッド。ファイル選択の口は持たず、追加はonAddでconsumerが実装する",
975
1210
  "画像1枚の入力(プレビュー+検証+値のバインド)はImageInputを使う"
@@ -984,7 +1219,15 @@
984
1219
  },
985
1220
  {
986
1221
  "name": "ListItem",
987
- "path": "src/components/primitives/list-item.tsx"
1222
+ "path": "src/components/primitives/list-item.tsx",
1223
+ "rules": [
1224
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。href 指定時の <a> だけが対象。onClick・aria-*・フォーカスリングは linkComponent 経由でも <a> に付く(#236)",
1225
+ "この部品は 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)",
1226
+ "フォーカスのリング・輪郭線は行の内側に描く(ring-inset / forced-colors は内向きの輪郭線)。overflow-hidden の角丸グループ(カード内に並べた行)に入れても左右が切り取られない。href あり(リンク)はフォーカスを受ける <a> が不透明な行 div に覆われるため、<a> は group/list-item + outline-none にして、リング・輪郭線は行 div に group-focus-visible/list-item: で描く(<a> に inset で付けると行の地の下に隠れて見えない)。静止時の見た目は変わらない"
1227
+ ],
1228
+ "accessibility": [
1229
+ "フォーカスをリングだけで示す。色は FOCUS_RING_ONLY(内側に描くなら FOCUS_RING_INSET)を使い、ring-[var(--Focus-High-Emphasis)]/50 を直書きしない。高コントラストのときだけ不透明なリング(--Focus-Ring-Control・3:1 以上)に切り替わる"
1230
+ ]
988
1231
  },
989
1232
  {
990
1233
  "name": "MenuDrawer",
@@ -1010,7 +1253,14 @@
1010
1253
  "3件以上を複数選択させる、または候補が多い場合に使う。少数候補はCheckboxGroupを使う",
1011
1254
  "選択済みは削除可能なChipで表示する",
1012
1255
  "制御コンポーネント。value と onValueChange を必ず渡す(非制御では選択が反映されない)",
1013
- "FormField と繋ぐときは id を FieldLabel の htmlFor と揃え、エラーは aria-invalid / aria-describedby で FieldError に接続する"
1256
+ "FormField の children に置けば id / aria-labelledby / aria-describedby / aria-invalid を自動で受け取る(FormField の error で aria-invalid=true)。FormField を使わないときは id / aria-invalid / aria-describedby を明示して FieldError に接続する"
1257
+ ],
1258
+ "accessibility": [
1259
+ "aria-invalid のときトリガーに赤 1px(Border-Caution)の内側リング(inset-ring)を出す。トリガーは枠幅 0 の Button tertiary なので border では描かない(高さは変わらない)。キーボードフォーカス時は外側のフォーカスリングと同時に表示される",
1260
+ "選択済み Chip を削除すると、フォーカスは次の Chip の削除ボタン → 無ければ直前の Chip → Chip が残らなければトリガーへ移る(body に落ちない)",
1261
+ "forced-colors(Windows のハイコントラスト)では box-shadow が消えるため、aria-invalid のときだけ CanvasText の輪郭線(outline。レイアウトに影響しない)で境界を出す。通常表示は変わらない",
1262
+ "選択済み Chip の削除ボタンの名前は「{項目のラベル}を削除」(Chip の removeLabel に渡す)",
1263
+ "トリガーは枠を持たない。高コントラストのときだけ内側へ 1px の輪郭(HIGH_CONTRAST_OUTLINE・--Border-Control)が出て 3:1 以上になる(エラー時は輪郭を透明にし、赤の内側リングをそのまま見せる)。トリガーに border を足して直さない"
1014
1264
  ]
1015
1265
  },
1016
1266
  {
@@ -1026,6 +1276,10 @@
1026
1276
  "layouts": [
1027
1277
  "vertical",
1028
1278
  "horizontal"
1279
+ ],
1280
+ "rules": [
1281
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。href を持つ通知だけが対象で、href の無い通知は従来どおり <div>(#236)",
1282
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
1029
1283
  ]
1030
1284
  },
1031
1285
  {
@@ -1090,7 +1344,8 @@
1090
1344
  "onDelete?: () => void",
1091
1345
  "formatOptions?: Intl.NumberFormatOptions",
1092
1346
  "locale?: string",
1093
- "inputId?: string"
1347
+ "inputId?: string",
1348
+ "label?: string"
1094
1349
  ],
1095
1350
  "rules": [
1096
1351
  "DSの数値入力はすべてこれを使う。生の input type=\"number\" は使わない(カート数量・価格・在庫・ポイント等すべて)",
@@ -1098,7 +1353,32 @@
1098
1353
  "カート数量では min=1 が標準(0 にしない — 削除は showTrash + onDelete で行う)",
1099
1354
  "既定は min=1 / max=99 とカート数量向け。価格・在庫など汎用の数値入力では min / max / step を必ず明示する",
1100
1355
  "桁区切り・通貨表示は formatOptions を渡す(編集中は素の数値になる)。JPY の全角¥は半角¥に正規化される",
1101
- "旧名 QuantitySelector は用途名だったため機能名に改名した。旧名は deprecated alias として残るが新規利用は不可"
1356
+ "旧名 QuantitySelector は用途名だったため機能名に改名した。旧名は deprecated alias として残るが新規利用は不可",
1357
+ "FormField の children に置くと、入力欄が id(label の htmlFor と一致)・aria-describedby(説明・エラー)・エラー状態(FormField の error。見た目の赤枠と aria-invalid)を受け取る。label を指定しなければ名前も FormField のラベルになる(入力欄・グループ = ラベル、増減ボタン =「ラベル 減らす / 増やす」)。明示した inputId / label / error が優先。FormField の外の DOM は変わらない",
1358
+ "高コントラストのとき、md は枠が --Border-Control(3:1 以上)に、sm は地の色を変えずに内側へ 1px の輪郭(HIGH_CONTRAST_OUTLINE)を足す。既定の見た目は変わらない"
1359
+ ],
1360
+ "accessibility": [
1361
+ "error のとき入力欄に aria-invalid=\"true\" を付ける(見た目の赤枠と同時)",
1362
+ "上限・下限に達した増減ボタンは native の disabled ではなく aria-disabled=\"true\"。押した直後にフォーカスを失って body に落ちないため。部品全体の disabled は native の disabled(Tab 順から外す)"
1363
+ ]
1364
+ },
1365
+ {
1366
+ "name": "ScrollRow",
1367
+ "path": "src/components/primitives/scroll-row.tsx",
1368
+ "props": [
1369
+ "bleed?: boolean(既定 true)",
1370
+ "snap?: boolean"
1371
+ ],
1372
+ "rules": [
1373
+ "カード・絵柄・タグ等を横 1 列に並べて横スクロールさせる行。overflow-x-auto の行を画面ごとに手組みしない(issue #320)",
1374
+ "Container の直下(左右の余白を足さない親の中)に置く。--Container-Gutter を読んで gutter 分だけ外へはみ出し(SP では画面端まで、PC でも gutter の中まで)、同じ幅の端の余白と scroll-padding を付けるので、1 枚目の左端が見出しとそろい、スナップ後・最後の要素の右にも余白が残る。外側に -mx-4 等を足さない",
1375
+ "Card など余白を持つ親の中、または Container の className で左右の余白を変えた中では bleed={false}(変数は Container の子孫すべてに届くため、そのままだと gutter 分はみ出す)。端の余白をやめたいときも className=\"px-0\" ではなく bleed={false}。Container の外に置いたときは、はみ出し・余白とも 0",
1376
+ "子要素は縮まない(shrink-0)。幅は子要素側で決める。snap 時の snap-start も含め子の class より優先される。間隔は 12px(gap-3)",
1377
+ "上下に 4px の余白(py-1)を持ち、同じだけ負のマージン(-my-1)で打ち消す。横スクロールの行は上下のはみ出しも切り取るため、子のフォーカスリングが切れないようにする",
1378
+ "カテゴリ(丸いアイコン+名前)の列は CategoryScroll、自動再生・ドット付きのバナーは ContentCarousel を使う"
1379
+ ],
1380
+ "accessibility": [
1381
+ "中にリンクやボタンが無い(キーボードで届かない)行は、tabIndex={0}・role=\"region\"・aria-label を渡してスクロール領域にフォーカスできるようにする(axe: scrollable-region-focusable)"
1102
1382
  ]
1103
1383
  },
1104
1384
  {
@@ -1116,7 +1396,9 @@
1116
1396
  "JS 無効環境・クローラでも動かす必要がある画面(SEO 重視の記事メディア等)は action を指定する。<form method=\"get\" action> として描画され、Enter も検索ボタンも素のフォーム送信になる(issue #237)",
1117
1397
  "action 指定時のクエリキーは name(既定 q)",
1118
1398
  "Input + Button で <form method=get> を手組みしない",
1119
- "searchHref は window.location を書き換える JS 前提の導線。action と併用しない(action が優先)"
1399
+ "searchHref は window.location を書き換える JS 前提の導線。action と併用しない(action が優先)",
1400
+ "枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わり、検索ボタンのリングは FOCUS_RING_INSET が不透明な --Focus-Ring-Control に切り替える",
1401
+ "検索ボタンは親の枠(border 1px)の内側いっぱいの高さ(h-full)。親が overflow-hidden なので、枠と同じ高さにすると上下 1px が切り取られ、内側のフォーカスのリング・輪郭線も 1px 切れる。切れていた分は元から見えないので静止時の見た目は変わらない"
1120
1402
  ]
1121
1403
  },
1122
1404
  {
@@ -1156,9 +1438,15 @@
1156
1438
  ],
1157
1439
  "rules": [
1158
1440
  "タイトル+もっと見るボタンをCardTitle+Button手書き禁止。SectionHeaderを使う",
1441
+ "「もっと見る」は補助リンクなのでブランド色を使わず、Text-Medium-Emphasis + 常時下線(hover で Text-High-Emphasis。Button の link variant と同じ意匠)で描く。自前で accent 色の「もっと見る」を組まない(brandColorUsage)",
1159
1442
  "中央寄せ大見出し(ランディング/ガイド系)は align=\"center\" を使う。eyebrow プロップに Badge 等を載せられる。手書きの中央寄せ見出し禁止",
1160
1443
  "3xl(28px)は通常ページの主見出しにも使う。4xl/5xlはLP・マーケティング系ヒーロー専用で、size=\"3xl\" に desktopSize=\"4xl\" または desktopSize=\"5xl\" を加えてPC時だけ拡大する",
1161
- "align=\"start\" では subtitle はタイトル行の下に全幅で置かれ、右側 action の下の空きも使って折り返す。moreHref 併用時にリンクになるのはタイトル行だけで、subtitle はリンクの外(issue #232)"
1444
+ "align=\"start\" では subtitle はタイトル行の下に全幅で置かれ、右側 action の下の空きも使って折り返す。moreHref 併用時にリンクになるのはタイトル行だけで、subtitle はリンクの外(issue #232)",
1445
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。moreHref を指定したときのタイトル行のリンクと「もっと見る」リンクの両方に効く(#236)",
1446
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
1447
+ ],
1448
+ "accessibility": [
1449
+ "丸いボタンの枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く(既定の見た目は変わらない)"
1162
1450
  ]
1163
1451
  },
1164
1452
  {
@@ -1200,7 +1488,9 @@
1200
1488
  "category": "navigation",
1201
1489
  "rules": [
1202
1490
  "ページ間遷移(別 URL へのナビゲーション)に使う。チェブロン付きで「別ページへ移動する」アフォーダンスを持つ",
1203
- "ページ内アンカー移動(href=\"#...\")に流用しない → SectionNav を使う(見た目と挙動の不一致を防ぐ)"
1491
+ "ページ内アンカー移動(href=\"#...\")に流用しない → SectionNav を使う(見た目と挙動の不一致を防ぐ)",
1492
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。この部品の項目に external フラグは無く、渡した href はすべて linkComponent で描かれる(外部 URL を出し分けたい場合は consumer 側でラップする)。項目の onClick(storyId 指定時の遷移の差し替え)と aria-current=page は linkComponent 経由でも保たれる(#236)",
1493
+ "この部品は全項目の <a> に onClick(storyId 指定時の遷移の差し替え)を付けるため、React Server Components では Server Component から描けない(実行時に Event handlers cannot be passed to Client Component props で落ちる。linkComponent の有無によらない)。Next.js App Router では呼び出し側のファイルを \"use client\" にする(#264)"
1204
1494
  ]
1205
1495
  },
1206
1496
  {
@@ -1231,6 +1521,9 @@
1231
1521
  "rules": [
1232
1522
  "ToggleGroup+Badge手書き禁止。Chip count propを使う",
1233
1523
  "activeIndex は初期値だけでなく、マウント後の外部変更にも表示が追従する"
1524
+ ],
1525
+ "accessibility": [
1526
+ "選択中のタブは Chip の selected と同じく、forced-colors(Windows のハイコントラスト)では Highlight の地で示す"
1234
1527
  ]
1235
1528
  },
1236
1529
  {
@@ -1257,8 +1550,23 @@
1257
1550
  {
1258
1551
  "name": "Tag",
1259
1552
  "path": "src/components/primitives/tag.tsx",
1553
+ "variants": [
1554
+ "default",
1555
+ "image",
1556
+ "caution",
1557
+ "sale",
1558
+ "warning",
1559
+ "info",
1560
+ "info-fill",
1561
+ "success",
1562
+ "accent",
1563
+ "timesale",
1564
+ "berry",
1565
+ "coupon"
1566
+ ],
1260
1567
  "rules": [
1261
- "表示専用。onClickを付けない(押せるものはChip)"
1568
+ "表示専用。onClickを付けない(押せるものはChip)",
1569
+ "ProductCardの画像上に置く配送タグはvariant=\"image\"を使い、画像の明暗に依存しない不透明背景にする"
1262
1570
  ]
1263
1571
  },
1264
1572
  {
@@ -1321,12 +1629,18 @@
1321
1629
  "ヘッダーの水平 padding(px-4 @md:px-6)は main の gutter(p-4 @md:p-6)と同値。片方だけ変えるとヘッダー内要素と h1 の左端がズレるので、変えるときは両方揃える",
1322
1630
  "既定値は props で差し替え可能で、骨格(Header + SideNav + Main)自体は他事業(ジョブボードの管理画面等)でも再利用できる",
1323
1631
  "a11y: DOM 先頭に SkipLink を標準内蔵し <main id> を制御できる(mainId 既定 main-content)。sr-only で視覚無影響。skipLink={false} で無効化",
1324
- "地がSurface-Secondaryなので、シェルが配下Cardのサーフェス文脈(--card-surface: Surface-Primary)を宣言する → Cardはsurface未指定のまま白で地から浮く(#150)。darkは据え置き"
1632
+ "地がSurface-Secondaryなので、シェルが配下Cardのサーフェス文脈(--card-surface: Surface-Primary)を宣言する → Cardはsurface未指定のまま白で地から浮く(#150)。darkは据え置き",
1633
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。サイドナビ(AdminSideNav)の項目・サブメニューとパンくずのリンクに効く(#236)",
1634
+ "この部品は 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)"
1325
1635
  ]
1326
1636
  },
1327
1637
  {
1328
1638
  "name": "AnniversaryCard",
1329
- "path": "src/components/ec/anniversary-card.tsx"
1639
+ "path": "src/components/ec/anniversary-card.tsx",
1640
+ "rules": [
1641
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。editHref 指定時の編集リンク(Button asChild 越しに描かれる)に効く(#236)",
1642
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
1643
+ ]
1330
1644
  },
1331
1645
  {
1332
1646
  "name": "CakeLogo",
@@ -1351,11 +1665,18 @@
1351
1665
  "layouts": [
1352
1666
  "vertical",
1353
1667
  "horizontal"
1668
+ ],
1669
+ "accessibility": [
1670
+ "獲得中(status=\"acquiring\")の回転アイコンは、動きを減らす設定(prefers-reduced-motion: reduce)では止まる"
1354
1671
  ]
1355
1672
  },
1356
1673
  {
1357
1674
  "name": "DeliveryCalendar",
1358
- "path": "src/components/ec/delivery-calendar.tsx"
1675
+ "path": "src/components/ec/delivery-calendar.tsx",
1676
+ "accessibility": [
1677
+ "日付は button で、選んだ日は aria-pressed=\"true\"(ほかの日は false)として読み上げにも伝わる",
1678
+ "forced-colors(Windows のハイコントラスト)では選んだ日を Highlight の地 + HighlightText(日付と ○△× も)で示す。通常表示は変わらない"
1679
+ ]
1359
1680
  },
1360
1681
  {
1361
1682
  "name": "MypageShell",
@@ -1376,7 +1697,9 @@
1376
1697
  "既定値は props で差し替え可能(menuGroups / breadcrumbs / bottomNavItems / headerActions)で、骨格(ヘッダー+2カラム+BottomTabBar)自体は他事業(ジョブボードのマイページ等)でも再利用できる",
1377
1698
  "breadcrumbs 省略時は「ホーム > {ユーザー}さまのマイページ > {title}」を自動生成する",
1378
1699
  "マイページトップ(Dashboard)はメインカラムを白背景で持たずセクションごとにカード化する別パターンなので、この shell を使わない",
1379
- "a11y: DOM 先頭に SkipLink を標準内蔵し <main id> を制御できる(mainId 既定 main-content)。sr-only で視覚無影響。skipLink={false} で無効化"
1700
+ "a11y: DOM 先頭に SkipLink を標準内蔵し <main id> を制御できる(mainId 既定 main-content)。sr-only で視覚無影響。skipLink={false} で無効化",
1701
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。PC ヘッダー(navItems / headerActions の href)・サイドメニュー・パンくず・SP の BottomTabBar の href に効く(#236)",
1702
+ "この部品は内部で Client Component の Header へ linkComponent を渡すため、React Server Components では Server Component から linkComponent を渡せない(実行時に Functions cannot be passed directly to Client Components で落ちる)。Next.js App Router では呼び出し側のファイルを \"use client\" にする。エラーメッセージは \"use server\" を示唆するが正解は逆(#264)。内部の SideNav が全項目にイベントハンドラを付けるため、linkComponent の有無によらず Server Component からは描けない"
1380
1703
  ]
1381
1704
  },
1382
1705
  {
@@ -1418,13 +1741,26 @@
1418
1741
  "captionは規格・仕様の短い自由記述(例: \"1kg / 冷蔵\")。価格表示の直下にtypo-body-xs / Text-Medium-Emphasisで表示。tags(配送方法等のenum variant)とは別物で代替不可",
1419
1742
  "カード幅は列(親グリッド/flex)が決める。カード自身は min-w-0 で、固定の min-width を持たない。自前のカードを組む場合もテキスト列は grid-template-columns: minmax(0, 1fr) にし、auto にしない(nowrapのメタ行のmin-content幅が列を押し広げ、サムネイルが列からはみ出す)",
1420
1743
  "商品名は 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)"
1744
+ "showCartButton のカートボタンは Button の outline 相当(白地 + 黒系の 1px 枠 + Text-High-Emphasis 文字。buttonVariants の outline / sm を高さ h-9 で使う)。一覧で1件ごとに繰り返す操作にブランド色のベタ塗りを使わないため(以前は Brand-Cake-Orange のベタ塗り)。ブランド色の主 CTA はページ側に1つだけ置く(brandColorUsage)",
1745
+ "hoverEffect でホバー時の画像演出を選ぶ: zoom / fade(拡大せず明るく・暗テーマ対応のため brightness を使う)/ none。未指定時の既定は orientation ごと(vertical=zoom / horizontal=none)で従来の見た目を維持し、明示指定すれば horizontal でも効く。consumer 側の CSS で scale を打ち消さない。Tailwind v4 の scale-* は transform ではなく scale プロパティのため打ち消しは壊れやすい(#244)",
1746
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。商品リンク(href・カード全体)とショップ名リンク(shopHref)の両方に効く。onCardClick・ショップ名の stopPropagation は linkComponent 経由でも保たれる(#236)",
1747
+ "shopHref(ショップ名リンクの stopPropagation)または showCartButton(カートボタンの onClick)を指定するときは、内部でイベントハンドラを付けるため React Server Components では Server Component から描けない(実行時に Event handlers cannot be passed to Client Component props で落ちる。linkComponent の有無によらない)。Next.js App Router では呼び出し側のファイルを \"use client\" にする。どちらも使わなければ Server Component のまま使える(#264)"
1422
1748
  ],
1423
- "variants": {}
1749
+ "variants": {},
1750
+ "accessibility": [
1751
+ "丸いボタンの枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く(既定の見た目は変わらない)"
1752
+ ]
1424
1753
  },
1425
1754
  {
1426
1755
  "name": "ProductCarousel",
1427
- "path": "src/components/ec/product-carousel.tsx"
1756
+ "path": "src/components/ec/product-carousel.tsx",
1757
+ "rules": [
1758
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。セクション画像リンク・「もっと見る」・各商品カード(商品とショップ名)に効く。products[].linkComponent を渡した商品はそちらを優先する(#236)",
1759
+ "showCartButton を指定するか、商品に shopHref を指定するときは、内部の ProductCard がイベントハンドラを付けるため React Server Components では Server Component から描けない(実行時に Event handlers cannot be passed to Client Component props で落ちる。linkComponent の有無によらない)。Next.js App Router では呼び出し側のファイルを \"use client\" にする。どちらも使わなければ Server Component のまま使える(#264)"
1760
+ ],
1761
+ "accessibility": [
1762
+ "フォーカスをリングだけで示す。色は FOCUS_RING_ONLY(内側に描くなら FOCUS_RING_INSET)を使い、ring-[var(--Focus-High-Emphasis)]/50 を直書きしない。高コントラストのときだけ不透明なリング(--Focus-Ring-Control・3:1 以上)に切り替わる"
1763
+ ]
1428
1764
  },
1429
1765
  {
1430
1766
  "name": "ProductCTA",
@@ -1440,12 +1776,18 @@
1440
1776
  "name": "ProductDetailBar",
1441
1777
  "path": "src/components/ec/product-detail-bar.tsx",
1442
1778
  "rules": [
1443
- "スティッキーヘッダー。タブ+評価表示"
1779
+ "スティッキーヘッダー。タブ+評価表示",
1780
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。タブのリンクに効く。タブの href は通常ページ内アンカー(#detail-slider 等)で、その用途では指定不要。別ページへ遷移するタブがあるときに使う(#236)",
1781
+ "この部品は全タブの <a> に onClick(onTabClick 用)を付けるため、React Server Components では Server Component から描けない(実行時に Event handlers cannot be passed to Client Component props で落ちる。linkComponent の有無によらない)。Next.js App Router では呼び出し側のファイルを \"use client\" にする(#264)"
1444
1782
  ]
1445
1783
  },
1446
1784
  {
1447
1785
  "name": "ProductDetailSidebar",
1448
- "path": "src/components/ec/product-detail-sidebar.tsx"
1786
+ "path": "src/components/ec/product-detail-sidebar.tsx",
1787
+ "rules": [
1788
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。ショップ名(shopHref)とブランド(brandHref)のリンクに効く(#236)",
1789
+ "sizeOptions を指定するときは、サイズの選択肢(Chip)に onClick を付けるため React Server Components では Server Component から描けない(実行時に Event handlers cannot be passed to Client Component props で落ちる。linkComponent の有無によらない)。Next.js App Router では呼び出し側のファイルを \"use client\" にする。sizeOptions を使わなければ Server Component のまま使える(#264)"
1790
+ ]
1449
1791
  },
1450
1792
  {
1451
1793
  "name": "ProductListItem",
@@ -1454,7 +1796,12 @@
1454
1796
  "rules": [
1455
1797
  "カート/注文履歴/レビュー variant を持つ商品リスト行",
1456
1798
  "captionは規格・仕様の短い自由記述(例: \"1kg / 冷蔵\")。価格表示の直下にtypo-body-xs / Text-Medium-Emphasisで表示。tags(配送方法等のenum variant)とは別物",
1457
- "subtotal/subtotalLabel(既定\"小計\")はcart variant専用。数量セレクタ行の右側(収まらなければ下に折り返し)に¥{カンマ区切り}で表示。BtoB/法人ECのロット購入等、商品ごとの単価×数量の内訳を明示したい場合に使う"
1799
+ "subtotal/subtotalLabel(既定\"小計\")はcart variant専用。数量セレクタ行の右側(収まらなければ下に折り返し)に¥{カンマ区切り}で表示。BtoB/法人ECのロット購入等、商品ごとの単価×数量の内訳を明示したい場合に使う",
1800
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。商品画像・商品名(href)とショップ名(shopHref)のリンクに効く(#236)",
1801
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
1802
+ ],
1803
+ "accessibility": [
1804
+ "丸いボタンの枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く(既定の見た目は変わらない)"
1458
1805
  ]
1459
1806
  },
1460
1807
  {
@@ -1475,6 +1822,10 @@
1475
1822
  "image",
1476
1823
  "candle",
1477
1824
  "letter"
1825
+ ],
1826
+ "accessibility": [
1827
+ "フォーカスをリングだけで示す。色は FOCUS_RING_ONLY(内側に描くなら FOCUS_RING_INSET)を使い、ring-[var(--Focus-High-Emphasis)]/50 を直書きしない。高コントラストのときだけ不透明なリング(--Focus-Ring-Control・3:1 以上)に切り替わる",
1828
+ "破線の追加ボタンの枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く"
1478
1829
  ]
1479
1830
  },
1480
1831
  {
@@ -1486,6 +1837,9 @@
1486
1837
  "features": [
1487
1838
  "sparkle-icon",
1488
1839
  "review-count"
1840
+ ],
1841
+ "rules": [
1842
+ "表示用の評価(星アイコンと数値)はブランド色を使わず Text-High-Emphasis(黒系)で描く。件数は Text-Low-Emphasis。入力用の星(ReviewForm の選択済み)はブランド色のまま(brandColorUsage)"
1489
1843
  ]
1490
1844
  },
1491
1845
  {
@@ -1512,6 +1866,10 @@
1512
1866
  "rating",
1513
1867
  "text",
1514
1868
  "image-upload"
1869
+ ],
1870
+ "accessibility": [
1871
+ "入力用の星(未選択)は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。色は text-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く",
1872
+ "破線の追加ボタンの枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く"
1515
1873
  ]
1516
1874
  },
1517
1875
  {
@@ -1532,6 +1890,10 @@
1532
1890
  "rating",
1533
1891
  "delivery-date",
1534
1892
  "bookmark"
1893
+ ],
1894
+ "rules": [
1895
+ "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。href 指定時のカード全体リンク(default)とカード本体(compact)に効く。href が無い compact は従来どおり <div>(#236)",
1896
+ "この部品は Server Component のまま使える。linkComponent を渡すためだけに、Next.js App Router の呼び出し側を \"use client\" にする必要はない(#264)"
1535
1897
  ]
1536
1898
  },
1537
1899
  {