@cakejp/design-system 0.20.1 → 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 +172 -0
  2. package/CLAUDE.md +5 -3
  3. package/MIGRATION.md +3 -3
  4. package/README.md +31 -11
  5. package/contracts/components.json +420 -63
  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 +87 -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 +6 -6
  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 +3051 -924
  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 +18 -16
  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.1",
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,7 +312,10 @@
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": [
244
321
  "DataTableCheckboxCell は Space またはセルのクリックで選択を一度だけ通知し、行の onClick へ伝播しない。Enter は Checkbox の標準仕様に従い選択を変えない。indeterminate からの操作は true を通知する",
@@ -258,8 +335,20 @@
258
335
  "DialogFooter",
259
336
  "DialogClose"
260
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
+ ],
261
348
  "rules": [
262
- "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)とは別物。値も意味も異なる"
263
352
  ]
264
353
  },
265
354
  {
@@ -271,7 +360,11 @@
271
360
  },
272
361
  {
273
362
  "name": "DropdownMenu",
274
- "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
+ ]
275
368
  },
276
369
  {
277
370
  "name": "Field",
@@ -328,7 +421,10 @@
328
421
  },
329
422
  {
330
423
  "name": "HoverCard",
331
- "path": "src/components/foundations/hover-card.tsx"
424
+ "path": "src/components/foundations/hover-card.tsx",
425
+ "rules": [
426
+ "中身の最大幅は Radix の available-width(画面内に使える幅)まで。長い項目や className で渡した広い幅でも、幅 390px の画面で右へはみ出さない(収まる幅・既定幅には効かない)"
427
+ ]
332
428
  },
333
429
  {
334
430
  "name": "IconBadge",
@@ -369,7 +465,11 @@
369
465
  },
370
466
  {
371
467
  "name": "Menubar",
372
- "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
+ ]
373
473
  },
374
474
  {
375
475
  "name": "NavigationMenu",
@@ -380,7 +480,9 @@
380
480
  "path": "src/components/foundations/pagination.tsx",
381
481
  "rules": [
382
482
  "linkComponent でリンク実装を差し替えられる(next/link 等)。未指定なら素の <a>。この部品の項目に external フラグは無く、渡した href はすべて linkComponent で描かれる(外部 URL を出し分けたい場合は consumer 側でラップする)(#236)",
383
- "この部品は 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 桁でもはみ出さない(幅はフォント次第)"
384
486
  ]
385
487
  },
386
488
  {
@@ -390,11 +492,17 @@
390
492
  "PopoverTrigger",
391
493
  "PopoverContent",
392
494
  "PopoverAnchor"
495
+ ],
496
+ "rules": [
497
+ "中身の最大幅は Radix の available-width(画面内に使える幅)まで。長い項目や className で渡した広い幅でも、幅 390px の画面で右へはみ出さない(収まる幅・既定幅には効かない)"
393
498
  ]
394
499
  },
395
500
  {
396
501
  "name": "Progress",
397
- "path": "src/components/foundations/progress.tsx"
502
+ "path": "src/components/foundations/progress.tsx",
503
+ "accessibility": [
504
+ "forced-colors(Windows のハイコントラスト)では進んだ部分を Highlight、全体の範囲を CanvasText の輪郭線(outline。レイアウトに影響しない)で示す。通常表示は変わらない"
505
+ ]
398
506
  },
399
507
  {
400
508
  "name": "RadioGroup",
@@ -408,7 +516,8 @@
408
516
  "chip"
409
517
  ],
410
518
  "accessibility": [
411
- "各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 と同じ)。通常表示は変わらない"
412
521
  ],
413
522
  "props": [
414
523
  "children: ラベル本文(必須。tile/chip 共通)",
@@ -419,14 +528,19 @@
419
528
  "variant='tile'(既定)=ラジオ丸+ラベル+補足説明+展開コンテンツのタイル型",
420
529
  "⚠️ タイルの枠(border/角丸/余白/選択状態の意匠)は RadioGroupItem 自身が描画する。consumer 側で <div className='border rounded-lg ...'> 等で囲むと枠が二重になる。囲まずにそのまま並べる",
421
530
  "ラベルは children で渡す。<RadioGroupItem/> と <Label htmlFor> を兄弟に並べる書き方は誤り(枠の二重化+ラベル重複を招く)",
422
- "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))] の形で書く"
423
533
  ]
