@qijenchen/design-system 0.1.0-beta.85 → 0.1.0-beta.87

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 (481) hide show
  1. package/AGENTS.md +210 -0
  2. package/CLAUDE.md +17 -191
  3. package/cli-init.mjs +30 -2
  4. package/dist/components/Accordion/accordion.d.ts +2 -2
  5. package/dist/components/Accordion/accordion.d.ts.map +1 -1
  6. package/dist/components/Accordion/accordion.js.map +1 -1
  7. package/dist/components/Avatar/avatar.js +1 -1
  8. package/dist/components/Avatar/avatar.js.map +1 -1
  9. package/dist/components/BulkActionBar/bulk-action-bar.d.ts +1 -1
  10. package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
  11. package/dist/components/BulkActionBar/bulk-action-bar.js +3 -3
  12. package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
  13. package/dist/components/Button/button-group.d.ts +2 -2
  14. package/dist/components/Button/button-group.d.ts.map +1 -1
  15. package/dist/components/Button/button-group.js +2 -2
  16. package/dist/components/Button/button-group.js.map +1 -1
  17. package/dist/components/Button/button.d.ts +2 -1
  18. package/dist/components/Button/button.d.ts.map +1 -1
  19. package/dist/components/Button/button.js +1 -1
  20. package/dist/components/Button/button.js.map +1 -1
  21. package/dist/components/Carousel/carousel.d.ts +14 -8
  22. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  23. package/dist/components/Carousel/carousel.js +64 -37
  24. package/dist/components/Carousel/carousel.js.map +1 -1
  25. package/dist/components/Checkbox/checkbox-group.d.ts.map +1 -1
  26. package/dist/components/Checkbox/checkbox-group.js +21 -14
  27. package/dist/components/Checkbox/checkbox-group.js.map +1 -1
  28. package/dist/components/Checkbox/checkbox.d.ts +2 -2
  29. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  30. package/dist/components/Checkbox/checkbox.js +49 -10
  31. package/dist/components/Checkbox/checkbox.js.map +1 -1
  32. package/dist/components/Combobox/combobox.d.ts +15 -6
  33. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  34. package/dist/components/Combobox/combobox.js +13 -7
  35. package/dist/components/Combobox/combobox.js.map +1 -1
  36. package/dist/components/Command/command.d.ts.map +1 -1
  37. package/dist/components/Command/command.js +11 -2
  38. package/dist/components/Command/command.js.map +1 -1
  39. package/dist/components/DataTable/cell-registry.d.ts +5 -5
  40. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  41. package/dist/components/DataTable/cell-registry.js +41 -38
  42. package/dist/components/DataTable/cell-registry.js.map +1 -1
  43. package/dist/components/DataTable/column-types.d.ts +1 -17
  44. package/dist/components/DataTable/column-types.d.ts.map +1 -1
  45. package/dist/components/DataTable/column-types.js.map +1 -1
  46. package/dist/components/DataTable/data-table-column-visibility-panel.d.ts.map +1 -1
  47. package/dist/components/DataTable/data-table-column-visibility-panel.js +71 -64
  48. package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -1
  49. package/dist/components/DataTable/data-table-filter-panel.d.ts +0 -2
  50. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  51. package/dist/components/DataTable/data-table-filter-panel.js +3 -4
  52. package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
  53. package/dist/components/DataTable/data-table-interaction-layer.d.ts +2 -2
  54. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  55. package/dist/components/DataTable/data-table-sort-manager.js +2 -2
  56. package/dist/components/DataTable/data-table-sort-manager.js.map +1 -1
  57. package/dist/components/DataTable/data-table.d.ts +1 -1
  58. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  59. package/dist/components/DataTable/data-table.js +44 -32
  60. package/dist/components/DataTable/data-table.js.map +1 -1
  61. package/dist/components/DatePicker/date-picker.d.ts +3 -3
  62. package/dist/components/DatePicker/date-picker.js +4 -4
  63. package/dist/components/DatePicker/date-picker.js.map +1 -1
  64. package/dist/components/DescriptionList/description-list.d.ts +3 -3
  65. package/dist/components/DescriptionList/description-list.d.ts.map +1 -1
  66. package/dist/components/DescriptionList/description-list.js +6 -6
  67. package/dist/components/DescriptionList/description-list.js.map +1 -1
  68. package/dist/components/Dialog/dialog.d.ts +2 -2
  69. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  70. package/dist/components/Dialog/dialog.js.map +1 -1
  71. package/dist/components/DropdownMenu/dropdown-menu.d.ts +8 -3
  72. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  73. package/dist/components/DropdownMenu/dropdown-menu.js +7 -7
  74. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  75. package/dist/components/Field/field-context.d.ts +16 -10
  76. package/dist/components/Field/field-context.d.ts.map +1 -1
  77. package/dist/components/Field/field-context.js.map +1 -1
  78. package/dist/components/Field/field-types.d.ts +2 -2
  79. package/dist/components/Field/field-types.d.ts.map +1 -1
  80. package/dist/components/Field/field-types.js.map +1 -1
  81. package/dist/components/Field/field-wrapper.d.ts +20 -4
  82. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  83. package/dist/components/Field/field-wrapper.js +30 -31
  84. package/dist/components/Field/field-wrapper.js.map +1 -1
  85. package/dist/components/Field/field.d.ts +1 -1
  86. package/dist/components/Field/field.d.ts.map +1 -1
  87. package/dist/components/Field/field.js +25 -3
  88. package/dist/components/Field/field.js.map +1 -1
  89. package/dist/components/Field/use-form-validation.d.ts +1 -1
  90. package/dist/components/Field/use-form-validation.js.map +1 -1
  91. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  92. package/dist/components/FileItem/file-item.js +2 -5
  93. package/dist/components/FileItem/file-item.js.map +1 -1
  94. package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
  95. package/dist/components/FileUpload/file-upload.js +5 -1
  96. package/dist/components/FileUpload/file-upload.js.map +1 -1
  97. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  98. package/dist/components/FileViewer/file-viewer.js +2 -1
  99. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  100. package/dist/components/HoverCard/hover-card.d.ts +2 -2
  101. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  102. package/dist/components/HoverCard/hover-card.js +4 -2
  103. package/dist/components/HoverCard/hover-card.js.map +1 -1
  104. package/dist/components/InlineEdit/inline-edit.d.ts +23 -16
  105. package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -1
  106. package/dist/components/InlineEdit/inline-edit.js +104 -62
  107. package/dist/components/InlineEdit/inline-edit.js.map +1 -1
  108. package/dist/components/Input/input.d.ts +1 -1
  109. package/dist/components/Input/input.d.ts.map +1 -1
  110. package/dist/components/Input/input.js +6 -6
  111. package/dist/components/Input/input.js.map +1 -1
  112. package/dist/components/LinkInput/link-input.d.ts +5 -5
  113. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  114. package/dist/components/LinkInput/link-input.js +3 -3
  115. package/dist/components/LinkInput/link-input.js.map +1 -1
  116. package/dist/components/Notice/notice.d.ts +17 -1
  117. package/dist/components/Notice/notice.d.ts.map +1 -1
  118. package/dist/components/Notice/notice.js +7 -1
  119. package/dist/components/Notice/notice.js.map +1 -1
  120. package/dist/components/NumberInput/number-input.d.ts +1 -1
  121. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  122. package/dist/components/NumberInput/number-input.js +12 -1
  123. package/dist/components/NumberInput/number-input.js.map +1 -1
  124. package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts +1 -1
  125. package/dist/components/PeoplePicker/avatar-stack-overflow.js.map +1 -1
  126. package/dist/components/PeoplePicker/people-picker.d.ts +5 -5
  127. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  128. package/dist/components/PeoplePicker/people-picker.js +7 -5
  129. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  130. package/dist/components/PeoplePicker/person-display.d.ts +1 -1
  131. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  132. package/dist/components/ProfileCard/profile-card.d.ts +1 -1
  133. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  134. package/dist/components/RadioGroup/radio-group.d.ts +3 -3
  135. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  136. package/dist/components/RadioGroup/radio-group.js +1 -1
  137. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  138. package/dist/components/Rating/rating.d.ts.map +1 -1
  139. package/dist/components/Rating/rating.js +4 -0
  140. package/dist/components/Rating/rating.js.map +1 -1
  141. package/dist/components/ScrollArea/scroll-area.d.ts +3 -2
  142. package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
  143. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  144. package/dist/components/SegmentedControl/segmented-control.d.ts +4 -3
  145. package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
  146. package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
  147. package/dist/components/Select/select.d.ts +16 -6
  148. package/dist/components/Select/select.d.ts.map +1 -1
  149. package/dist/components/Select/select.js +17 -5
  150. package/dist/components/Select/select.js.map +1 -1
  151. package/dist/components/SelectMenu/select-menu.d.ts +17 -1
  152. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  153. package/dist/components/SelectMenu/select-menu.js +17 -3
  154. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  155. package/dist/components/SelectionControl/boolean-value.d.ts +1 -1
  156. package/dist/components/SelectionControl/boolean-value.js.map +1 -1
  157. package/dist/components/Sheet/sheet.d.ts +2 -2
  158. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  159. package/dist/components/Sheet/sheet.js.map +1 -1
  160. package/dist/components/Sidebar/sidebar.d.ts +1 -1
  161. package/dist/components/Sidebar/sidebar.js +2 -2
  162. package/dist/components/Sidebar/sidebar.js.map +1 -1
  163. package/dist/components/Slider/slider.d.ts.map +1 -1
  164. package/dist/components/Slider/slider.js +6 -3
  165. package/dist/components/Slider/slider.js.map +1 -1
  166. package/dist/components/Steps/steps.d.ts +6 -4
  167. package/dist/components/Steps/steps.d.ts.map +1 -1
  168. package/dist/components/Steps/steps.js +19 -5
  169. package/dist/components/Steps/steps.js.map +1 -1
  170. package/dist/components/Switch/switch.d.ts +4 -4
  171. package/dist/components/Switch/switch.d.ts.map +1 -1
  172. package/dist/components/Switch/switch.js +3 -1
  173. package/dist/components/Switch/switch.js.map +1 -1
  174. package/dist/components/Tabs/tabs.d.ts +1 -1
  175. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  176. package/dist/components/Tabs/tabs.js +73 -23
  177. package/dist/components/Tabs/tabs.js.map +1 -1
  178. package/dist/components/Tag/tag.d.ts +3 -1
  179. package/dist/components/Tag/tag.d.ts.map +1 -1
  180. package/dist/components/Tag/tag.js +2 -2
  181. package/dist/components/Tag/tag.js.map +1 -1
  182. package/dist/components/Textarea/textarea.d.ts +4 -4
  183. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  184. package/dist/components/Textarea/textarea.js +15 -15
  185. package/dist/components/Textarea/textarea.js.map +1 -1
  186. package/dist/components/TimePicker/time-picker.d.ts +4 -4
  187. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  188. package/dist/components/TimePicker/time-picker.js +8 -5
  189. package/dist/components/TimePicker/time-picker.js.map +1 -1
  190. package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
  191. package/dist/components/Tooltip/tooltip.js +27 -23
  192. package/dist/components/Tooltip/tooltip.js.map +1 -1
  193. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  194. package/dist/components/TreeView/tree-view.js +16 -0
  195. package/dist/components/TreeView/tree-view.js.map +1 -1
  196. package/dist/index.d.ts +7 -7
  197. package/dist/index.d.ts.map +1 -1
  198. package/dist/index.js +6 -13
  199. package/dist/index.js.map +1 -1
  200. package/dist/lib/drag-visual.d.ts +2 -2
  201. package/dist/lib/drag-visual.js.map +1 -1
  202. package/dist/lib/utils.d.ts +1 -0
  203. package/dist/lib/utils.d.ts.map +1 -1
  204. package/dist/lib/utils.js +21 -1
  205. package/dist/lib/utils.js.map +1 -1
  206. package/dist/patterns/element-anatomy/item-anatomy.d.ts +2 -0
  207. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  208. package/dist/patterns/element-anatomy/item-anatomy.js +2 -2
  209. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  210. package/dist/tokens/categorical-color.d.ts +3 -3
  211. package/dist/tokens/categorical-color.d.ts.map +1 -1
  212. package/dist/tokens/categorical-color.js +2 -2
  213. package/dist/tokens/categorical-color.js.map +1 -1
  214. package/ds-canonical/fork/AGENTS.md +213 -0
  215. package/ds-canonical/fork/codex/agents/skills/independent-review/SKILL.md +33 -0
  216. package/ds-canonical/fork/codex/hooks.json +16 -0
  217. package/ds-canonical/fork/governance.lock +22 -7
  218. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +22 -1
  219. package/ds-canonical/fork/hooks/inject_deploy_url_after_push.sh +14 -2
  220. package/ds-canonical/fork/manifest.json +10 -1
  221. package/ds-canonical/fork/preamble.md +3 -2
  222. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +7 -7
  223. package/ds-canonical/fork/skills/ux-audit/SKILL.md +1 -1
  224. package/ds-canonical/fork/skills/visual-audit/SKILL.md +0 -1
  225. package/ds-canonical/hooks/check_codex_brief_invariants.sh +9 -3
  226. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +22 -1
  227. package/ds-canonical/hooks/check_overlay_open_focus_escape_probe.sh +19 -2
  228. package/ds-canonical/hooks/check_plugin_fork_health.sh +17 -1
  229. package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +21 -4
  230. package/ds-canonical/hooks/inject_deploy_url_after_push.sh +14 -2
  231. package/ds-canonical/hooks/session_start_governance_check.sh +10 -2
  232. package/ds-canonical/references/certified-surfaces.md +20 -0
  233. package/ds-canonical/references/preflight-gate-baseline.json +5 -0
  234. package/ds-canonical/references/principle-dim-map.json +20 -2
  235. package/ds-canonical/references/props-naming.md +2 -1
  236. package/ds-canonical/references/ssot-index.md +2 -2
  237. package/ds-canonical/references/story-baseline-registry.json +2 -2
  238. package/ds-canonical/rules/story-rules.md +1 -1
  239. package/ds-canonical/rules/ui-development.md +2 -1
  240. package/ds-canonical/skills/codex-collab/SKILL.md +7 -7
  241. package/ds-canonical/skills/codex-collab/references/brief-template.md +5 -1
  242. package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
  243. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +5 -5
  244. package/ds-canonical/skills/design-system-audit/SKILL.md +5 -5
  245. package/ds-canonical/skills/product-ui-audit/SKILL.md +7 -7
  246. package/ds-canonical/skills/ux-audit/SKILL.md +1 -1
  247. package/ds-canonical/skills/visual-audit/SKILL.md +0 -1
  248. package/ds-story-manifest.json +21 -21
  249. package/llms-full.txt +3 -3
  250. package/llms.txt +1 -1
  251. package/package.json +2 -1
  252. package/src/components/Accordion/accordion.anatomy.stories.tsx +11 -11
  253. package/src/components/Accordion/accordion.principles.stories.tsx +21 -6
  254. package/src/components/Accordion/accordion.spec.md +10 -1
  255. package/src/components/Accordion/accordion.stories.tsx +10 -5
  256. package/src/components/Accordion/accordion.tsx +7 -2
  257. package/src/components/Alert/alert.principles.stories.tsx +2 -8
  258. package/src/components/AppShell/app-shell.anatomy.stories.tsx +19 -13
  259. package/src/components/AppShell/app-shell.principles.stories.tsx +21 -21
  260. package/src/components/AppShell/app-shell.spec.md +4 -0
  261. package/src/components/AppShell/app-shell.stories.tsx +4 -5
  262. package/src/components/Avatar/avatar.tsx +1 -1
  263. package/src/components/Badge/badge.spec.md +2 -2
  264. package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +7 -3
  265. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +4 -1
  266. package/src/components/Breadcrumb/breadcrumb.stories.tsx +1 -1
  267. package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +6 -6
  268. package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +18 -6
  269. package/src/components/BulkActionBar/bulk-action-bar.spec.md +15 -5
  270. package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +8 -24
  271. package/src/components/BulkActionBar/bulk-action-bar.tsx +4 -4
  272. package/src/components/Button/button-group.tsx +4 -4
  273. package/src/components/Button/button.anatomy.stories.tsx +26 -7
  274. package/src/components/Button/button.principles.stories.tsx +1 -1
  275. package/src/components/Button/button.spec.md +2 -2
  276. package/src/components/Button/button.stories.tsx +22 -28
  277. package/src/components/Button/button.tsx +2 -1
  278. package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
  279. package/src/components/Calendar/calendar.spec.md +1 -1
  280. package/src/components/Carousel/carousel.spec.md +17 -1
  281. package/src/components/Carousel/carousel.tsx +93 -42
  282. package/src/components/Checkbox/checkbox-group.tsx +26 -15
  283. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +3 -3
  284. package/src/components/Checkbox/checkbox.spec.md +12 -3
  285. package/src/components/Checkbox/checkbox.stories.tsx +4 -4
  286. package/src/components/Checkbox/checkbox.tsx +79 -19
  287. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +5 -5
  288. package/src/components/Coachmark/coachmark.principles.stories.tsx +1 -1
  289. package/src/components/Combobox/combobox.anatomy.stories.tsx +11 -4
  290. package/src/components/Combobox/combobox.principles.stories.tsx +10 -11
  291. package/src/components/Combobox/combobox.spec.md +6 -2
  292. package/src/components/Combobox/combobox.stories.tsx +9 -36
  293. package/src/components/Combobox/combobox.tsx +42 -27
  294. package/src/components/Command/command.spec.md +3 -3
  295. package/src/components/Command/command.tsx +9 -2
  296. package/src/components/DataTable/cell-registry.tsx +75 -76
  297. package/src/components/DataTable/column-types.ts +4 -17
  298. package/src/components/DataTable/data-table-column-visibility-panel.tsx +20 -7
  299. package/src/components/DataTable/data-table-filter-panel.tsx +6 -6
  300. package/src/components/DataTable/data-table-interaction-layer.tsx +3 -3
  301. package/src/components/DataTable/data-table-sort-manager.tsx +2 -2
  302. package/src/components/DataTable/data-table.anatomy.stories.tsx +25 -24
  303. package/src/components/DataTable/data-table.css +2 -3
  304. package/src/components/DataTable/data-table.principles.stories.tsx +17 -11
  305. package/src/components/DataTable/data-table.spec.md +8 -7
  306. package/src/components/DataTable/data-table.stories.tsx +39 -29
  307. package/src/components/DataTable/data-table.tsx +90 -67
  308. package/src/components/DataTable/filter-operators.spec.md +2 -0
  309. package/src/components/DateGrid/date-grid.spec.md +1 -1
  310. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +9 -9
  311. package/src/components/DatePicker/date-picker.principles.stories.tsx +4 -4
  312. package/src/components/DatePicker/date-picker.spec.md +10 -9
  313. package/src/components/DatePicker/date-picker.stories.tsx +9 -9
  314. package/src/components/DatePicker/date-picker.tsx +13 -13
  315. package/src/components/DescriptionList/description-list.anatomy.stories.tsx +5 -5
  316. package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
  317. package/src/components/DescriptionList/description-list.spec.md +12 -12
  318. package/src/components/DescriptionList/description-list.stories.tsx +3 -3
  319. package/src/components/DescriptionList/description-list.tsx +10 -10
  320. package/src/components/Dialog/dialog.tsx +4 -1
  321. package/src/components/DropdownMenu/dropdown-menu.tsx +27 -14
  322. package/src/components/Empty/empty.spec.md +0 -1
  323. package/src/components/Field/field-context.ts +17 -11
  324. package/src/components/Field/field-controls.spec.md +46 -37
  325. package/src/components/Field/field-types.ts +14 -12
  326. package/src/components/Field/field-wrapper.tsx +59 -43
  327. package/src/components/Field/field.anatomy.stories.tsx +6 -6
  328. package/src/components/Field/field.principles.stories.tsx +5 -28
  329. package/src/components/Field/field.spec.md +5 -4
  330. package/src/components/Field/field.stories.tsx +103 -72
  331. package/src/components/Field/field.tsx +36 -4
  332. package/src/components/Field/form-validation.spec.md +4 -0
  333. package/src/components/Field/use-form-validation.ts +1 -1
  334. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
  335. package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
  336. package/src/components/FileItem/file-item.tsx +10 -8
  337. package/src/components/FileUpload/file-upload.principles.stories.tsx +7 -7
  338. package/src/components/FileUpload/file-upload.spec.md +3 -2
  339. package/src/components/FileUpload/file-upload.tsx +5 -1
  340. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
  341. package/src/components/FileViewer/file-viewer.principles.stories.tsx +1 -1
  342. package/src/components/FileViewer/file-viewer.spec.md +1 -1
  343. package/src/components/FileViewer/file-viewer.stories.tsx +1 -1
  344. package/src/components/FileViewer/file-viewer.tsx +3 -2
  345. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -4
  346. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -1
  347. package/src/components/HoverCard/hover-card.spec.md +2 -2
  348. package/src/components/HoverCard/hover-card.tsx +4 -2
  349. package/src/components/InlineEdit/inline-edit.anatomy.stories.tsx +184 -0
  350. package/src/components/InlineEdit/inline-edit.principles.stories.tsx +142 -0
  351. package/src/components/InlineEdit/inline-edit.spec.md +71 -46
  352. package/src/components/InlineEdit/inline-edit.stories.tsx +20 -19
  353. package/src/components/InlineEdit/inline-edit.tsx +211 -125
  354. package/src/components/Input/input.anatomy.stories.tsx +15 -15
  355. package/src/components/Input/input.principles.stories.tsx +3 -3
  356. package/src/components/Input/input.spec.md +4 -4
  357. package/src/components/Input/input.stories.tsx +3 -3
  358. package/src/components/Input/input.tsx +13 -13
  359. package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -2
  360. package/src/components/LinkInput/link-input.principles.stories.tsx +1 -1
  361. package/src/components/LinkInput/link-input.spec.md +6 -2
  362. package/src/components/LinkInput/link-input.stories.tsx +8 -8
  363. package/src/components/LinkInput/link-input.tsx +21 -19
  364. package/src/components/Notice/notice.principles.stories.tsx +1 -1
  365. package/src/components/Notice/notice.tsx +8 -2
  366. package/src/components/NumberInput/number-input.anatomy.stories.tsx +6 -6
  367. package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
  368. package/src/components/NumberInput/number-input.spec.md +6 -6
  369. package/src/components/NumberInput/number-input.stories.tsx +4 -4
  370. package/src/components/NumberInput/number-input.tsx +17 -5
  371. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +8 -4
  372. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +6 -7
  373. package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
  374. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -30
  375. package/src/components/PeoplePicker/avatar-stack-overflow.ts +1 -1
  376. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +13 -10
  377. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +5 -1
  378. package/src/components/PeoplePicker/people-picker.stories.tsx +5 -5
  379. package/src/components/PeoplePicker/people-picker.tsx +39 -20
  380. package/src/components/PeoplePicker/person-display.tsx +3 -3
  381. package/src/components/Popover/popover.anatomy.stories.tsx +10 -7
  382. package/src/components/Popover/popover.principles.stories.tsx +49 -28
  383. package/src/components/Popover/popover.spec.md +4 -2
  384. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
  385. package/src/components/ProfileCard/profile-card.tsx +1 -1
  386. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +14 -13
  387. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +8 -23
  388. package/src/components/ProgressBar/progress-bar.stories.tsx +8 -2
  389. package/src/components/README.md +1 -0
  390. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +8 -3
  391. package/src/components/RadioGroup/radio-group.stories.tsx +3 -3
  392. package/src/components/RadioGroup/radio-group.tsx +19 -14
  393. package/src/components/Rating/rating.anatomy.stories.tsx +1 -1
  394. package/src/components/Rating/rating.principles.stories.tsx +3 -3
  395. package/src/components/Rating/rating.spec.md +2 -2
  396. package/src/components/Rating/rating.stories.tsx +4 -18
  397. package/src/components/Rating/rating.tsx +5 -1
  398. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
  399. package/src/components/ScrollArea/scroll-area.spec.md +1 -1
  400. package/src/components/ScrollArea/scroll-area.tsx +5 -2
  401. package/src/components/SegmentedControl/segmented-control.principles.stories.tsx +4 -1
  402. package/src/components/SegmentedControl/segmented-control.spec.md +5 -10
  403. package/src/components/SegmentedControl/segmented-control.tsx +6 -3
  404. package/src/components/Select/select.anatomy.stories.tsx +13 -7
  405. package/src/components/Select/select.principles.stories.tsx +3 -3
  406. package/src/components/Select/select.spec.md +10 -1
  407. package/src/components/Select/select.stories.tsx +41 -27
  408. package/src/components/Select/select.tsx +44 -26
  409. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +44 -26
  410. package/src/components/SelectMenu/select-menu.principles.stories.tsx +0 -1
  411. package/src/components/SelectMenu/select-menu.stories.tsx +7 -19
  412. package/src/components/SelectMenu/select-menu.tsx +38 -5
  413. package/src/components/SelectionControl/boolean-value.tsx +2 -2
  414. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
  415. package/src/components/SelectionControl/selection-item.principles.stories.tsx +4 -4
  416. package/src/components/Separator/separator.principles.stories.tsx +1 -1
  417. package/src/components/Sheet/sheet.principles.stories.tsx +5 -3
  418. package/src/components/Sheet/sheet.tsx +4 -1
  419. package/src/components/Sidebar/sidebar.tsx +1 -1
  420. package/src/components/Slider/slider.anatomy.stories.tsx +5 -4
  421. package/src/components/Slider/slider.principles.stories.tsx +6 -6
  422. package/src/components/Slider/slider.spec.md +13 -1
  423. package/src/components/Slider/slider.stories.tsx +44 -32
  424. package/src/components/Slider/slider.tsx +13 -4
  425. package/src/components/Steps/steps.anatomy.stories.tsx +7 -7
  426. package/src/components/Steps/steps.principles.stories.tsx +1 -1
  427. package/src/components/Steps/steps.spec.md +4 -2
  428. package/src/components/Steps/steps.stories.tsx +4 -56
  429. package/src/components/Steps/steps.tsx +31 -7
  430. package/src/components/Switch/switch.anatomy.stories.tsx +3 -3
  431. package/src/components/Switch/switch.principles.stories.tsx +1 -1
  432. package/src/components/Switch/switch.spec.md +4 -4
  433. package/src/components/Switch/switch.stories.tsx +3 -3
  434. package/src/components/Switch/switch.tsx +16 -10
  435. package/src/components/Tabs/tabs.anatomy.stories.tsx +6 -0
  436. package/src/components/Tabs/tabs.principles.stories.tsx +10 -2
  437. package/src/components/Tabs/tabs.spec.md +5 -2
  438. package/src/components/Tabs/tabs.stories.tsx +36 -44
  439. package/src/components/Tabs/tabs.tsx +101 -36
  440. package/src/components/Tag/tag.anatomy.stories.tsx +2 -2
  441. package/src/components/Tag/tag.principles.stories.tsx +4 -4
  442. package/src/components/Tag/tag.spec.md +4 -1
  443. package/src/components/Tag/tag.tsx +5 -3
  444. package/src/components/Textarea/textarea.anatomy.stories.tsx +6 -6
  445. package/src/components/Textarea/textarea.spec.md +5 -5
  446. package/src/components/Textarea/textarea.tsx +24 -24
  447. package/src/components/TimePicker/time-picker.anatomy.stories.tsx +12 -11
  448. package/src/components/TimePicker/time-picker.principles.stories.tsx +37 -44
  449. package/src/components/TimePicker/time-picker.spec.md +6 -5
  450. package/src/components/TimePicker/time-picker.stories.tsx +3 -3
  451. package/src/components/TimePicker/time-picker.tsx +24 -14
  452. package/src/components/Toast/toast.principles.stories.tsx +1 -1
  453. package/src/components/Toast/toast.stories.tsx +1 -1
  454. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +15 -6
  455. package/src/components/Tooltip/tooltip.principles.stories.tsx +4 -4
  456. package/src/components/Tooltip/tooltip.spec.md +9 -3
  457. package/src/components/Tooltip/tooltip.stories.tsx +1 -1
  458. package/src/components/Tooltip/tooltip.tsx +12 -2
  459. package/src/components/TreeView/tree-view.anatomy.stories.tsx +16 -14
  460. package/src/components/TreeView/tree-view.principles.stories.tsx +44 -11
  461. package/src/components/TreeView/tree-view.spec.md +8 -6
  462. package/src/components/TreeView/tree-view.stories.tsx +9 -43
  463. package/src/components/TreeView/tree-view.tsx +36 -0
  464. package/src/index.ts +13 -10
  465. package/src/lib/drag-visual.ts +2 -2
  466. package/src/lib/utils.ts +19 -0
  467. package/src/patterns/action-bar/action-bar.spec.md +1 -0
  468. package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
  469. package/src/patterns/element-anatomy/item-anatomy.spec.md +4 -0
  470. package/src/patterns/element-anatomy/item-anatomy.tsx +4 -2
  471. package/src/patterns/header-canonical/header-canonical.spec.md +9 -0
  472. package/src/patterns/header-canonical/header-canonical.stories.tsx +31 -5
  473. package/src/tokens/categorical-color.ts +5 -5
  474. package/src/tokens/color/color.spec.md +4 -4
  475. package/src/tokens/color/primitives.css +9 -2
  476. package/src/tokens/motion/motion.spec.md +6 -7
  477. package/src/tokens/opacity/opacity.spec.md +1 -1
  478. package/src/tokens/orphan-tokens.spec.md +1 -1
  479. package/src/tokens/typography/typography.spec.md +2 -0
  480. package/src/tokens/uiSize/uiSize.css +5 -0
  481. package/src/tokens/uiSize/uiSize.spec.md +1 -1