424
534
  },
425
535
  {
426
536
  "name": "ResponsiveDialog",
427
537
  "path": "src/components/foundations/responsive-dialog.tsx",
538
+ "props": [
539
+ "ResponsiveDialogContent: size?: sm | md | lg | xl(未指定は従来の幅。PC の Dialog の幅の段で DialogContent の size と同じ)"
540
+ ],
428
541
  "rules": [
429
- "デスクトップ=Dialog、モバイル=Sheetを自動切替"
542
+ "デスクトップ=Dialog、モバイル=Drawer(下部のボトムシート)を自動切替",
543
+ "PC の Dialog の幅は ResponsiveDialogContent の size で選ぶ(sm 400px / md 512px / lg 640px / xl 800px。未指定は従来の幅)。SP の Drawer は全幅のボトムシートなので size は無視される(Drawer には渡らない)"
430
544
  ]
431
545
  },
432
546
  {
@@ -439,7 +553,9 @@
439
553
  "formBinding": "controller",
440
554
  "rules": [
441
555
  "<select>HTMLネイティブを使わない",
442
- "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 の輪郭線を出す)。通常表示は変わらない"
443
559
  ]
444
560
  },
445
561
  {
@@ -458,11 +574,20 @@
458
574
  "SheetFooter",
459
575
  "SheetClose"
460
576
  ],
577
+ "sizes": [
578
+ "sm",
579
+ "md",
580
+ "lg"
581
+ ],
461
582
  "props": [
462
583
  "SheetContent: side?: top | right | bottom | left",
584
+ "SheetContent: size?: sm | md | lg(既定 sm。side が left / right のときだけ効く)",
463
585
  "SheetContent: showCloseButton?: boolean",
464
586
  "SheetContent: autoFocus?: boolean"
465
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
+ ],
466
591
  "accessibility": [
467
592
  "SheetTitle は必須(ダイアログの読み上げ名)",
468
593
  "フォーカスは既定で自動移動する(開く=Sheet の中へ / 閉じる=トリガーへ戻る)。WCAG 2.4.3。autoFocus={false} は止める必要がある特別な場合だけ使う(issue #233)"
@@ -473,6 +598,9 @@
473
598
  "path": "src/components/foundations/skeleton.tsx",
474
599
  "rules": [
475
600
  "アニメーションはpulseのみ許可"
601
+ ],
602
+ "accessibility": [
603
+ "動きを減らす設定(prefers-reduced-motion: reduce)では明滅(pulse)を止め、面だけを残す。読み込み中であることは包んでいる領域の aria-busy や Spinner の文言で伝える"
476
604
  ]
477
605
  },
478
606
  {
@@ -499,7 +627,11 @@
499
627
  "accessibility": [
500
628
  "role=\"slider\" を持つのはつまみ側なので、名前はつまみに付ける。aria-label / aria-labelledby は Slider が各つまみへ転送する(issue #202)",
501
629
  "つまみが 2 つ以上(範囲指定)で aria-label を使うと、読み上げで区別できるよう「(下限)」「(上限)」が自動で補われる。文言を自分で決めたいときは thumbLabels を使う",
502
- "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 を満たす)"
503
635
  ]
504
636
  },
505
637
  {
@@ -519,14 +651,18 @@
519
651
  },
520
652
  {
521
653
  "name": "Spinner",
522
- "path": "src/components/foundations/spinner.tsx"
654
+ "path": "src/components/foundations/spinner.tsx",
655
+ "accessibility": [
656
+ "role=\"status\" と「読み込み中」の文言を持つ。動きを減らす設定(prefers-reduced-motion: reduce)では回転を止め、上辺だけ色付きの欠けた輪のまま処理中を示す"
657
+ ]
523
658
  },
524
659
  {
525
660
  "name": "Switch",
526
661
  "path": "src/components/foundations/switch.tsx",
527
662
  "formBinding": "controller",
528
663
  "accessibility": [
529
- "Label必須"
664
+ "Label必須",
665
+ "フォーカスをリングだけで示す。色は FOCUS_RING_ONLY(内側に描くなら FOCUS_RING_INSET)を使い、ring-[var(--Focus-High-Emphasis)]/50 を直書きしない。高コントラストのときだけ不透明なリング(--Focus-Ring-Control・3:1 以上)に切り替わる"
530
666
  ]
531
667
  },
532
668
  {
@@ -551,7 +687,17 @@
551
687
  ],
552
688
  "rules": [
553
689
  "サイドバー等の狭い場所に segment を置くときは TabsList size=\"sm\" を使う。既定は 1 項目 100px の最小幅を持ち、3 つ並べると 266px 幅ではみ出す(issue #235)",
554
- "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 で消す)。通常表示は変わらない"
555
701
  ]
556
702
  },
557
703
  {
@@ -572,19 +718,31 @@
572
718
  "rules": [
573
719
  "トースト内にアクションリンクを入れない。左ボーダーではなく背景色全体で状態を表現",
574
720
  "アプリのルートに <Toaster /> を1つ置き、表示は toast()(sonner の再 export)で呼ぶ"
721
+ ],
722
+ "accessibility": [
723
+ "loading トーストの回転アイコンは、動きを減らす設定(prefers-reduced-motion: reduce)では止まる"
575
724
  ]
576
725
  },
577
726
  {
578
727
  "name": "Toggle",
579
728
  "path": "src/components/foundations/toggle.tsx",
580
- "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
+ ]
581
734
  },
582
735
  {
583
736
  "name": "ToggleGroup",
584
737
  "path": "src/components/foundations/toggle-group.tsx",
585
738
  "formBinding": "controller",
586
739
  "rules": [
587
- "水平パディングはコンポーネント内に入れない。親が管理する"
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))] の形で書く"
588
746
  ]
589
747
  },
590
748
  {
@@ -622,18 +780,26 @@
622
780
  "折りたたみ(開閉)を持たせる場合、トリガーはサイドバー内ヘッダー行の右端に置く。コンテンツヘッダーの左端に置かない。動かす対象と操作する場所が離れると、そのボタンが何に属するか読めなくなる",
623
781
  "ナビ項目は項目高 44px(WCAG 2.5.5 の最小ターゲット)を確保する。メイン項目・サブメニュー項目とも min-h-11 で下限を固定する(行高の小さいテキストは縦 padding だけだと割り込む)",
624
782
  "PC のサブメニューは DropdownMenu の Portal / collision handling を使い、side=right / align=start / collisionPadding=8 で viewport 内に収める。絶対配置の flyout を手組みしない",
625
- "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)"
626
786
  ]
627
787
  },
628
788
  {
629
789
  "name": "BannerCarousel",
630
- "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
+ ]
631
795
  },
632
796
  {
633
797
  "name": "BottomTabBar",
634
798
  "path": "src/components/primitives/bottom-tab-bar.tsx",
635
799
  "rules": [
636
- "モバイル専用。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)"
637
803
  ]
638
804
  },
639
805
  {
@@ -647,12 +813,18 @@
647
813
  "name": "CategoryScroll",
648
814
  "path": "src/components/primitives/category-scroll.tsx",
649
815
  "rules": [
650
- "ルートは 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)"
651
819
  ]
652
820
  },
653
821
  {
654
822
  "name": "CategoryShortcuts",
655
- "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
+ ]
656
828
  },
657
829
  {
658
830
  "name": "ChatComposer",
@@ -680,7 +852,8 @@
680
852
  "IME変換中のEnterでは送信しない(日本語入力の誤送信防止)",
681
853
  "添付は内蔵のFileInputで選択し、選択済みはChipのremovableで表示・削除する",
682
854
  "送信中はisSubmittingを立てる(送信ボタン無効化で二重送信を防ぐ)",
683
- "<form>を内包しないため、フォーム内に置いても入れ子にならない"
855
+ "<form>を内包しないため、フォーム内に置いても入れ子にならない",
856
+ "フォーカス表示は枠(ラッパー)が FOCUS_RING_WITHIN で描く。forced-colors(Windows のハイコントラスト)では枠に輪郭線(outline)を出し、中の Textarea の輪郭線は二重にならないよう消す。通常表示は変わらない"
684
857
  ]