@@ -351,7 +351,7 @@ export const OrderRule: Story = {
351
351
  note="使用者眼睛最先掃到最上方,最希望被點擊的動作放第一個。所有按鈕撐滿容器寬度"
352
352
  >
353
353
  <div className="w-[200px]">
354
- <ButtonGroup direction="vertical">
354
+ <ButtonGroup orientation="vertical">
355
355
  <Button variant="primary">確認送出</Button>
356
356
  <Button variant="tertiary">取消</Button>
357
357
  </ButtonGroup>
@@ -273,7 +273,7 @@ const ICON_ONLY_BASE = 'aspect-square p-0 min-w-0 gap-0'
273
273
 
274
274
  完整 rationale(包含 2026-04-25 從 padding-formula 派切換的 bug origin)+ 4 家世界級對照 → **SSOT 在 `tokens/uiSize/uiSize.spec.md`「Icon-only 元件的 padding canonical」節**。本 spec 不重複,只 pointer。
275
275
 
276
- **icon-only 按鈕內建自動 tooltip:** 當 `iconOnly` 為 true 時,Button 自動以 `aria-label` 的值渲染 tooltip。開發者不需要額外包 `<Tooltip>`——tooltip 是元件保證的行為,不是開發者「記得要加」的外部組合。
276
+ **icon-only 按鈕內建自動 tooltip:** 當 `iconOnly` 為 true 且非 `asChild` 時,Button 自動以 `aria-label` 的值渲染 tooltip(`asChild` 因 Radix Slot 單 child 限制不包 Tooltip,consumer 需自包)。開發者不需要額外包 `<Tooltip>`——tooltip 是元件保證的行為,不是開發者「記得要加」的外部組合。
277
277
 
278
278
  `aria-label` 是給螢幕閱讀器的,tooltip 是給所有人看的,兩者語意相同,由同一個值驅動。
279
279
 
@@ -434,7 +434,7 @@ N/A(action trigger,無資料層)。
434
434
 
435
435
  ## A11y 預設
436
436
 
437
- **ARIA / Pattern**:預設渲染原生 `<button>`,自動繼承瀏覽器原生按鈕語意(button role / Enter / Space 觸發 / Tab 聚焦),無需額外 ARIA。`asChild` 時改用 Radix `Slot` 把 props 合併到 consumer child element(Slot 為純 prop-merger,a11y 仍由該 child 自負)。icon-only 模式傳入 `aria-label` 描述用途並自動包 Tooltip。`pressed` 定義時自動寫 `aria-pressed` + `data-state`。
437
+ **ARIA / Pattern**:預設渲染原生 `<button>`,自動繼承瀏覽器原生按鈕語意(button role / Enter / Space 觸發 / Tab 聚焦),無需額外 ARIA。`asChild` 時改用 Radix `Slot` 把 props 合併到 consumer child element(Slot 為純 prop-merger,a11y 仍由該 child 自負)。icon-only 模式傳入 `aria-label` 描述用途;**僅在非 `asChild` 時**自動包 Tooltip(`asChild` 因 Radix Slot 單 child 限制不包,consumer 需自管 child 的 `aria-label` 與 Tooltip)。`pressed` 定義時自動寫 `aria-pressed` + `data-state`。
438
438
 
439
439
  **Focus**:鍵盤聚焦時(`focus-visible`)顯示 focus ring(`focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1`,以 box-shadow 實作並 `outline-none`);滑鼠點擊不觸發。Button 為單一互動元件,**不含** focus trap / focus restoration(那是 Dialog / 浮層的概念)。
440
440
 
@@ -24,7 +24,7 @@ const meta: Meta<typeof Button> = {
24
24
  },
25
25
  danger: {
26
26
  control: 'boolean',
27
- description: '套用危險色(紅色)。僅 `primary` / `secondary` / `text` 支援(`tertiary` 視覺與 secondary+danger 重複、`link` 語義矛盾,見 spec 禁止事項)。',
27
+ description: '套用危險色(紅色)。僅 `primary` / `secondary` / `text` 支援(`tertiary` danger compound——會靜默渲染成一般 tertiary、並非等同 secondary+danger;`link` 語義矛盾。見 spec 禁止事項)。',
28
28
  },
29
29
  size: {
30
30
  control: 'select',
@@ -76,11 +76,11 @@ export const Default: Story = {
76
76
  name: '預設',
77
77
  render: () => (
78
78
  <div className="flex flex-wrap items-center gap-3">
79
- <Button variant="primary">主要動作</Button>
80
- <Button variant="secondary">次要動作</Button>
81
- <Button variant="tertiary">第三動作</Button>
82
- <Button variant="text">純文字</Button>
83
- <Button variant="link">連結樣式</Button>
79
+ <Button variant="primary">送出申請</Button>
80
+ <Button variant="secondary">儲存草稿</Button>
81
+ <Button variant="tertiary">取消</Button>
82
+ <Button variant="text">重設表單</Button>
83
+ <Button variant="link">查看填寫說明</Button>
84
84
  </div>
85
85
  ),
86
86
  }
@@ -240,26 +240,20 @@ export const Loading: Story = {
240
240
  <div className="flex flex-col gap-6">
241
241
  {/* 行為對照 */}
242
242
  <div>
243
- <p className="mb-2 text-caption text-fg-muted">行為對照:原始 → loading</p>
244
- <div className="flex flex-col gap-3">
245
- <div className="flex items-center gap-3">
246
- <Button variant="primary" startIcon={Download}>匯出</Button>
247
- <span className="text-caption text-fg-muted">→</span>
248
- <Button variant="primary" startIcon={Download} loading>匯出中</Button>
249
- <span className="text-caption text-fg-muted">startIcon 被 spinner 替換,位置不變</span>
250
- </div>
251
- <div className="flex items-center gap-3">
252
- <Button variant="primary">儲存</Button>
253
- <span className="text-caption text-fg-muted">→</span>
254
- <Button variant="primary" loading>儲存中</Button>
255
- <span className="text-caption text-fg-muted">spinner 出現在文字左側,按鈕略寬</span>
256
- </div>
257
- <div className="flex items-center gap-3">
258
- <Button size="sm" iconOnly startIcon={Download} aria-label="下載" />
259
- <span className="text-caption text-fg-muted">→</span>
260
- <Button size="sm" iconOnly startIcon={Download} loading aria-label="下載中" />
261
- <span className="text-caption text-fg-muted">icon-only:spinner 替換 icon</span>
262
- </div>
243
+ <p className="mb-2 text-caption text-fg-muted">行為對照:操作前與載入中</p>
244
+ <div className="grid grid-cols-[auto_auto_1fr] items-center gap-x-6 gap-y-3">
245
+ <span className="text-caption text-fg-muted">操作前</span>
246
+ <span className="text-caption text-fg-muted">載入中</span>
247
+ <span />
248
+ <Button variant="primary" startIcon={Download}>匯出</Button>
249
+ <Button variant="primary" startIcon={Download} loading>匯出中</Button>
250
+ <span className="text-caption text-fg-muted">startIcon 被 spinner 替換,位置不變</span>
251
+ <Button variant="primary">儲存</Button>
252
+ <Button variant="primary" loading>儲存中</Button>
253
+ <span className="text-caption text-fg-muted">spinner 出現在文字左側,按鈕略寬</span>
254
+ <Button size="sm" iconOnly startIcon={Download} aria-label="下載" />
255
+ <Button size="sm" iconOnly startIcon={Download} loading aria-label="下載中" />
256
+ <span className="text-caption text-fg-muted">icon-only:spinner 替換 icon</span>
263
257
  </div>
264
258
  </div>
265
259
 
@@ -325,8 +319,8 @@ export const HoverFocusState: Story = {
325
319
  tags: ['!autodocs'],
326
320
  render: () => (
327
321
  <div className="flex gap-4">
328
- <Button variant="primary" data-testid="hover-target">Hover 我</Button>
329
- <Button variant="primary" data-testid="focus-target">Focus 我</Button>
322
+ <Button variant="primary" data-testid="hover-target">查看帳單</Button>
323
+ <Button variant="primary" data-testid="focus-target">下載明細</Button>
330
324
  </div>
331
325
  ),
332
326
  play: async ({ canvasElement }) => {
@@ -228,6 +228,7 @@ const buttonVariants = cva(
228
228
  interface ButtonGroupContextValue {
229
229
  fullWidth?: boolean
230
230
  }
231
+ /** @internal — ButtonGroup↔Button 私有 context(fullWidth 注入);consumer 不直接 import,經 <ButtonGroup> 消費。root barrel 排除(subpath 仍可 wrap 後用)。 */
231
232
  const ButtonGroupContext = React.createContext<ButtonGroupContextValue>({})
232
233
 
233
234
  type InternalVariant = VariantProps<typeof buttonVariants>['variant']
@@ -581,7 +582,7 @@ export const buttonMeta = {
581
582
  // 2026-07-14 deep-audit 二次補齊(掃 cva 全 bg-* class):+ neutral-selected-hover / neutral-selected-active
582
583
  // (neutral pressedTone compound 消費)+ error / error-hover / error-active(primary+danger bg-error 系)
583
584
  bg: ['--primary', '--primary-hover', '--primary-active', '--primary-subtle', '--bg-disabled', '--surface', '--neutral-hover', '--neutral-active', '--neutral-selected', '--neutral-selected-hover', '--neutral-selected-active', '--error', '--error-hover', '--error-active'],
584
- fg: ['--on-emphasis', '--fg-disabled', '--foreground', '--primary', '--error', '--error-hover', '--error-active'],
585
+ fg: ['--on-emphasis', '--fg-disabled', '--foreground', '--primary', '--primary-hover', '--primary-active', '--error', '--error-hover', '--error-active'],
585
586
  ring: ['--ring'],
586
587
  },
587
588
  defaultVariant: 'tertiary', // 2026-06-10 修 stale:對齊 cva defaultVariants(2026-06-06 labeled 預設改 tertiary,meta 漏同步)
@@ -216,7 +216,7 @@ export const Accessibility = {
216
216
  render: () => (
217
217
  <div className="max-w-3xl text-body text-fg-secondary">
218
218
  <h3 className="text-h5 text-foreground mb-2">無障礙設計</h3>
219
- <p className="whitespace-pre-line">{"詳 `calendar.spec.md` 「A11y 預設」段。摘要:\n\n Grid role :月格容器 `role=\"grid\"` + `aria-label`(月份),每列 `role=\"row\"`(`display:contents` 保 CSS grid 佈局),每格 `role=\"gridcell\"`(非互動容器),日期數字按鈕帶 `aria-label`(日期 + 事件數)。事件 tile `role=\"button\"` + `aria-label`(事件標題)。\n\n Keyboard 行為(MVP 實作現況) :\n\n- Tab — 逐一 focus 每格的日期數字按鈕與其中的事件 tile(cell 為非互動 gridcell 容器,滑鼠點空白處等同點日期)\n- Enter / Space — 日期數字按鈕觸發 `onDateClick`;事件 tile 觸發 `onEventClick`\n- Toolbar 的 ◀ / 今天 / ▶ / 檢視切換為標準可聚焦控件,Tab 可達\n\n Keyboard tech debt(尚未實作,見 spec.md「MVP vs 後續增量」) :\n\n- ↑/↓/←/→ 在日期格間 roving 移動、PageUp/Down 切月、Shift+PageUp/Down 切年、Esc 關閉 — 隨週 / 日 view 增量一併補上 roving tabindex\n\n Focus :focus-visible ring 對齊 DS 設計準則( outline: 2px solid var(--ring) );日期數字按鈕與事件 tile 皆有 ring。\n\n 驗證 :Storybook a11y addon panel 應 0 critical violation。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。"}</p>
219
+ <p className="whitespace-pre-line">{"詳 `calendar.spec.md` 「A11y 預設」段。摘要:\n\n Grid role :月格容器 `role=\"grid\"` + `aria-label`(月份),每列 `role=\"row\"`(`display:contents` 保 CSS grid 佈局),每格 `role=\"gridcell\"`(非互動容器),日期數字按鈕帶 `aria-label`(日期 + 事件數)。事件 tile `role=\"button\"` + `aria-label`(事件標題)。\n\n Keyboard 行為(MVP 實作現況) :\n\n- Tab — 逐一 focus 每格的日期數字按鈕與其中的事件 tile(cell 為非互動 gridcell 容器,滑鼠點空白處等同點日期)\n- Enter / Space — 日期數字按鈕觸發 `onDateClick`;事件 tile 觸發 `onEventClick`\n- Toolbar 的 ◀ / 今天 / ▶ / 檢視切換為標準可聚焦控件,Tab 可達\n\n Keyboard tech debt(尚未實作,見 spec.md「MVP vs 後續增量」) :\n\n- ↑/↓/←/→ 在日期格間 roving 移動、PageUp/Down 切月、Shift+PageUp/Down 切年、Esc 關閉 — 隨週 / 日 view 增量一併補上 roving tabindex\n\n Focus :focus-visible ring 對齊 DS 設計準則(ring-2 ring-ring,box-shadow 實作 + outline-none);日期數字按鈕與事件 tile 皆有 ring。\n\n 驗證 :Storybook a11y addon panel 應 0 critical violation。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。"}</p>
220
220
  </div>
221
221
  ),
222
222
  }
@@ -267,6 +267,6 @@ MVP 無內建 error 狀態(無 `error` / `onRetry` prop)——載入失敗由 co
267
267
  - ↑/↓/←/→ 在日期格間 roving 移動、PageUp/Down 切月、Shift+PageUp/Down 切年、Esc 關閉 — 隨週 / 日 view 增量一併補上 roving tabindex
268
268
  - 此為**已知 a11y gap(非發佈 blocker)**:方向鍵 roving 是 APG grid pattern 建議;MVP 所有互動元素皆 Tab 可達(鍵盤可操作性已滿足),驗證基準見下方「驗證」
269
269
 
270
- **Focus**:focus-visible ring 對齊 DS canonical(`outline: 2px solid var(--ring)`);日期數字按鈕與事件 tile 皆有 ring。
270
+ **Focus**:focus-visible ring 對齊 DS canonical(`ring-2 ring-ring`,box-shadow 實作 + `outline-none`,同 button.spec A11y );日期數字按鈕與事件 tile 皆有 ring。
271
271
 
272
272
  **驗證**:Storybook a11y addon panel 應 0 critical violation。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。
@@ -128,7 +128,7 @@ Tabs 和 Carousel 都能「按順序切換下方內容」,但**語意與視覺
128
128
 
129
129
  ### 視覺規格
130
130
 
131
- - **基於 DS Button**:`<Button variant="tertiary" size="md" iconOnly />`——對齊 DS 規則,唯 shape 覆寫 `rounded-full`(media carousel documented 例外,見下一條「形狀 documented 例外」);其餘視覺全繼承 Button tertiary,不另自訂
131
+ - **基於 DS Button**:`<Button variant="tertiary" size="md" iconOnly />`(variant/size 為 **default,可覆寫**——見「Arrow Props」)——對齊 DS 規則,唯 shape 覆寫 `rounded-full`(media carousel documented 例外,見下一條「形狀 documented 例外」);其餘視覺全繼承 Button tertiary,不另自訂
132
132
  - **形狀 documented 例外**:override `rounded-md` → **`rounded-full`**(圓形)。理由:media carousel 視覺取向,圓形箭頭減少方塊感、不壓迫媒體內容(對齊 **Instagram / Airbnb / Notion Gallery / Apple Photos / Google Photos lightbox 世界級慣例** — 媒體 overlay 控制器圓形)。本 DS 內 Button default 是 `rounded-md`,這裡是唯一文明 override,documented 例外記錄即為本段(同步 `carousel.tsx` Button `className="rounded-full"` 旁 inline 註解)。**不傳染**:其他元件的 Button 仍走 rounded-md <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
133
133
  - 尺寸:`size="md"` 對應 `--field-height-md`(default/md 32px / lg 36px),iconOnly 正方形;形狀 **`rounded-full`**(documented 例外,見上一條)
134
134
  - 為什麼 tertiary:`bg-surface` + `border-border`,hover 僅改 text/border 為 `primary-hover`——無 bg-hover tint,視覺最輕,不搶 media 權重;secondary 有 neutral 邊框+hover 較重,primary 有底色更搶焦點,均不合適
@@ -136,6 +136,22 @@ Tabs 和 Carousel 都能「按順序切換下方內容」,但**語意與視覺
136
136
  - 圖標:`startIcon={ChevronLeft}` / `startIcon={ChevronRight}`,尺寸由 Button 內部程式化決定
137
137
  - **絕對定位 wrapper**:Button 外層 `<div>` 負責 `absolute` 定位 + hover-only opacity(`group-hover/carousel:opacity-100` / `focus-within:opacity-100`);Button 本身不做任何定位或可見性邏輯(職責分離)
138
138
 
139
+ ### Arrow Props(API 契約,2026-07-18 決策14)
140
+
141
+ `CarouselPrevious` / `CarouselNext` 開放**完整 Button props**——對齊 shadcn Carousel 的 `React.ComponentProps<typeof Button>` idiom(source `https://github.com/shadcn-ui/ui` `apps/www/registry/default/ui/carousel.tsx` CarouselPrevious/Next) <!-- @benchmark-unverified: shadcn Carousel arrow 轉發 Button props 為已知 pattern,確切行號未逐一 WebFetch 驗 -->。consumer 可換 `variant` / `size`、綁 analytics `onClick`、傳 `data-*` / `id`:
142
+
143
+ | Prop | 指向 | 說明 |
144
+ |------|------|------|
145
+ | `className` | **箭頭按鈕本身** | 疊在 DS default(`rounded-full` 等)之後,可覆寫按鈕樣式(className 指按鈕,非 wrapper) |
146
+ | `wrapperClassName` | **定位層 wrapper** | 絕對定位 + hover-only 顯示層的 className(換位置 / 顯示邏輯用) |
147
+ | `variant` / `size` | 箭頭按鈕 | default `tertiary` / `md`,可覆寫 |
148
+ | `startIcon` | 箭頭按鈕 | default `ChevronLeft` / `ChevronRight`,可覆寫圖標 |
149
+ | `onClick` | 箭頭按鈕 | 與內部 `scrollPrev/Next` **compose**(先導覽再呼叫 consumer handler;analytics 用) |
150
+ | `disabled` | 箭頭按鈕 | 與邊界態 **compose**(`!canScroll* \|\| disabled`;consumer 可強制 disable,不可強制在邊界 enable) |
151
+ | `iconOnly` | — | 固定 `true`(箭頭恆 icon-only),不可覆寫 |
152
+
153
+ **⚠️ BREAKING(決策14 遷移)**:`className` 語義由「定位 wrapper」改為「箭頭按鈕」。**原本用 `className` 做定位覆寫的 consumer → 改傳 `wrapperClassName`**。DS 內既有 consumer 全數 bare `<CarouselPrevious />`(無 className 傳入)= 0 遷移成本;fork consumer 若有覆寫定位,依本條遷移。
154
+
139
155
  ---
140
156
 
141
157
  ## Dots indicator
@@ -187,8 +187,12 @@ Carousel.displayName = 'Carousel'
187
187
  const CarouselContent = React.forwardRef<
188
188
  HTMLDivElement,
189
189
  React.HTMLAttributes<HTMLDivElement>
190
- >(({ className, ...props }, ref) => {
190
+ >(({ className, children, ...props }, ref) => {
191
191
  const { carouselRef, orientation } = useCarousel()
192
+ // 2026-07-18 決策5:注入 slide index/total 給 CarouselItem 自動產生「第 N 張,共 M 張」a11y 位置名
193
+ // (WAI-ARIA APG carousel「N of M」https://www.w3.org/WAI/ARIA/apg/patterns/carousel/)。count 取
194
+ // React.Children.count(可靠,不依賴 embla init 後的 scrollSnaps)。
195
+ const total = React.Children.count(children)
192
196
  return (
193
197
  <div ref={carouselRef} className="overflow-hidden">
194
198
  <div
@@ -199,31 +203,46 @@ const CarouselContent = React.forwardRef<
199
203
  className,
200
204
  )}
201
205
  {...props}
202
- />
206
+ >
207
+ {React.Children.map(children, (child, index) =>
208
+ React.isValidElement(child)
209
+ ? React.cloneElement(child as React.ReactElement<CarouselItemInternalProps>, { _slideIndex: index, _slideTotal: total })
210
+ : child,
211
+ )}
212
+ </div>
203
213
  </div>
204
214
  )
205
215
  })
206
216
  CarouselContent.displayName = 'CarouselContent'
207
217
 
208
- const CarouselItem = React.forwardRef<
209
- HTMLDivElement,
210
- React.HTMLAttributes<HTMLDivElement>
211
- >(({ className, ...props }, ref) => {
212
- const { orientation } = useCarousel()
213
- return (
214
- <div
215
- ref={ref}
216
- role="group"
217
- aria-roledescription="slide"
218
- className={cn(
219
- 'min-w-0 shrink-0 grow-0 basis-full',
220
- orientation === 'horizontal' ? 'pl-4' : 'pt-4',
221
- className,
222
- )}
223
- {...props}
224
- />
225
- )
226
- })
218
+ interface CarouselItemInternalProps extends React.HTMLAttributes<HTMLDivElement> {
219
+ /** @internal CarouselContent 注入 —— 用於自動 slide 位置名。 */
220
+ _slideIndex?: number
221
+ _slideTotal?: number
222
+ }
223
+
224
+ const CarouselItem = React.forwardRef<HTMLDivElement, CarouselItemInternalProps>(
225
+ ({ className, _slideIndex, _slideTotal, 'aria-label': ariaLabel, ...props }, ref) => {
226
+ const { orientation } = useCarousel()
227
+ // 無 consumer aria-label 時自動位置名(第 N 張,共 M 張);consumer 傳 aria-label 優先(可自訂內容名)。
228
+ const autoLabel =
229
+ ariaLabel ?? (_slideIndex != null && _slideTotal != null ? `第 ${_slideIndex + 1} 張,共 ${_slideTotal} 張` : undefined)
230
+ return (
231
+ <div
232
+ ref={ref}
233
+ role="group"
234
+ aria-roledescription="slide"
235
+ aria-label={autoLabel}
236
+ className={cn(
237
+ 'min-w-0 shrink-0 grow-0 basis-full',
238
+ orientation === 'horizontal' ? 'pl-4' : 'pt-4',
239
+ className,
240
+ )}
241
+ {...props}
242
+ />
243
+ )
244
+ },
245
+ )
227
246
  CarouselItem.displayName = 'CarouselItem'
228
247
 
229
248
  // ── Arrow buttons(hover 才顯示)────────────────────────────────────────────
@@ -231,10 +250,15 @@ CarouselItem.displayName = 'CarouselItem'
231
250
  // opacity transition 控制(Button 本身不負責)。此 wrapper 存在僅為絕對定位 +
232
251
  // hover/focus 可見性,不再覆寫 Button 的視覺 token。
233
252
 
234
- type ArrowProps = {
235
- className?: string
236
- /** ARIA label. Override for i18n. Prev default: 「上一張」;Next default: 「下一張」 */
237
- 'aria-label'?: string
253
+ // 2026-07-18 決策14(user 授權 B 案:開放完整 Button props + wrapperClassName):
254
+ // 對齊 shadcn/Radix Carousel —— `className` 指**箭頭按鈕本身**(非定位 wrapper),
255
+ // consumer 可換 variant/size、綁 analytics onClick、傳 data-* 等完整 Button props。
256
+ // 定位層(絕對定位 + hover-only 顯示)的 className 走新 `wrapperClassName`。
257
+ // **BREAKING**:`className` 語義由「定位 wrapper」改為「按鈕」(遷移:定位覆寫改傳 wrapperClassName)。
258
+ // onClick/disabled 內部與 carousel 導覽 compose(scrollPrev/Next + 邊界 disable 保留,consumer 疊加)。
259
+ type ArrowProps = Omit<React.ComponentPropsWithoutRef<typeof Button>, 'iconOnly'> & {
260
+ /** 定位層 wrapper 的 className(絕對定位 + hover-only 顯示層);箭頭按鈕樣式走 `className`。 */
261
+ wrapperClassName?: string
238
262
  }
239
263
 
240
264
  const arrowWrapperClass = cn(
@@ -247,7 +271,19 @@ const arrowWrapperClass = cn(
247
271
 
248
272
  // code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding
249
273
  const CarouselPrevious = React.forwardRef<HTMLButtonElement, ArrowProps>(
250
- ({ className, 'aria-label': ariaLabel = '上一張' /* i18n-allow: DS default; consumer override via aria-label prop */ }, ref) => {
274
+ (
275
+ {
276
+ className,
277
+ wrapperClassName,
278
+ onClick,
279
+ disabled,
280
+ variant = 'tertiary',
281
+ size = 'md',
282
+ 'aria-label': ariaLabel = '上一張' /* i18n-allow: DS default; consumer override via aria-label prop */,
283
+ ...props
284
+ },
285
+ ref,
286
+ ) => {
251
287
  const { orientation, scrollPrev, canScrollPrev } = useCarousel()
252
288
  return (
253
289
  <div
@@ -256,22 +292,24 @@ const CarouselPrevious = React.forwardRef<HTMLButtonElement, ArrowProps>(
256
292
  orientation === 'horizontal'
257
293
  ? 'left-3 top-1/2 -translate-y-1/2'
258
294
  : 'top-3 left-1/2 -translate-x-1/2 rotate-90',
259
- className,
295
+ wrapperClassName,
260
296
  )}
261
297
  >
262
298
  <Button
263
299
  ref={ref}
264
- variant="tertiary"
265
- size="md"
266
- iconOnly
300
+ variant={variant}
301
+ size={size}
267
302
  startIcon={ChevronLeft}
268
303
  aria-label={ariaLabel}
269
- disabled={!canScrollPrev}
270
- onClick={scrollPrev}
304
+ {...props}
305
+ iconOnly
306
+ disabled={!canScrollPrev || disabled}
307
+ onClick={(e) => { scrollPrev(); onClick?.(e) }}
271
308
  // documented exception:視覺取向的 media carousel 箭頭用 rounded-full 圓形,
272
309
  // 優於 DS default rounded-md。對齊 Instagram / Airbnb / Notion / Apple Photos
273
310
  // 世界級慣例 — media carousel 箭頭圓形減少視覺方塊感壓迫內容。spec「箭頭視覺規格」有明示。
274
- className="rounded-full"
311
+ // consumer className 疊加在後可覆寫(決策14)。
312
+ className={cn('rounded-full', className)}
275
313
  />
276
314
  </div>
277
315
  )
@@ -281,7 +319,19 @@ CarouselPrevious.displayName = 'CarouselPrevious'
281
319
 
282
320
  // code-quality-allow: long-function — 與 CarouselPrevious 結構對稱的 JSX-heavy arrow(方向/位置/邊界三態 className 組合),拆 helper 會破壞兩支對讀性
283
321
  const CarouselNext = React.forwardRef<HTMLButtonElement, ArrowProps>(
284
- ({ className, 'aria-label': ariaLabel = '下一張' /* i18n-allow: DS default; consumer override via aria-label prop */ }, ref) => {
322
+ (
323
+ {
324
+ className,
325
+ wrapperClassName,
326
+ onClick,
327
+ disabled,
328
+ variant = 'tertiary',
329
+ size = 'md',
330
+ 'aria-label': ariaLabel = '下一張' /* i18n-allow: DS default; consumer override via aria-label prop */,
331
+ ...props
332
+ },
333
+ ref,
334
+ ) => {
285
335
  const { orientation, scrollNext, canScrollNext } = useCarousel()
286
336
  return (
287
337
  <div
@@ -290,20 +340,21 @@ const CarouselNext = React.forwardRef<HTMLButtonElement, ArrowProps>(
290
340
  orientation === 'horizontal'
291
341
  ? 'right-3 top-1/2 -translate-y-1/2'
292
342
  : 'bottom-3 left-1/2 -translate-x-1/2 rotate-90',
293
- className,
343
+ wrapperClassName,
294
344
  )}
295
345
  >
296
346
  <Button
297
347
  ref={ref}
298
- variant="tertiary"
299
- size="md"
300
- iconOnly
348
+ variant={variant}
349
+ size={size}
301
350
  startIcon={ChevronRight}
302
351
  aria-label={ariaLabel}
303
- disabled={!canScrollNext}
304
- onClick={scrollNext}
305
- // documented exception:同 Previous,媒體導向 carousel 箭頭圓形
306
- className="rounded-full"
352
+ {...props}
353
+ iconOnly
354
+ disabled={!canScrollNext || disabled}
355
+ onClick={(e) => { scrollNext(); onClick?.(e) }}
356
+ // documented exception:同 Previous,媒體導向 carousel 箭頭圓形;consumer className 疊加可覆寫。
357
+ className={cn('rounded-full', className)}
307
358
  />
308
359
  </div>
309
360
  )
@@ -1,6 +1,7 @@
1
1
  // @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.
2
2
  import * as React from 'react'
3
3
  import { cn } from '@/lib/utils'
4
+ import { useFieldContext } from '@/design-system/components/Field/field-context'
4
5
 
5
6
  // ── CheckboxGroupContext ────────────────────────────────────────────────────
6
7
  // 讓內部 `<Checkbox>` 知道「我在 CheckboxGroup 裡」→ 即使 Field context 也存在,
@@ -71,21 +72,31 @@ export interface CheckboxGroupProps extends React.HTMLAttributes<HTMLDivElement>
71
72
  const CHECKBOX_GROUP_CTX_VALUE = { inGroup: true } as const
72
73
 
73
74
  const CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps>(
74
- ({ className, orientation = 'vertical', ...props }, ref) => (
75
- <CheckboxGroupContext.Provider value={CHECKBOX_GROUP_CTX_VALUE}>
76
- <div
77
- ref={ref}
78
- role="group"
79
- className={cn(
80
- // 垂直 CheckboxGroup:zero gap(間距由 SelectionItem py 獨家擁有,見 docblock canonical)
81
- // 水平:短 label 並排需水平 gap-4(label 沒有 py 擴散,需要顯式 gap)
82
- orientation === 'vertical' ? 'grid' : 'flex flex-wrap gap-4',
83
- className
84
- )}
85
- {...props}
86
- />
87
- </CheckboxGroupContext.Provider>
88
- )
75
+ ({ className, orientation = 'vertical', ...props }, ref) => {
76
+ // Field 整合 a11y(2026-07-17 d10i1):role="group" 的 <div> 無法被 <FieldLabel htmlFor> 命名 →
77
+ // 自讀 fieldCtx 接 aria-labelledby / aria-describedby / aria-invalid,讓群組在 accessibility tree
78
+ // 有名稱、描述與錯誤狀態(對齊 RadioGroup Root 的 fieldCtx 接線,radio-group.tsx L164-166)。
79
+ // standalone(無 Field)時皆 undefined,consumer 經 {...props} 傳的 aria-* 照舊生效(置於 spread 前故可被覆蓋)。
80
+ const fieldCtx = useFieldContext()
81
+ return (
82
+ <CheckboxGroupContext.Provider value={CHECKBOX_GROUP_CTX_VALUE}>
83
+ <div
84
+ ref={ref}
85
+ role="group"
86
+ aria-labelledby={fieldCtx?.labelId}
87
+ aria-describedby={fieldCtx?.descriptionId}
88
+ aria-invalid={fieldCtx?.invalid || undefined}
89
+ className={cn(
90
+ // 垂直 CheckboxGroup:zero gap(間距由 SelectionItem py 獨家擁有,見 docblock canonical)
91
+ // 水平:短 label 並排需水平 gap-4(label 沒有 py 擴散,需要顯式 gap)
92
+ orientation === 'vertical' ? 'grid' : 'flex flex-wrap gap-4',
93
+ className
94
+ )}
95
+ {...props}
96
+ />
97
+ </CheckboxGroupContext.Provider>
98
+ )
99
+ }
89
100
  )
90
101
  CheckboxGroup.displayName = 'CheckboxGroup'
91
102
  // Field layout declaration:block primitive(多項堆疊)——進入 <Field> 時
@@ -297,8 +297,8 @@ const InspectorInner = () => {
297
297
  size={size}
298
298
  checked={checkedState === 'checked' ? true : checkedState === 'indeterminate' ? 'indeterminate' : false}
299
299
  disabled={interaction === 'disabled'}
300
- label="Label text"
301
- description={withDescription ? '次要說明文字,給使用者更多 context' : undefined}
300
+ label="接收每週專案摘要"
301
+ description={withDescription ? '每週一自動寄送專案進度與待辦事項摘要到你的信箱' : undefined}
302
302
  />
303
303
  ) : (
304
304
  <Checkbox
@@ -496,7 +496,7 @@ export const SizeMatrix = {
496
496
  <Td><Checkbox size={sz} defaultChecked /></Td>
497
497
  <Td>
498
498
  {/* 公開 API:`<Checkbox label>` 內部自動包 SelectionItem + wire id/htmlFor(selection-item.spec.md 禁裸用) */}
499
- <Checkbox size={sz} defaultChecked label="Label text" />
499
+ <Checkbox size={sz} defaultChecked label="接收通知" />
500
500
  </Td>
501
501
  </tr>
502
502
  ))}
@@ -232,6 +232,10 @@ Indeterminate 是由父層邏輯控制的狀態,Checkbox 本身不會自動進
232
232
  | disabled unchecked | transparent | bg-disabled | 無 |
233
233
  | disabled checked | transparent | bg-disabled | fg-disabled dot |
234
234
 
235
+ ### Loading(N/A)
236
+
237
+ Checkbox / Radio **無 Loading 狀態**——選擇控件是同步 toggle,值隨 form submit 才套用(見「定位」),不承載非同步進度。提交進行中的 loading 狀態由**表單的 submit `Button` loading state** 擁有;提交期間若需鎖定選項,將 Checkbox 設 `disabled` 即可,不在控件本體加 spinner。(對齊 Switch spec「無 loading state」——async 進度不屬選擇控件本體。)
238
+
235
239
  ---
236
240
 
237
241
  ## Controlled / Uncontrolled API(M26)
@@ -244,14 +248,18 @@ Indeterminate 是由父層邏輯控制的狀態,Checkbox 本身不會自動進
244
248
 
245
249
  ### `mode` prop(Field mode,正交於 size)
246
250
 
247
- `mode?: 'edit' | 'display' | 'readonly' | 'disabled'`(默認 inherit Field context 或 `'edit'`),對齊 `field-types.ts` FieldMode(完整 4-mode canonical SSOT → `Field/field-controls.spec.md`;本段鏡像 switch.spec.md 同名段):
251
+ `mode?: 'edit' | 'view' | 'readonly' | 'disabled'`(默認 inherit Field context 或 `'edit'`),對齊 `field-types.ts` FieldMode(完整 4-mode canonical SSOT → `Field/field-controls.spec.md`;本段鏡像 switch.spec.md 同名段):
248
252
  - `edit`(預設)— 可勾選的 Checkbox。
249
- - `display` — **純展示**:渲染 **Check icon(true=勾)/ X icon(false=叉)**,中性 `text-foreground` 色,非互動、無 input chrome,供 DataTable boolean cell 非編輯態共用。boolean 值符號 SSOT = `../SelectionControl/boolean-value.tsx`(勾/叉 icon + 中性色;M22 世界級:MUI X DataGrid Check/Close icon 中性色 / React-admin TrueIcon-FalseIcon;false 中性非紅 = 「否」值非「錯誤」)。
253
+ - `view` — **純展示**:渲染 **Check icon(true=勾)/ X icon(false=叉)**,中性 `text-foreground` 色,非互動、無 input chrome,供 DataTable boolean cell 非編輯態共用。boolean 值符號 SSOT = `../SelectionControl/boolean-value.tsx`(勾/叉 icon + 中性色;M22 世界級:MUI X DataGrid Check/Close icon 中性色 / React-admin TrueIcon-FalseIcon;false 中性非紅 = 「否」值非「錯誤」)。
250
254
  - `readonly` — **Field 內(無 inline label)**= `fieldWrapperStyles` readonly 灰框 + 勾/叉 icon(與 Input readonly 同一視覺語言,2026-06-12 user 拍板;世界級:Salesforce output ✓ glyph / SAP 靜態文字);**standalone / 有 inline label**(SelectionItem row)= 正常色鎖互動(同下方 Read-only)。
251
- - `disabled` — 落到真 disabled chrome(`effectiveDisabled`,2026-06-12 修:mode='disabled' 直傳〔如 DataTable disabled cell〕與 `disabled` prop 等效,降色 + 不可 focus)。
255
+ - `disabled` — 落到真 disabled chrome(`effectiveDisabled`,2026-06-12 修:mode='disabled' 直傳〔如 `<Field mode="disabled">` cascade〕與 `disabled` prop 等效,降色 + 不可 focus)。DataTable cell 無 disabled 態(已廢除);cell 非編輯態 = `view` 純展示。
252
256
 
253
257
  CheckboxGroup 是純 layout primitive — **不**持有 group-level selection state(無 `value` / `defaultValue` / `onValueChange`)。每個 `<Checkbox>` child 各自管自己的 `checked` / `defaultChecked` / `onCheckedChange`;CheckboxGroup 只透過 `CheckboxGroupContext` 告知 child「你在 group 裡」(保留各自 label)。
254
258
 
259
+ ### 表單驗證接線(useFormValidation v1 邊界)
260
+
261
+ Checkbox 值走 `onCheckedChange`(非 value/onChange 型控件),接 `useFormValidation`(`../Field/form-validation.spec.md`)時用 **`setFieldValue` 自接、不經 `getInputProps`**。這是該 spec「v1 邊界」誠實記載的已知限制:**無自動 blur 驗證 / 邊改邊清 error**(該層 timing 只覆蓋 value/onChange 型控件)—— Checkbox 值變更後由 consumer 於 `setFieldValue` 後自行 re-validate,或隨 submit 全驗。**error 視覺不受此限制影響**:`Field invalid` context 是 engine-agnostic 的,紅框 + `FieldError` 照常呈現;`<Field>` 錯誤群組化見 field.spec.md。
262
+
255
263
  ---
256
264
 
257
265
  ## 群組模式(CheckboxGroup)
@@ -336,6 +344,7 @@ Horizontal 需 `gap-4` 因 row 的 py 不擴散到左右。
336
344
  - `../RadioGroup/radio-group.spec.md` — Radio 的 group 容器 + 結構對稱 reciprocal
337
345
  - `../SelectionControl/selection-item.spec.md` — Checkbox / Radio 共用的 SelectionItem 佈局 primitive(本 spec 的 Clamp 政策為其 SSOT)
338
346
  - `../Field/field-controls.spec.md` — Field Control 共用規則
347
+ - `../Field/form-validation.spec.md` — 表單驗證引擎;Checkbox 用 `setFieldValue` 自接的 v1 邊界見「Controlled / Uncontrolled API → 表單驗證接線」
339
348
 
340
349
  ## A11y 預設
341
350
 
@@ -13,7 +13,7 @@ const meta: Meta<typeof Checkbox> = {
13
13
  export default meta
14
14
  type Story = StoryObj<typeof Checkbox>
15
15
 
16
- // @story-trait-rationale: pre-existing trait gaps tracked separately; this PR scope = add Modes story with display card.
16
+ // @story-trait-rationale: pre-existing trait gaps tracked separately; this PR scope = add Modes story with view card.
17
17
  /* ── 四模式 ── */
18
18
  export const Modes: Story = {
19
19
  name: '四模式',
@@ -24,9 +24,9 @@ export const Modes: Story = {
24
24
  <Checkbox defaultChecked aria-label="同意條款(edit mode demo)" />
25
25
  </div>
26
26
  <div>
27
- <h3 className="text-body font-bold text-foreground mb-2">display</h3>
28
- <Checkbox mode="display" checked />
29
- <p className="text-caption text-fg-muted mt-1">純視覺 glyph(勾/叉 icon,Check/X);語意由 context(如 DataTable 表頭 + 行標籤)提供,display 不暴露獨立 aria-label。需螢幕報讀器可讀的勾選請用 edit / readonly 模式。</p>
27
+ <h3 className="text-body font-bold text-foreground mb-2">view</h3>
28
+ <Checkbox mode="view" checked />
29
+ <p className="text-caption text-fg-muted mt-1">純視覺 glyph(勾/叉 icon,Check/X);語意由 context(如 DataTable 表頭 + 行標籤)提供,view 不暴露獨立 aria-label。需螢幕報讀器可讀的勾選請用 edit / readonly 模式。</p>
30
30
  </div>
31
31
  <div>
32
32
  <h3 className="text-body font-bold text-foreground mb-2">readonly</h3>