685
858
  },
686
859
  {
@@ -696,7 +869,19 @@
696
869
  ],
697
870
  "rules": [
698
871
  "フィルター・ステータスタブに使う。ToggleGroup+Badge手書き禁止",
699
- "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 共通)。通常表示は変わらない"
700
885
  ]
701
886
  },
702
887
  {
@@ -749,7 +934,12 @@
749
934
  "検索が必要な単一選択に使う。少数候補はSelectまたはRadioGroupを使う",
750
935
  "Popover + Command の検索・キーボード操作を内包する",
751
936
  "制御コンポーネント。value と onValueChange を必ず渡す(非制御では選択が反映されない)",
752
- "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 を足して直さない"
753
943
  ]
754
944
  },
755
945
  {
@@ -761,7 +951,8 @@
761
951
  ],
762
952
  "rules": [
763
953
  "ページ最大幅と左右paddingの唯一の担い手。これを使う親(Section等)は自身で左右paddingを持たない(二重がけ防止)",
764
- "縦余白・背景色は持たない(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 を直書きせずこの変数を使う"
765
956
  ]
766
957
  },
767
958
  {
@@ -778,7 +969,13 @@
778
969
  "ReactNode スライド対応(slides: ReactNode[])。テキスト主体のヒーローバナー用",
779
970
  "itemsPerView=2|3|4 で PC の複数カード表示に対応。SP は1枚表示を維持し、EC 商品以外の求人・記事カード等にも使う",
780
971
  "画像だけのバナーは ImageCarousel を使う",
781
- "矢印・ドットの意匠は 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))] の形で書く(既定の見た目は変わらない)"
782
979
  ]
783
980
  },
784
981
  {
@@ -793,7 +990,19 @@
793
990
  "trigger の選択値は M月D日(曜日)表記、カレンダーは日本語ロケール",
794
991
  "選択範囲は min/max、個別不可日は disabledDates で指定する",
795
992
  "DateTimePicker は JST 前提で Date をそのまま扱う(TZ 変換しない)",
796
- "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 が届く"
797
1006
  ]
798
1007
  },
799
1008
  {
@@ -858,11 +1067,13 @@
858
1067
  "Label+Input+エラーメッセージをセットで管理",
859
1068
  "グリッド横並び時は descriptionPosition='below' で高さを揃える",
860
1069
  "npm 経由では CakeFormField として import する(foundations/form.tsx の FormField と名前が衝突するため別名で export)",
861
- "ラベルの id を配下のコントロールへ自動で渡す。Select / Switch / Checkbox(Radix の <button> トリガー)は aria-labelledby、Input / Textarea は id ↔ htmlFor で関連付く。description / error があれば aria-describedby も付く(issue #202)",
862
- "そのため 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 で関連付く"
863
1072
  ],
864
1073
  "accessibility": [
865
- "利用側が 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 が無ければ属性自体を付けない"
866
1077
  ]
867
1078
  },
868
1079
  {
@@ -900,7 +1111,12 @@
900
1111
  "「その他」のラベルは navOverflowLabel で差し替えられる(既定 その他)。ハンバーガーを自前で組まない",
901
1112
  "HeaderAction の showLabel=true でアイコン+可視ラベルのピル型(h-10・枠線)になる。「ログイン」等アイコンだけでは伝わらない導線に使い、consumer 側でアイコンにテキストを差し込まない(#245)",
902
1113
  "searchBreakpoint で searchSlot の器を出し始めるコンテナ幅を選ぶ(md=768 / lg=960 の切替段のみ。xl=1280 は上限段でレイアウト切替に使わない)。狭い帯で検索を出さないときは consumer の CSS で中身を隠さずこの prop を使う。中身だけ隠しても器が flex-1 の幅を確保し、インラインナビを潰す(#243)",
903
- "本文を 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))] の形で書く(既定の見た目は変わらない)"
904
1120
  ]
905
1121
  },
906
1122
  {
@@ -910,7 +1126,19 @@
910
1126
  "scroll-snap",
911
1127
  "dots",
912
1128
  "arrows",
913
- "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))] の形で書く(既定の見た目は変わらない)"
914
1142
  ]
915
1143
  },
916
1144
  {
@@ -973,7 +1201,9 @@
973
1201
  ],
974
1202
  "accessibility": [
975
1203
  "hover がない端末では削除ボタンを常時表示し、44pxの操作域を確保する。PCでもキーボードフォーカス時に削除操作を表示する",
976
- "追加・削除ボタンはフォームの送信を起こさない"
1204
+ "追加・削除ボタンはフォームの送信を起こさない",
1205
+ "追加ボタンのフォーカスは FOCUS_RING_INSET。高コントラストのときだけ不透明なリング(--Focus-Ring-Control)に切り替わる",
1206
+ "破線の追加ボタンの枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く"
977
1207
  ],
978
1208
  "rules": [
979
1209
  "複数枚の画像を並べて管理するグリッド。ファイル選択の口は持たず、追加はonAddでconsumerが実装する",
@@ -989,7 +1219,15 @@
989
1219
  },
990
1220
  {
991
1221
  "name": "ListItem",
992
- "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
+ ]
993
1231
  },
994
1232
  {
995
1233
  "name": "MenuDrawer",
@@ -1015,7 +1253,14 @@
1015
1253
  "3件以上を複数選択させる、または候補が多い場合に使う。少数候補はCheckboxGroupを使う",
1016
1254
  "選択済みは削除可能なChipで表示する",
1017
1255
  "制御コンポーネント。value と onValueChange を必ず渡す(非制御では選択が反映されない)",
1018
- "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 を足して直さない"
1019
1264
  ]
1020
1265
  },
1021
1266
  {
@@ -1031,6 +1276,10 @@
1031
1276
  "layouts": [
1032
1277
  "vertical",
1033
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)"
1034
1283
  ]
1035
1284
  },
1036
1285
  {
@@ -1095,7 +1344,8 @@
1095
1344
  "onDelete?: () => void",
1096
1345
  "formatOptions?: Intl.NumberFormatOptions",
1097
1346
  "locale?: string",
1098
- "inputId?: string"
1347
+ "inputId?: string",
1348
+ "label?: string"
1099
1349
  ],
1100
1350
  "rules": [
1101
1351
  "DSの数値入力はすべてこれを使う。生の input type=\"number\" は使わない(カート数量・価格・在庫・ポイント等すべて)",
@@ -1103,7 +1353,32 @@
1103
1353
  "カート数量では min=1 が標準(0 にしない — 削除は showTrash + onDelete で行う)",
1104
1354
  "既定は min=1 / max=99 とカート数量向け。価格・在庫など汎用の数値入力では min / max / step を必ず明示する",
1105
1355
  "桁区切り・通貨表示は formatOptions を渡す(編集中は素の数値になる)。JPY の全角¥は半角¥に正規化される",
1106
- "旧名 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)"
1107
1382
  ]
1108
1383
  },
1109
1384
  {
@@ -1121,7 +1396,9 @@
1121
1396
  "JS 無効環境・クローラでも動かす必要がある画面(SEO 重視の記事メディア等)は action を指定する。<form method=\"get\" action> として描画され、Enter も検索ボタンも素のフォーム送信になる(issue #237)",
1122
1397
  "action 指定時のクエリキーは name(既定 q)",
1123
1398
  "Input + Button で <form method=get> を手組みしない",
1124
- "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 切れる。切れていた分は元から見えないので静止時の見た目は変わらない"
1125
1402
  ]
1126
1403
  },
1127
1404
  {
@@ -1161,9 +1438,15 @@
1161
1438
  ],
1162
1439
  "rules": [
1163
1440
  "タイトル+もっと見るボタンをCardTitle+Button手書き禁止。SectionHeaderを使う",
1441
+ "「もっと見る」は補助リンクなのでブランド色を使わず、Text-Medium-Emphasis + 常時下線(hover で Text-High-Emphasis。Button の link variant と同じ意匠)で描く。自前で accent 色の「もっと見る」を組まない(brandColorUsage)",
1164
1442
  "中央寄せ大見出し(ランディング/ガイド系)は align=\"center\" を使う。eyebrow プロップに Badge 等を載せられる。手書きの中央寄せ見出し禁止",
1165
1443
  "3xl(28px)は通常ページの主見出しにも使う。4xl/5xlはLP・マーケティング系ヒーロー専用で、size=\"3xl\" に desktopSize=\"4xl\" または desktopSize=\"5xl\" を加えてPC時だけ拡大する",
1166
- "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))] の形で書く(既定の見た目は変わらない)"
1167
1450
  ]
1168
1451
  },
1169
1452
  {
@@ -1205,7 +1488,9 @@
1205
1488
  "category": "navigation",
1206
1489
  "rules": [
1207
1490
  "ページ間遷移(別 URL へのナビゲーション)に使う。チェブロン付きで「別ページへ移動する」アフォーダンスを持つ",
1208
- "ページ内アンカー移動(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)"
1209
1494
  ]
1210
1495
  },
1211
1496
  {
@@ -1236,6 +1521,9 @@
1236
1521
  "rules": [
1237
1522
  "ToggleGroup+Badge手書き禁止。Chip count propを使う",
1238
1523
  "activeIndex は初期値だけでなく、マウント後の外部変更にも表示が追従する"
1524
+ ],
1525
+ "accessibility": [
1526
+ "選択中のタブは Chip の selected と同じく、forced-colors(Windows のハイコントラスト)では Highlight の地で示す"
1239
1527
  ]
1240
1528
  },
1241
1529
  {
@@ -1262,8 +1550,23 @@
1262
1550
  {
1263
1551
  "name": "Tag",
1264
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
+ ],
1265
1567
  "rules": [
1266
- "表示専用。onClickを付けない(押せるものはChip)"
1568
+ "表示専用。onClickを付けない(押せるものはChip)",
1569
+ "ProductCardの画像上に置く配送タグはvariant=\"image\"を使い、画像の明暗に依存しない不透明背景にする"
1267
1570
  ]
1268
1571
  },
1269
1572
  {
@@ -1326,12 +1629,18 @@
1326
1629
  "ヘッダーの水平 padding(px-4 @md:px-6)は main の gutter(p-4 @md:p-6)と同値。片方だけ変えるとヘッダー内要素と h1 の左端がズレるので、変えるときは両方揃える",
1327
1630
  "既定値は props で差し替え可能で、骨格(Header + SideNav + Main)自体は他事業(ジョブボードの管理画面等)でも再利用できる",
1328
1631
  "a11y: DOM 先頭に SkipLink を標準内蔵し <main id> を制御できる(mainId 既定 main-content)。sr-only で視覚無影響。skipLink={false} で無効化",
1329
- "地が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)"
1330
1635
  ]
1331
1636
  },
1332
1637
  {
1333
1638
  "name": "AnniversaryCard",
1334
- "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
+ ]
1335
1644
  },
1336
1645
  {
1337
1646
  "name": "CakeLogo",
@@ -1356,11 +1665,18 @@
1356
1665
  "layouts": [
1357
1666
  "vertical",
1358
1667
  "horizontal"
1668
+ ],
1669
+ "accessibility": [
1670
+ "獲得中(status=\"acquiring\")の回転アイコンは、動きを減らす設定(prefers-reduced-motion: reduce)では止まる"
1359
1671
  ]
1360
1672
  },
1361
1673
  {
1362
1674
  "name": "DeliveryCalendar",
1363
- "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
+ ]
1364
1680
  },
1365
1681
  {
1366
1682
  "name": "MypageShell",
@@ -1381,7 +1697,9 @@
1381
1697
  "既定値は props で差し替え可能(menuGroups / breadcrumbs / bottomNavItems / headerActions)で、骨格(ヘッダー+2カラム+BottomTabBar)自体は他事業(ジョブボードのマイページ等)でも再利用できる",
1382
1698
  "breadcrumbs 省略時は「ホーム > {ユーザー}さまのマイページ > {title}」を自動生成する",
1383
1699
  "マイページトップ(Dashboard)はメインカラムを白背景で持たずセクションごとにカード化する別パターンなので、この shell を使わない",
1384
- "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 からは描けない"
1385
1703
  ]
1386
1704
  },
1387
1705
  {
@@ -1423,13 +1741,26 @@
1423
1741
  "captionは規格・仕様の短い自由記述(例: \"1kg / 冷蔵\")。価格表示の直下にtypo-body-xs / Text-Medium-Emphasisで表示。tags(配送方法等のenum variant)とは別物で代替不可",
1424
1742
  "カード幅は列(親グリッド/flex)が決める。カード自身は min-w-0 で、固定の min-width を持たない。自前のカードを組む場合もテキスト列は grid-template-columns: minmax(0, 1fr) にし、auto にしない(nowrapのメタ行のmin-content幅が列を押し広げ、サムネイルが列からはみ出す)",
1425
1743
  "商品名は line-clamp-2 + min-h-[2lh] で常に2行分の高さを確保する。1行の商品名でも下に続く価格の縦位置が一覧内で揃う",
1426
- "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)"
1427
1748
  ],
1428
- "variants": {}
1749
+ "variants": {},
1750
+ "accessibility": [
1751
+ "丸いボタンの枠は高コントラストのとき --Border-Control(3:1 以上)に切り替わる。枠色は border-[var(--Border-Control,var(--Border-Medium-Emphasis))] の形で書く(既定の見た目は変わらない)"
1752
+ ]
1429
1753
  },
1430
1754
  {
1431
1755
  "name": "ProductCarousel",
1432
- "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
+ ]
1433
1764
  },
1434
1765
  {
1435
1766
  "name": "ProductCTA",
@@ -1445,12 +1776,18 @@
1445
1776
  "name": "ProductDetailBar",
1446
1777
  "path": "src/components/ec/product-detail-bar.tsx",
1447
1778
  "rules": [
1448
- "スティッキーヘッダー。タブ+評価表示"
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)"
1449
1782
  ]
1450
1783
  },
1451
1784
  {
1452
1785
  "name": "ProductDetailSidebar",
1453
- "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
+ ]
1454
1791
  },
1455
1792
  {
1456
1793
  "name": "ProductListItem",
@@ -1459,7 +1796,12 @@
1459
1796
  "rules": [
1460
1797
  "カート/注文履歴/レビュー variant を持つ商品リスト行",
1461
1798
  "captionは規格・仕様の短い自由記述(例: \"1kg / 冷蔵\")。価格表示の直下にtypo-body-xs / Text-Medium-Emphasisで表示。tags(配送方法等のenum variant)とは別物",
1462
- "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))] の形で書く(既定の見た目は変わらない)"
1463
1805
  ]
1464
1806
  },
1465
1807
  {
@@ -1480,6 +1822,10 @@
1480
1822
  "image",
1481
1823
  "candle",
1482
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))] の形で書く"
1483
1829
  ]
1484
1830
  },
1485
1831
  {
@@ -1491,6 +1837,9 @@
1491
1837
  "features": [
1492
1838
  "sparkle-icon",
1493
1839
  "review-count"
1840
+ ],
1841
+ "rules": [
1842
+ "表示用の評価(星アイコンと数値)はブランド色を使わず Text-High-Emphasis(黒系)で描く。件数は Text-Low-Emphasis。入力用の星(ReviewForm の選択済み)はブランド色のまま(brandColorUsage)"
1494
1843
  ]
1495
1844
  },
1496
1845
  {
@@ -1517,6 +1866,10 @@
1517
1866
  "rating",
1518
1867
  "text",
1519
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))] の形で書く"
1520
1873
  ]
1521
1874
  },
1522
1875
  {
@@ -1537,6 +1890,10 @@
1537
1890
  "rating",
1538
1891
  "delivery-date",
1539
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)"
1540
1897
  ]
1541
1898
  },
1542
1899
  {