@qijenchen/design-system 0.1.0-beta.80 → 0.1.0-beta.82

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 (548) hide show
  1. package/CLAUDE.md +1 -1
  2. package/dist/components/Alert/alert.d.ts +5 -0
  3. package/dist/components/Alert/alert.d.ts.map +1 -1
  4. package/dist/components/Alert/alert.js +4 -1
  5. package/dist/components/Alert/alert.js.map +1 -1
  6. package/dist/components/AppShell/app-shell.d.ts +1 -1
  7. package/dist/components/AppShell/app-shell.js.map +1 -1
  8. package/dist/components/AspectRatio/aspect-ratio.d.ts +2 -1
  9. package/dist/components/AspectRatio/aspect-ratio.d.ts.map +1 -1
  10. package/dist/components/AspectRatio/aspect-ratio.js +1 -1
  11. package/dist/components/AspectRatio/aspect-ratio.js.map +1 -1
  12. package/dist/components/Avatar/avatar.d.ts +3 -2
  13. package/dist/components/Avatar/avatar.d.ts.map +1 -1
  14. package/dist/components/Avatar/avatar.js +8 -3
  15. package/dist/components/Avatar/avatar.js.map +1 -1
  16. package/dist/components/Badge/badge.d.ts.map +1 -1
  17. package/dist/components/Badge/badge.js +2 -1
  18. package/dist/components/Badge/badge.js.map +1 -1
  19. package/dist/components/Breadcrumb/breadcrumb.d.ts +14 -1
  20. package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
  21. package/dist/components/Breadcrumb/breadcrumb.js +9 -3
  22. package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
  23. package/dist/components/BulkActionBar/bulk-action-bar.d.ts +4 -1
  24. package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
  25. package/dist/components/BulkActionBar/bulk-action-bar.js +2 -2
  26. package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
  27. package/dist/components/Button/button.d.ts +4 -4
  28. package/dist/components/Button/button.d.ts.map +1 -1
  29. package/dist/components/Button/button.js +73 -44
  30. package/dist/components/Button/button.js.map +1 -1
  31. package/dist/components/Button/index.d.ts +1 -0
  32. package/dist/components/Button/index.d.ts.map +1 -1
  33. package/dist/components/Button/index.js +3 -0
  34. package/dist/components/Button/index.js.map +1 -1
  35. package/dist/components/Calendar/calendar.d.ts +3 -1
  36. package/dist/components/Calendar/calendar.d.ts.map +1 -1
  37. package/dist/components/Calendar/calendar.js +40 -17
  38. package/dist/components/Calendar/calendar.js.map +1 -1
  39. package/dist/components/Carousel/carousel.d.ts +1 -1
  40. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  41. package/dist/components/Carousel/carousel.js +3 -1
  42. package/dist/components/Carousel/carousel.js.map +1 -1
  43. package/dist/components/Chart/chart.d.ts.map +1 -1
  44. package/dist/components/Chart/chart.js +1 -1
  45. package/dist/components/Chart/chart.js.map +1 -1
  46. package/dist/components/Checkbox/checkbox.d.ts +6 -3
  47. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  48. package/dist/components/Checkbox/checkbox.js +26 -4
  49. package/dist/components/Checkbox/checkbox.js.map +1 -1
  50. package/dist/components/Checkbox/index.d.ts +1 -0
  51. package/dist/components/Checkbox/index.d.ts.map +1 -1
  52. package/dist/components/Checkbox/index.js +3 -0
  53. package/dist/components/Checkbox/index.js.map +1 -1
  54. package/dist/components/Chip/chip.d.ts +9 -3
  55. package/dist/components/Chip/chip.d.ts.map +1 -1
  56. package/dist/components/Chip/chip.js +46 -13
  57. package/dist/components/Chip/chip.js.map +1 -1
  58. package/dist/components/Coachmark/coachmark.d.ts +5 -3
  59. package/dist/components/Coachmark/coachmark.d.ts.map +1 -1
  60. package/dist/components/Coachmark/coachmark.js +10 -7
  61. package/dist/components/Coachmark/coachmark.js.map +1 -1
  62. package/dist/components/Combobox/combobox.d.ts +3 -1
  63. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  64. package/dist/components/Combobox/combobox.js +11 -7
  65. package/dist/components/Combobox/combobox.js.map +1 -1
  66. package/dist/components/Command/command.d.ts +2 -2
  67. package/dist/components/Command/command.d.ts.map +1 -1
  68. package/dist/components/Command/command.js +4 -2
  69. package/dist/components/Command/command.js.map +1 -1
  70. package/dist/components/DataTable/active-editor-controller.d.ts +4 -3
  71. package/dist/components/DataTable/active-editor-controller.d.ts.map +1 -1
  72. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  73. package/dist/components/DataTable/cell-registry.js +3 -0
  74. package/dist/components/DataTable/cell-registry.js.map +1 -1
  75. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  76. package/dist/components/DataTable/data-table-interaction-layer.d.ts +4 -3
  77. package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
  78. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  79. package/dist/components/DataTable/data-table.d.ts +38 -7
  80. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  81. package/dist/components/DataTable/data-table.js +213 -63
  82. package/dist/components/DataTable/data-table.js.map +1 -1
  83. package/dist/components/DataTable/filter-tree.d.ts +5 -0
  84. package/dist/components/DataTable/filter-tree.d.ts.map +1 -1
  85. package/dist/components/DateGrid/date-grid.d.ts +3 -3
  86. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  87. package/dist/components/DateGrid/date-grid.js +52 -6
  88. package/dist/components/DateGrid/date-grid.js.map +1 -1
  89. package/dist/components/DatePicker/date-picker.d.ts +2 -8
  90. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  91. package/dist/components/DatePicker/date-picker.js +139 -109
  92. package/dist/components/DatePicker/date-picker.js.map +1 -1
  93. package/dist/components/DescriptionList/description-list.d.ts.map +1 -1
  94. package/dist/components/DescriptionList/description-list.js +1 -1
  95. package/dist/components/DescriptionList/description-list.js.map +1 -1
  96. package/dist/components/Dialog/dialog.d.ts +1 -1
  97. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  98. package/dist/components/Dialog/dialog.js +2 -1
  99. package/dist/components/Dialog/dialog.js.map +1 -1
  100. package/dist/components/DropdownMenu/dropdown-menu.d.ts +4 -4
  101. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  102. package/dist/components/DropdownMenu/dropdown-menu.js +25 -8
  103. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  104. package/dist/components/Empty/empty.d.ts.map +1 -1
  105. package/dist/components/Empty/empty.js +1 -1
  106. package/dist/components/Empty/empty.js.map +1 -1
  107. package/dist/components/Field/field-wrapper.d.ts +1 -0
  108. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  109. package/dist/components/Field/field-wrapper.js +27 -7
  110. package/dist/components/Field/field-wrapper.js.map +1 -1
  111. package/dist/components/Field/field.d.ts +1 -1
  112. package/dist/components/Field/field.d.ts.map +1 -1
  113. package/dist/components/Field/field.js +2 -1
  114. package/dist/components/Field/field.js.map +1 -1
  115. package/dist/components/Field/use-form-validation.d.ts +5 -1
  116. package/dist/components/Field/use-form-validation.d.ts.map +1 -1
  117. package/dist/components/Field/use-form-validation.js +31 -21
  118. package/dist/components/Field/use-form-validation.js.map +1 -1
  119. package/dist/components/FieldControlGroup/field-control-group.d.ts +3 -3
  120. package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
  121. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  122. package/dist/components/FileItem/file-item.js +3 -2
  123. package/dist/components/FileItem/file-item.js.map +1 -1
  124. package/dist/components/FileUpload/file-upload.d.ts +1 -1
  125. package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
  126. package/dist/components/FileUpload/file-upload.js +22 -10
  127. package/dist/components/FileUpload/file-upload.js.map +1 -1
  128. package/dist/components/FileViewer/can-render-image.d.ts +6 -0
  129. package/dist/components/FileViewer/can-render-image.d.ts.map +1 -0
  130. package/dist/components/FileViewer/can-render-image.js +11 -0
  131. package/dist/components/FileViewer/can-render-image.js.map +1 -0
  132. package/dist/components/FileViewer/file-viewer.d.ts +2 -2
  133. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  134. package/dist/components/FileViewer/file-viewer.js +24 -16
  135. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  136. package/dist/components/FileViewer/image-renderer.d.ts +0 -5
  137. package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
  138. package/dist/components/FileViewer/image-renderer.js +1 -9
  139. package/dist/components/FileViewer/image-renderer.js.map +1 -1
  140. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  141. package/dist/components/HoverCard/hover-card.js +3 -1
  142. package/dist/components/HoverCard/hover-card.js.map +1 -1
  143. package/dist/components/Input/input.d.ts +26 -3
  144. package/dist/components/Input/input.d.ts.map +1 -1
  145. package/dist/components/Input/input.js +12 -8
  146. package/dist/components/Input/input.js.map +1 -1
  147. package/dist/components/LinkInput/link-input.d.ts +3 -3
  148. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  149. package/dist/components/LinkInput/link-input.js +29 -28
  150. package/dist/components/LinkInput/link-input.js.map +1 -1
  151. package/dist/components/Menu/menu-item.d.ts.map +1 -1
  152. package/dist/components/Menu/menu-item.js +5 -4
  153. package/dist/components/Menu/menu-item.js.map +1 -1
  154. package/dist/components/Notice/notice.d.ts +5 -0
  155. package/dist/components/Notice/notice.d.ts.map +1 -1
  156. package/dist/components/Notice/notice.js +11 -5
  157. package/dist/components/Notice/notice.js.map +1 -1
  158. package/dist/components/NumberInput/number-input.d.ts +1 -1
  159. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  160. package/dist/components/NumberInput/number-input.js +17 -8
  161. package/dist/components/NumberInput/number-input.js.map +1 -1
  162. package/dist/components/OverflowIndicator/overflow-indicator.d.ts +1 -1
  163. package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
  164. package/dist/components/OverflowIndicator/overflow-indicator.js +2 -1
  165. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  166. package/dist/components/Pagination/index.d.ts +2 -0
  167. package/dist/components/Pagination/index.d.ts.map +1 -0
  168. package/dist/components/Pagination/index.js +6 -0
  169. package/dist/components/Pagination/index.js.map +1 -0
  170. package/dist/components/Pagination/pagination.d.ts +49 -0
  171. package/dist/components/Pagination/pagination.d.ts.map +1 -0
  172. package/dist/components/Pagination/pagination.js +182 -0
  173. package/dist/components/Pagination/pagination.js.map +1 -0
  174. package/dist/components/PeoplePicker/people-picker.d.ts +3 -3
  175. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  176. package/dist/components/PeoplePicker/people-picker.js +38 -36
  177. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  178. package/dist/components/PeoplePicker/person-display.d.ts +4 -0
  179. package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
  180. package/dist/components/PeoplePicker/person-display.js +3 -3
  181. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  182. package/dist/components/Popover/popover.d.ts.map +1 -1
  183. package/dist/components/Popover/popover.js +58 -32
  184. package/dist/components/Popover/popover.js.map +1 -1
  185. package/dist/components/ProfileCard/profile-card.d.ts +11 -3
  186. package/dist/components/ProfileCard/profile-card.d.ts.map +1 -1
  187. package/dist/components/ProfileCard/profile-card.js +14 -4
  188. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  189. package/dist/components/RadioGroup/radio-group.d.ts +1 -1
  190. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  191. package/dist/components/RadioGroup/radio-group.js +28 -3
  192. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  193. package/dist/components/Rating/rating.js +3 -3
  194. package/dist/components/Rating/rating.js.map +1 -1
  195. package/dist/components/ScrollArea/scroll-area.d.ts +1 -1
  196. package/dist/components/ScrollArea/scroll-area.js +2 -2
  197. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  198. package/dist/components/SegmentedControl/segmented-control.d.ts +2 -2
  199. package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
  200. package/dist/components/SegmentedControl/segmented-control.js +23 -10
  201. package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
  202. package/dist/components/Select/select.d.ts +10 -4
  203. package/dist/components/Select/select.d.ts.map +1 -1
  204. package/dist/components/Select/select.js +57 -14
  205. package/dist/components/Select/select.js.map +1 -1
  206. package/dist/components/SelectMenu/index.js +4 -2
  207. package/dist/components/SelectMenu/select-menu.d.ts +31 -6
  208. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  209. package/dist/components/SelectMenu/select-menu.js +169 -85
  210. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  211. package/dist/components/Separator/separator.js +1 -1
  212. package/dist/components/Separator/separator.js.map +1 -1
  213. package/dist/components/Sheet/sheet.d.ts +1 -1
  214. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  215. package/dist/components/Sheet/sheet.js +3 -2
  216. package/dist/components/Sheet/sheet.js.map +1 -1
  217. package/dist/components/Sidebar/sidebar.d.ts +2 -2
  218. package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
  219. package/dist/components/Sidebar/sidebar.js +28 -10
  220. package/dist/components/Sidebar/sidebar.js.map +1 -1
  221. package/dist/components/Skeleton/skeleton.d.ts +1 -1
  222. package/dist/components/Skeleton/skeleton.js +2 -2
  223. package/dist/components/Skeleton/skeleton.js.map +1 -1
  224. package/dist/components/Slider/slider.d.ts +1 -1
  225. package/dist/components/Slider/slider.d.ts.map +1 -1
  226. package/dist/components/Slider/slider.js +7 -3
  227. package/dist/components/Slider/slider.js.map +1 -1
  228. package/dist/components/Steps/steps.d.ts.map +1 -1
  229. package/dist/components/Steps/steps.js +6 -3
  230. package/dist/components/Steps/steps.js.map +1 -1
  231. package/dist/components/Switch/switch.d.ts +2 -2
  232. package/dist/components/Switch/switch.d.ts.map +1 -1
  233. package/dist/components/Switch/switch.js +47 -9
  234. package/dist/components/Switch/switch.js.map +1 -1
  235. package/dist/components/Tabs/tabs.d.ts +8 -5
  236. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  237. package/dist/components/Tabs/tabs.js +45 -33
  238. package/dist/components/Tabs/tabs.js.map +1 -1
  239. package/dist/components/Tag/tag.d.ts.map +1 -1
  240. package/dist/components/Tag/tag.js +20 -4
  241. package/dist/components/Tag/tag.js.map +1 -1
  242. package/dist/components/Textarea/textarea.d.ts +2 -1
  243. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  244. package/dist/components/Textarea/textarea.js +24 -13
  245. package/dist/components/Textarea/textarea.js.map +1 -1
  246. package/dist/components/TimePicker/time-picker.d.ts +2 -2
  247. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  248. package/dist/components/TimePicker/time-picker.js +23 -12
  249. package/dist/components/TimePicker/time-picker.js.map +1 -1
  250. package/dist/components/Toast/toast.d.ts +1 -1
  251. package/dist/components/Toast/toast.d.ts.map +1 -1
  252. package/dist/components/Toast/toast.js +7 -3
  253. package/dist/components/Toast/toast.js.map +1 -1
  254. package/dist/components/Tooltip/tooltip.js +1 -1
  255. package/dist/components/Tooltip/tooltip.js.map +1 -1
  256. package/dist/components/TreeView/tree-view.d.ts +1 -1
  257. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  258. package/dist/components/TreeView/tree-view.js +11 -5
  259. package/dist/components/TreeView/tree-view.js.map +1 -1
  260. package/dist/hooks/use-controllable.d.ts.map +1 -1
  261. package/dist/hooks/use-controllable.js +4 -2
  262. package/dist/hooks/use-controllable.js.map +1 -1
  263. package/dist/hooks/use-is-touch-device.d.ts.map +1 -1
  264. package/dist/hooks/use-is-touch-device.js +3 -1
  265. package/dist/hooks/use-is-touch-device.js.map +1 -1
  266. package/dist/index.d.ts +2 -0
  267. package/dist/index.d.ts.map +1 -1
  268. package/dist/index.js +14 -0
  269. package/dist/index.js.map +1 -1
  270. package/dist/patterns/element-anatomy/item-anatomy.d.ts +2 -2
  271. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  272. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  273. package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
  274. package/dist/tokens/categorical-color.d.ts +1 -1
  275. package/dist/tokens/categorical-color.js.map +1 -1
  276. package/dist/tokens/uiSize/icon-size.d.ts +2 -2
  277. package/ds-canonical/fork/governance.lock +1 -1
  278. package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +10 -1
  279. package/ds-canonical/hooks/check_audit_post_report_validator.sh +13 -0
  280. package/ds-canonical/hooks/check_full_story_visual_interaction_sweep.sh +4 -3
  281. package/ds-canonical/hooks/check_opacity_token_usage.sh +10 -1
  282. package/ds-canonical/hooks/check_plugin_fork_health.sh +30 -22
  283. package/ds-canonical/hooks/check_story_invariants.sh +17 -5
  284. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +5 -2
  285. package/ds-canonical/references/principle-dim-map.json +100 -0
  286. package/ds-canonical/references/story-baseline-registry.json +4 -3
  287. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +1 -1
  288. package/ds-canonical/skills/design-system-audit/SKILL.md +2 -2
  289. package/ds-story-manifest.json +41 -18
  290. package/llms-full.txt +11 -1
  291. package/llms.txt +3 -1
  292. package/package.json +1 -1
  293. package/src/components/Alert/alert.anatomy.stories.tsx +9 -6
  294. package/src/components/Alert/alert.spec.md +3 -3
  295. package/src/components/Alert/alert.tsx +9 -1
  296. package/src/components/AppShell/app-shell.anatomy.stories.tsx +3 -0
  297. package/src/components/AppShell/app-shell.spec.md +3 -2
  298. package/src/components/AppShell/app-shell.tsx +1 -1
  299. package/src/components/AspectRatio/aspect-ratio.spec.md +1 -2
  300. package/src/components/AspectRatio/aspect-ratio.tsx +3 -2
  301. package/src/components/Avatar/avatar.principles.stories.tsx +1 -1
  302. package/src/components/Avatar/avatar.spec.md +10 -30
  303. package/src/components/Avatar/avatar.stories.tsx +1 -1
  304. package/src/components/Avatar/avatar.tsx +30 -16
  305. package/src/components/Badge/badge.anatomy.stories.tsx +2 -2
  306. package/src/components/Badge/badge.spec.md +5 -4
  307. package/src/components/Badge/badge.tsx +4 -1
  308. package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +2 -2
  309. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +15 -10
  310. package/src/components/Breadcrumb/breadcrumb.spec.md +5 -3
  311. package/src/components/Breadcrumb/breadcrumb.tsx +10 -4
  312. package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +3 -2
  313. package/src/components/BulkActionBar/bulk-action-bar.spec.md +15 -14
  314. package/src/components/BulkActionBar/bulk-action-bar.tsx +10 -4
  315. package/src/components/Button/button.anatomy.stories.tsx +6 -6
  316. package/src/components/Button/button.spec.md +3 -2
  317. package/src/components/Button/button.tsx +67 -27
  318. package/src/components/Button/index.ts +1 -0
  319. package/src/components/Calendar/calendar.anatomy.stories.tsx +5 -3
  320. package/src/components/Calendar/calendar.principles.stories.tsx +3 -1
  321. package/src/components/Calendar/calendar.spec.md +9 -2
  322. package/src/components/Calendar/calendar.stories.tsx +3 -1
  323. package/src/components/Calendar/calendar.tsx +53 -19
  324. package/src/components/Carousel/carousel.principles.stories.tsx +1 -1
  325. package/src/components/Carousel/carousel.tsx +4 -1
  326. package/src/components/Chart/chart.spec.md +1 -1
  327. package/src/components/Chart/chart.tsx +2 -1
  328. package/src/components/Checkbox/checkbox.principles.stories.tsx +1 -1
  329. package/src/components/Checkbox/checkbox.spec.md +1 -1
  330. package/src/components/Checkbox/checkbox.tsx +41 -7
  331. package/src/components/Checkbox/index.ts +1 -0
  332. package/src/components/Chip/chip.anatomy.stories.tsx +9 -9
  333. package/src/components/Chip/chip.principles.stories.tsx +3 -3
  334. package/src/components/Chip/chip.spec.md +7 -4
  335. package/src/components/Chip/chip.tsx +60 -15
  336. package/src/components/CircularProgress/circular-progress.anatomy.stories.tsx +1 -1
  337. package/src/components/CircularProgress/circular-progress.principles.stories.tsx +7 -5
  338. package/src/components/CircularProgress/circular-progress.spec.md +2 -0
  339. package/src/components/CircularProgress/circular-progress.stories.tsx +3 -11
  340. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +2 -2
  341. package/src/components/Coachmark/coachmark.principles.stories.tsx +1 -1
  342. package/src/components/Coachmark/coachmark.spec.md +3 -3
  343. package/src/components/Coachmark/coachmark.tsx +23 -13
  344. package/src/components/Combobox/combobox.anatomy.stories.tsx +4 -4
  345. package/src/components/Combobox/combobox.spec.md +1 -1
  346. package/src/components/Combobox/combobox.tsx +30 -17
  347. package/src/components/Command/command.principles.stories.tsx +10 -12
  348. package/src/components/Command/command.spec.md +5 -5
  349. package/src/components/Command/command.stories.tsx +4 -4
  350. package/src/components/Command/command.tsx +3 -2
  351. package/src/components/DataTable/active-editor-controller.ts +4 -3
  352. package/src/components/DataTable/cell-registry.tsx +8 -0
  353. package/src/components/DataTable/data-table-filter-panel.tsx +8 -2
  354. package/src/components/DataTable/data-table-interaction-layer.tsx +8 -7
  355. package/src/components/DataTable/data-table.anatomy.stories.tsx +4 -4
  356. package/src/components/DataTable/data-table.css +2 -2
  357. package/src/components/DataTable/data-table.spec.md +72 -115
  358. package/src/components/DataTable/data-table.stories.tsx +51 -3
  359. package/src/components/DataTable/data-table.tsx +348 -63
  360. package/src/components/DataTable/filter-operators.spec.md +4 -4
  361. package/src/components/DataTable/filter-tree.ts +192 -42
  362. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +1 -1
  363. package/src/components/DateGrid/date-grid.spec.md +11 -8
  364. package/src/components/DateGrid/date-grid.tsx +63 -8
  365. package/src/components/DatePicker/date-picker.principles.stories.tsx +1 -1
  366. package/src/components/DatePicker/date-picker.spec.md +1 -0
  367. package/src/components/DatePicker/date-picker.stories.tsx +19 -0
  368. package/src/components/DatePicker/date-picker.tsx +52 -15
  369. package/src/components/DescriptionList/description-list.spec.md +1 -1
  370. package/src/components/DescriptionList/description-list.tsx +3 -1
  371. package/src/components/Dialog/dialog.spec.md +1 -1
  372. package/src/components/Dialog/dialog.tsx +2 -1
  373. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +3 -1
  374. package/src/components/DropdownMenu/dropdown-menu.spec.md +8 -5
  375. package/src/components/DropdownMenu/dropdown-menu.tsx +33 -7
  376. package/src/components/Empty/empty.anatomy.stories.tsx +1 -1
  377. package/src/components/Empty/empty.spec.md +2 -2
  378. package/src/components/Empty/empty.stories.tsx +4 -15
  379. package/src/components/Empty/empty.tsx +4 -1
  380. package/src/components/Field/field-controls.spec.md +2 -1
  381. package/src/components/Field/field-wrapper.tsx +28 -8
  382. package/src/components/Field/field.anatomy.stories.tsx +7 -7
  383. package/src/components/Field/field.principles.stories.tsx +1 -1
  384. package/src/components/Field/field.spec.md +9 -7
  385. package/src/components/Field/field.tsx +2 -1
  386. package/src/components/Field/form-validation.spec.md +6 -3
  387. package/src/components/Field/use-form-validation.ts +49 -21
  388. package/src/components/FieldControlGroup/field-control-group.anatomy.stories.tsx +11 -10
  389. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
  390. package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
  391. package/src/components/FieldControlGroup/field-control-group.tsx +3 -3
  392. package/src/components/FileItem/file-item.anatomy.stories.tsx +6 -4
  393. package/src/components/FileItem/file-item.tsx +3 -2
  394. package/src/components/FileUpload/file-upload.anatomy.stories.tsx +1 -1
  395. package/src/components/FileUpload/file-upload.spec.md +4 -1
  396. package/src/components/FileUpload/file-upload.tsx +36 -10
  397. package/src/components/FileViewer/can-render-image.ts +15 -0
  398. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +6 -4
  399. package/src/components/FileViewer/file-viewer.spec.md +2 -2
  400. package/src/components/FileViewer/file-viewer.tsx +47 -21
  401. package/src/components/FileViewer/image-renderer.tsx +3 -8
  402. package/src/components/HoverCard/hover-card.spec.md +1 -1
  403. package/src/components/HoverCard/hover-card.tsx +3 -1
  404. package/src/components/Input/input.anatomy.stories.tsx +4 -4
  405. package/src/components/Input/input.principles.stories.tsx +1 -1
  406. package/src/components/Input/input.spec.md +1 -1
  407. package/src/components/Input/input.stories.tsx +11 -5
  408. package/src/components/Input/input.tsx +11 -9
  409. package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -0
  410. package/src/components/LinkInput/link-input.tsx +49 -34
  411. package/src/components/Menu/menu-item.anatomy.stories.tsx +1 -1
  412. package/src/components/Menu/menu-item.spec.md +4 -3
  413. package/src/components/Menu/menu-item.tsx +15 -4
  414. package/src/components/Notice/notice.anatomy.stories.tsx +10 -5
  415. package/src/components/Notice/notice.spec.md +4 -3
  416. package/src/components/Notice/notice.tsx +22 -5
  417. package/src/components/NumberInput/number-input.anatomy.stories.tsx +5 -4
  418. package/src/components/NumberInput/number-input.principles.stories.tsx +2 -1
  419. package/src/components/NumberInput/number-input.spec.md +2 -1
  420. package/src/components/NumberInput/number-input.tsx +25 -9
  421. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +3 -3
  422. package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
  423. package/src/components/OverflowIndicator/overflow-indicator.tsx +2 -1
  424. package/src/components/Pagination/index.ts +5 -0
  425. package/src/components/Pagination/pagination.anatomy.stories.tsx +143 -0
  426. package/src/components/Pagination/pagination.principles.stories.tsx +81 -0
  427. package/src/components/Pagination/pagination.spec.md +150 -0
  428. package/src/components/Pagination/pagination.stories.tsx +98 -0
  429. package/src/components/Pagination/pagination.tsx +263 -0
  430. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +1 -1
  431. package/src/components/PeoplePicker/people-picker.spec.md +4 -4
  432. package/src/components/PeoplePicker/people-picker.stories.tsx +1 -1
  433. package/src/components/PeoplePicker/people-picker.tsx +65 -55
  434. package/src/components/PeoplePicker/person-display.tsx +10 -4
  435. package/src/components/Popover/popover.anatomy.stories.tsx +2 -2
  436. package/src/components/Popover/popover.spec.md +13 -14
  437. package/src/components/Popover/popover.tsx +88 -39
  438. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +4 -4
  439. package/src/components/ProfileCard/profile-card.principles.stories.tsx +2 -2
  440. package/src/components/ProfileCard/profile-card.spec.md +1 -2
  441. package/src/components/ProfileCard/profile-card.stories.tsx +1 -1
  442. package/src/components/ProfileCard/profile-card.tsx +29 -8
  443. package/src/components/RadioGroup/radio-group.principles.stories.tsx +1 -1
  444. package/src/components/RadioGroup/radio-group.spec.md +2 -2
  445. package/src/components/RadioGroup/radio-group.stories.tsx +3 -1
  446. package/src/components/RadioGroup/radio-group.tsx +37 -3
  447. package/src/components/Rating/rating.anatomy.stories.tsx +11 -8
  448. package/src/components/Rating/rating.principles.stories.tsx +1 -1
  449. package/src/components/Rating/rating.spec.md +2 -2
  450. package/src/components/Rating/rating.stories.tsx +1 -1
  451. package/src/components/Rating/rating.tsx +3 -3
  452. package/src/components/ScrollArea/scroll-area.spec.md +1 -0
  453. package/src/components/ScrollArea/scroll-area.tsx +1 -1
  454. package/src/components/SegmentedControl/segmented-control.anatomy.stories.tsx +3 -3
  455. package/src/components/SegmentedControl/segmented-control.spec.md +7 -3
  456. package/src/components/SegmentedControl/segmented-control.tsx +29 -11
  457. package/src/components/Select/select.anatomy.stories.tsx +2 -2
  458. package/src/components/Select/select.spec.md +3 -2
  459. package/src/components/Select/select.tsx +62 -19
  460. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +1 -1
  461. package/src/components/SelectMenu/select-menu.principles.stories.tsx +1 -1
  462. package/src/components/SelectMenu/select-menu.spec.md +12 -8
  463. package/src/components/SelectMenu/select-menu.tsx +176 -20
  464. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
  465. package/src/components/SelectionControl/selection-item.principles.stories.tsx +2 -2
  466. package/src/components/SelectionControl/selection-item.spec.md +2 -1
  467. package/src/components/SelectionControl/selection-item.stories.tsx +1 -1
  468. package/src/components/Separator/separator.anatomy.stories.tsx +2 -2
  469. package/src/components/Separator/separator.tsx +1 -1
  470. package/src/components/Sheet/sheet.spec.md +2 -1
  471. package/src/components/Sheet/sheet.tsx +4 -2
  472. package/src/components/Sidebar/sidebar.anatomy.stories.tsx +1 -1
  473. package/src/components/Sidebar/sidebar.spec.md +2 -1
  474. package/src/components/Sidebar/sidebar.stories.tsx +6 -4
  475. package/src/components/Sidebar/sidebar.tsx +41 -13
  476. package/src/components/Skeleton/skeleton.principles.stories.tsx +1 -1
  477. package/src/components/Skeleton/skeleton.spec.md +1 -2
  478. package/src/components/Skeleton/skeleton.tsx +1 -1
  479. package/src/components/Slider/slider.anatomy.stories.tsx +4 -4
  480. package/src/components/Slider/slider.spec.md +19 -85
  481. package/src/components/Slider/slider.tsx +13 -5
  482. package/src/components/Steps/steps.anatomy.stories.tsx +1 -1
  483. package/src/components/Steps/steps.spec.md +6 -4
  484. package/src/components/Steps/steps.tsx +11 -3
  485. package/src/components/Switch/switch.spec.md +4 -3
  486. package/src/components/Switch/switch.tsx +59 -10
  487. package/src/components/Tabs/tabs.anatomy.stories.tsx +7 -6
  488. package/src/components/Tabs/tabs.principles.stories.tsx +5 -3
  489. package/src/components/Tabs/tabs.spec.md +15 -48
  490. package/src/components/Tabs/tabs.tsx +70 -34
  491. package/src/components/Tag/tag.principles.stories.tsx +2 -2
  492. package/src/components/Tag/tag.spec.md +11 -43
  493. package/src/components/Tag/tag.tsx +30 -4
  494. package/src/components/Textarea/textarea.principles.stories.tsx +2 -1
  495. package/src/components/Textarea/textarea.spec.md +2 -1
  496. package/src/components/Textarea/textarea.tsx +20 -10
  497. package/src/components/TimePicker/time-picker.anatomy.stories.tsx +2 -1
  498. package/src/components/TimePicker/time-picker.spec.md +2 -2
  499. package/src/components/TimePicker/time-picker.tsx +35 -16
  500. package/src/components/Toast/toast.anatomy.stories.tsx +5 -6
  501. package/src/components/Toast/toast.principles.stories.tsx +10 -11
  502. package/src/components/Toast/toast.spec.md +5 -5
  503. package/src/components/Toast/toast.stories.tsx +5 -1
  504. package/src/components/Toast/toast.tsx +13 -7
  505. package/src/components/Tooltip/tooltip.spec.md +3 -3
  506. package/src/components/Tooltip/tooltip.tsx +1 -1
  507. package/src/components/TreeView/tree-view.anatomy.stories.tsx +6 -1
  508. package/src/components/TreeView/tree-view.principles.stories.tsx +2 -2
  509. package/src/components/TreeView/tree-view.spec.md +2 -2
  510. package/src/components/TreeView/tree-view.tsx +27 -5
  511. package/src/hooks/use-controllable.ts +7 -2
  512. package/src/hooks/use-is-touch-device.ts +8 -1
  513. package/src/index.ts +2 -1
  514. package/src/patterns/element-anatomy/element-anatomy.spec.md +3 -3
  515. package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
  516. package/src/patterns/element-anatomy/item-anatomy.spec.md +5 -14
  517. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +1 -0
  518. package/src/patterns/element-anatomy/item-anatomy.tsx +5 -6
  519. package/src/patterns/header-canonical/header-canonical.css +1 -2
  520. package/src/patterns/header-canonical/header-canonical.spec.md +5 -5
  521. package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +5 -4
  522. package/src/patterns/horizontal-overflow/horizontal-overflow.stories.tsx +137 -0
  523. package/src/patterns/overlay-surface/overlay-surface.spec.md +8 -7
  524. package/src/patterns/overlay-surface/overlay-surface.stories.tsx +116 -0
  525. package/src/patterns/overlay-surface/overlay-surface.tsx +5 -5
  526. package/src/patterns/resize-handle/resize-handle.spec.md +1 -1
  527. package/src/tokens/categorical-color.ts +1 -1
  528. package/src/tokens/color/color.spec.md +11 -13
  529. package/src/tokens/color/color.stories.tsx +1 -1
  530. package/src/tokens/color/primitives.css +7 -5
  531. package/src/tokens/color/semantic.css +8 -2
  532. package/src/tokens/density/density.spec.md +2 -0
  533. package/src/tokens/elevation/elevation.spec.md +1 -1
  534. package/src/tokens/layoutSpace/layoutSpace.spec.md +2 -1
  535. package/src/tokens/layoutSpace/layoutSpace.stories.tsx +3 -3
  536. package/src/tokens/motion/motion.spec.md +1 -1
  537. package/src/tokens/motion/motion.stories.tsx +142 -0
  538. package/src/tokens/opacity/opacity.spec.md +1 -1
  539. package/src/tokens/opacity/opacity.stories.tsx +7 -11
  540. package/src/tokens/radius/radius.spec.md +1 -1
  541. package/src/tokens/typography/typography.css +1 -1
  542. package/src/tokens/typography/typography.spec.md +1 -1
  543. package/src/tokens/uiSize/icon-size.ts +2 -2
  544. package/src/tokens/uiSize/uiSize.spec.md +12 -11
  545. package/src/tokens/uiSize/uiSize.stories.tsx +103 -0
  546. package/src/tokens/utility-registry.json +5 -4
  547. package/dist/node_modules/react-hook-form/dist/index.esm.js +0 -1950
  548. package/dist/node_modules/react-hook-form/dist/index.esm.js.map +0 -1
@@ -17,7 +17,7 @@ Skeleton.displayName = "Skeleton"
17
17
  // Phase 2 fill needed: purpose descriptions + when rationale + world-class refs
18
18
  export const skeletonMeta = {
19
19
  component: 'Skeleton',
20
- family: null, // non-family composite / overlay / layout
20
+ family: 'self-contained', // 對齊 skeleton.spec.md frontmatter(SSOT;同 ScrollArea meta 寫法)
21
21
  variants: {
22
22
 
23
23
  },
@@ -108,9 +108,9 @@ export const ColorMatrix: Story = {
108
108
  <tr><Td>Thumb 底色 disabled</Td><Td><TokenCell token="--canvas" display="bg-canvas(不透明背景色,沉回)" /></Td><Td><TokenCell token="--canvas" /></Td></tr>
109
109
  <tr><Td>Thumb 邊框 default</Td><Td><TokenCell token="--primary" display="border-2 border-primary" /></Td><Td><TokenCell token="--primary" display="--primary(與 Range default 同色)" /></Td></tr>
110
110
  <tr><Td>Thumb 邊框 disabled</Td><Td><TokenCell token="--border" display="border-border" /></Td><Td><TokenCell token="--border" display="--border(與 Range disabled 同色)" /></Td></tr>
111
- <tr><Td>Thumb hover</Td><Td mono>border primary-hover + --elevation-100</Td><Td>—</Td></tr>
112
- <tr><Td>Thumb active</Td><Td mono>--elevation-200</Td><Td>—</Td></tr>
113
- <tr><Td>Thumb focus</Td><Td mono>border-primary-hover(加深,不加 ring/halo)</Td><Td><TokenCell token="--primary-hover" /></Td></tr>
111
+ <tr><Td>Thumb hover</Td><Td mono>border primary-hover(升 hover 階)+ --elevation-100</Td><Td>—</Td></tr>
112
+ <tr><Td>Thumb active(按壓拖曳)</Td><Td mono>border primary-active(深一階)+ --elevation-200</Td><Td><TokenCell token="--primary-active" /></Td></tr>
113
+ <tr><Td>Thumb focus</Td><Td mono>border-primary-hover( hover 視覺,不加 ring/halo)</Td><Td><TokenCell token="--primary-hover" /></Td></tr>
114
114
  </tbody>
115
115
  </table>
116
116
  </div>
@@ -230,7 +230,7 @@ export const Accessibility = {
230
230
  render: () => (
231
231
  <div className="max-w-3xl text-body text-fg-secondary">
232
232
  <h3 className="text-h5 text-foreground mb-2">無障礙設計</h3>
233
- <p className="whitespace-pre-line">{"ARIA / 角色:繼承 Radix slider 的無障礙預設(role、aria-* 屬性、鍵盤導覽都由 primitive 提供)。\n\n鍵盤操作:\n\n- Tab — 聚焦到 thumb\n- ←/→ — 微調數值\n- Home/End — 跳到最小/最大值\n- PageUp/PageDown — 大步階調整\n\n聚焦樣式:thumb 鍵盤聚焦時,邊框加深成 primary-hover (跟滑鼠 hover 同一個視覺),不另外加外框光暈(ring/halo)。\n\n驗證:Storybook a11y 面板應為 0 個 critical violation;不用滑鼠、純鍵盤也能完整操作。文字對比 ≥ 4.5:1、UI 元件對比 ≥ 3:1(WCAG AA)。"}</p>
233
+ <p className="whitespace-pre-line">{"ARIA / 角色:繼承 Radix slider 的無障礙預設(role、aria-* 屬性、鍵盤導覽都由 primitive 提供)。\n\n鍵盤操作:\n\n- Tab — 聚焦到 thumb\n- ←/→ — 微調數值\n- Home/End — 跳到最小/最大值\n- PageUp/PageDown — 大步階調整\n\n聚焦樣式:thumb 鍵盤聚焦時,邊框升 hover (跟滑鼠 hover 同一個視覺),不另外加外框光暈(ring/halo)。\n\n驗證:Storybook a11y 面板應為 0 個 critical violation;不用滑鼠、純鍵盤也能完整操作。文字對比 ≥ 4.5:1、UI 元件對比 ≥ 3:1(WCAG AA)。"}</p>
234
234
  </div>
235
235
  ),
236
236
  }
@@ -66,28 +66,9 @@ benchmark:
66
66
 
67
67
  ### 一種視覺,多種容器尺寸
68
68
 
69
- **Slider 的視覺是固定的**——track 厚度、thumb 直徑、thumb 邊框都是單一值,不會隨 `size` 變動(focus 不加 ring / halo,改用 border 加深)。這是跟 Button / Input 等元件的明顯差異,理由是:
69
+ **track / thumb 是單一視覺規格**(track 4px、thumb 16px、邊框 2px,皆不隨 `size` 變):thumb 是位置指示器,必須夠大好捕捉(Fitts's Law),等比縮小會直接傷可用性;世界級同款——Material 3(track 4dp / thumb 20dp)/ iOS(3pt / 28pt)/ Ant(4px / 14px)/ Radix,全部 thumb / track 尺寸獨立於 form-size system。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
70
70
 
71
- - Slider thumb 必須足夠容易被手指 / 鼠標捕捉,改小會降低可用性
72
- - 連續拖曳的 track 太細會看不清楚 range
73
- - 業界(Material / Ant / Radix Themes / shadcn)都只有一組 thumb / track 視覺,不做多尺寸
74
-
75
- **但**,Slider 可以被丟進 `Field` 容器跟 `Input` / `Select` / `NumberInput` 等並排,這時它**必須跟該 Field 的 `size` 對齊高度**(否則一排 sm field 裡混一個 md 高度的 slider,breakline rhythm 會崩)。
76
-
77
- ### 解決方式:`size` prop 只控容器外高
78
-
79
- Slider 接收 `size?: 'sm' | 'md' | 'lg'` prop(預設 `md`),**這個 prop 只決定 root 容器的 `h-field-*` class**,不改變 track 厚度、thumb 直徑、thumb 邊框尺寸。內部視覺元素透過 flex `items-center` 在容器中**垂直置中**,所以不論容器高度是 28 / 32 / 36 px,track 和 thumb 的位置都在中間,視覺體驗一致。
80
-
81
- 這樣做的效果:
82
- - 視覺:一種尺寸,任何 Field context 長得都一樣
83
- - 對齊:完美對齊 Field family 的 field-height tier
84
- - API 一致性:跟 Input / NumberInput 同樣有 `size` prop,消費者不需要特別記「Slider 沒有 size」
85
-
86
- ### 為什麼不完全對齊 `--field-height-*`
87
-
88
- - **現況**:容器高 = `--field-height-sm/md/lg`(對齊);track 厚度固定 4px / thumb 直徑固定 16px / thumb 邊框固定 2px,**皆不隨 size 變化**(偏離 size-proportional scaling)。focus 不加 ring / halo,改用 border 加深(詳「視覺規格」表)
89
- - **Rationale**:Slider 的 thumb 是「位置指示器」,必須足夠大好捕捉(Fitts's Law);縮小成比例(sm=12 / lg=20 之類)會讓 sm 幾乎無法精確拖曳。業界共識:track/thumb 是**單一視覺規格**,`size` 只控容器外高讓 Slider 並排對齊,內部透過 `flex items-center` 垂直置中
90
- - **世界級對照**:Material 3 Slider(track 4dp thumb 20dp 固定)/ iOS UISlider(track 3pt thumb 28pt 固定)/ Ant Design Slider(track 4px thumb 14px 固定)/ Radix Slider(default 4px track)——全部 thumb / track 尺寸獨立於 form-size system <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
71
+ `size?: 'sm' | 'md' | 'lg'`(預設 `md`)**只控 root 容器外高 `h-field-*`**,內部視覺 `flex items-center` 垂直置中——Slider 丟進 `Field` Input / Select / NumberInput 並排時對齊 field-height tier(否則一排 sm field 混一個 md 高 slider,rhythm 會崩),同時保持「一種尺寸,任何 context 長得都一樣」;API 與其他 field 元件一致(都有 `size`),消費者不需特別記「Slider 沒有 size」。focus 不加 ring / halo,改用 border 階變(詳「視覺規格」表)。
91
72
 
92
73
  ---
93
74
 
@@ -105,9 +86,9 @@ Slider 接收 `size?: 'sm' | 'md' | 'lg'` prop(預設 `md`),**這個 prop 只決
105
86
  | **Thumb** 底色 disabled | `bg-canvas`(不透明頁面背景色,沉回背景;= Radix disabled thumb gray-1 同款) | `--canvas` |
106
87
  | **Thumb** 邊框 rest | 2px,色 = Range rest 同 token | `--primary` |
107
88
  | **Thumb** 邊框 disabled | `border-border`(= Range disabled 同色)| `--border` |
108
- | **Thumb** hover | border `primary-hover` + 陰影 `--elevation-100` | `--primary-hover` |
109
- | **Thumb** active | 陰影 `--elevation-200` | |
110
- | **Thumb** focus | border `primary-hover`(`outline-none focus-visible:border-primary-hover`,不加 ring / halo,跟 hover 同視覺)| `--primary-hover` |
89
+ | **Thumb** hover | border 升 hover 階(light mode 淺一階 lift,= Button primary hover 邏輯)+ 陰影 `--elevation-100` | `--primary-hover` |
90
+ | **Thumb** active(按壓拖曳) | border 深一階 `primary-active`(= Button active 邏輯;2026-07-06 修:原誤用 hover 階,全 DS 唯一 active-用-hover 偏移)+ 陰影 `--elevation-200` | `--primary-active` |
91
+ | **Thumb** focus | border hover 視覺(`outline-none focus-visible:border-primary-hover`,不加 ring / halo)| `--primary-hover` |
111
92
  | **Disabled cursor** | `cursor-not-allowed` + hover 陰影關閉 | — |
112
93
 
113
94
  ### 為什麼 thumb 是**白底 + 邊框**,不是**實心 primary**
@@ -116,14 +97,7 @@ Slider 接收 `size?: 'sm' | 'md' | 'lg'` prop(預設 `md`),**這個 prop 只決
116
97
 
117
98
  ### 為什麼 Track 底色維持「灰色凹槽」身分,不跟 enabled state 變動
118
99
 
119
- Track 是「凹槽底線」,它的視覺角色是「告訴使用者這條線是 slider 可滑動範圍」——這個語意不會因為 enabled / disabled 改變,所以**色相 / 角色也不該變**。rest `bg-secondary`(neutral-3,「微淡可辨」),disabled 才退化成 `bg-muted`(neutral-2);兩者都在最淡的 subtle bg 層級,只是深淺差一階,凹槽身分始終不變。當整個 slider 被 disable 時,Range Thumb border 會降級(rest primary → border),Track 也同步退一階灰。
120
-
121
- 為什麼用 `--secondary` / `--muted` 而非其他 token?
122
- - 兩者都是 semantic token,屬於 subtle bg 家族,語意歸屬明確
123
- - 是最淡的 subtle bg,符合「track 是背景凹槽,不是主角」的視覺層級
124
- - **優先用 semantic token,不直接引用 primitive**——這是系統的基本原則
125
-
126
- > **若覺得 track 底色在 white canvas 上太淡**,那是 `--secondary` / `--muted` 本身的系統級議題,應該統一調整其定義(影響 Badge / skeleton / Slider 一起),而不是在 Slider 裡偷偷用 primitive 繞過。
100
+ Track 的視覺角色是「可滑動範圍的凹槽底線」,語意不因 enabled / disabled 改變——rest `bg-secondary`(neutral-3)disabled 退一階 `bg-muted`(neutral-2),都在最淡 subtle bg 層級、凹槽身分不變(disable Range / Thumb border 同步降級 primary → border)。semantic token 優先、不直接引 primitive;**若覺得 track 在 white canvas 上太淡**,那是 `--secondary` / `--muted` 的系統級議題(Badge / Skeleton 一起調),不是在 Slider 裡用 primitive 繞過的理由。
127
101
 
128
102
  ### Range 色 ↔ Thumb border 色的綁定規則
129
103
 
@@ -134,13 +108,11 @@ Range 填滿色和 Thumb border 色**永遠是同一個 token**:
134
108
  | Rest | `bg-primary` | `border-primary` | `--primary` |
135
109
  | Disabled | `bg-border` | `border-border` | `--border` |
136
110
 
137
- **為什麼綁在一起**:thumb 坐落在 range 的端點上,border 是 range 的視覺延續;兩者同色讓 thumb 作為「range 的終點標記」而非獨立元素,對齊物理滑桿 handle 屬於軌道一部分的 mental model(具體視覺呈現見 `.principles.stories.tsx` ThumbBindingRule)。
138
-
139
- **強制規則**:未來若要改 Range 的 disabled 色(例如加新 variant),必須同步改 Thumb border。不要讓兩者漂移。
111
+ **為什麼綁在一起**:thumb range 的端點標記、border 是 range 的視覺延續(物理滑桿 handle 屬於軌道的 mental model;呈現見 principles `ThumbBindingRule`)。**強制規則**:改 Range 任一 state 色必同步 Thumb border,不許漂移。
140
112
 
141
113
  ### 為什麼 hover / active 用陰影不用色變
142
114
 
143
- Slider 不是 button——它是「當前位置指示器」,底色不該動(動了會暗示是另一個狀態)。陰影(elevation)是世界級 slider 的標準 hover 語言:Material 3 的 state layer + elevation、iOS slider 的 scale + shadow、Linear 的 drop shadow。對齊 `elevation.spec.md` 的 `--elevation-100` / `--elevation-200`。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
115
+ Slider 不是 button——它是「當前位置指示器」,底色不該動(動了會暗示是另一個狀態);「不用色變」指 thumb 底色與 range 填色;thumb border 仍有同色相變化:hover / focus 升 hover 階(淺一階 lift)、按壓拖曳深至 `primary-active`(2026-07-06 修語:原文「加深至 primary-hover」與 token 實際方向相反,見視覺規格表)。陰影(elevation)是世界級 slider 的標準 hover 語言:Material 3 的 state layer + elevation、iOS slider 的 scale + shadow、Linear 的 drop shadow。對齊 `elevation.spec.md` 的 `--elevation-100` / `--elevation-200`。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
144
116
 
145
117
  ---
146
118
 
@@ -193,36 +165,20 @@ Radix Slider 原生支援多 thumb——只要 `value` / `defaultValue` 傳長
193
165
  | State | 視覺 | 觸發 |
194
166
  |---|---|---|
195
167
  | Rest | track `bg-secondary`,range `bg-primary`,thumb `bg-on-emphasis + border-primary` | 預設 |
196
- | Hover(thumb) | 加 `--elevation-100` 陰影 | 滑鼠 hover 在 thumb 上 |
197
- | Active(拖曳中) | 加 `--elevation-200` 陰影 | 按住拖曳 |
198
- | Focus | thumb border 加深成 `primary-hover`(跟 hover 同視覺,不加 ring / halo)| 鍵盤 Tab 聚焦 |
168
+ | Hover(thumb) | thumb border 升 hover 階 `primary-hover` + 加 `--elevation-100` 陰影 | 滑鼠 hover 在 thumb 上 |
169
+ | Active(拖曳中) | border 深一階 `primary-active` + 加 `--elevation-200` 陰影 | 按住拖曳 |
170
+ | Focus | thumb border hover 階 `primary-hover`(跟 hover 同視覺,不加 ring / halo)| 鍵盤 Tab 聚焦 |
199
171
  | Disabled | 灰階降級:range `bg-border`、thumb `border-border`(= range 同 token)、thumb bg 沉回 `bg-canvas`(不透明背景色)、`cursor-not-allowed`、hover 陰影關閉 | `disabled` prop 或 Field context disabled |
200
172
 
201
173
  **Mode / readonly / dark mode / density** 詳見 `../Field/field-controls.spec.md`(Slider 作為 Field 家族整合時繼承其 canonical;semantic token 自動處理 dark mode,無需元件內特殊 handling)。
202
174
 
203
- ### Disabled 視覺階層
204
-
205
- ```
206
- track (muted, n-2) < range = thumb border (border, n-5) < text (n-7+)
207
- ```
175
+ ### Disabled 視覺階層(三階,不是四階)
208
176
 
209
- **三階**,不是四階。Range Thumb border 刻意用同一個 token(見前面「Range Thumb border 色的綁定規則」),不拆成兩階。
177
+ `track(muted, n-2)< range = thumb border(border, n-5)< text(n-7+)`——Range Thumb border 刻意同 token 不拆階(見綁定規則);thumb fill(rest / disabled canvas)與 range fill 對比、位置由 range 長度段決定;text 永遠最上層。
210
178
 
211
- - **Track n-2**:最底的凹槽底線
212
- - **Range + Thumb border n-5**:同層——thumb border 是 range 的連續視覺,兩者融為一體;thumb 的 fill(rest 白 / disabled canvas)與 range fill 對比,位置由 range 的長度段決定
213
- - **Text n-7+**:label / description 在所有視覺元件之上
179
+ ### 為什麼 range disabled 用 `--border` 不用 `--fg-disabled`
214
180
 
215
- ### 為什麼 range disabled 不用 `--fg-disabled`
216
-
217
- `--fg-disabled`(neutral-6)的語意是「**disabled 狀態下的文字前景色**」,它存在的目的是讓 disabled text 有足夠對比度可讀。把它拿來當 range 的 background 是**混用 fg token 當 bg 色**——在設計系統語意上是 smell,偶然 work 只是因為 neutral-6 剛好是我想要的深度。
218
-
219
- 正確選擇是 `--border`(neutral-5):
220
- - **語意對**:`--border` 屬於 Border family,「視覺分隔 / 容器邊框」語意,跟 range 的「填充視覺指示器」角色接近
221
- - **Family 對**:非文字的視覺元素,不借用 fg family
222
- - **可同時作為 border-color**:`border-border` 在 Thumb 上直接用,Range bg 跟 Thumb border 可共享同一個 token(這是綁定規則的前提)
223
- - **跟 fg-disabled 解耦**:未來若 `--fg-disabled` 被微調,Slider 不會被拖著一起變
224
-
225
- 詳細的 fg / bg token family 借用規則見 `tokens/color/color.spec.md` 的「fg token 不可當 bg 用」節。
181
+ `--fg-disabled` 是「disabled 文字前景」語意,拿來當 bg = fg token 混用當 bg 的 smell(偶然 work 只因 neutral-6 深度剛好)。`--border`(neutral-5)語意對(視覺分隔家族、非文字元素不借 fg)、可同時作 Thumb 的 border-color(綁定規則的前提)、與 fg-disabled 解耦。詳 `tokens/color/color.spec.md`「fg token 不可當 bg 用」節。
226
182
 
227
183
  ---
228
184
 
@@ -239,27 +195,9 @@ track (muted, n-2) < range = thumb border (border, n-5) < text (n-7+)
239
195
  | **Button** | Primary 色是品牌美學,不是 state | — | **灰階 swap** |
240
196
  | **Slider** | Range 藍色是「已選長度」的美學視覺,**位置 + 長度是 state**,顏色只是裝飾 | **位置(thumb)+ 長度(range 佔比)** | **灰階 swap** — 灰色 range 跟灰色 thumb 的位置/長度仍然完全可辨 |
241
197
 
242
- ### Slider 的完整論證
243
-
244
- 使用者看到 disabled slider 需要理解兩件事:
245
- 1. **Thumb 在 track 的哪個位置**(例如「60% 處」)
246
- 2. **Range 的填滿長度**(例如「20% 到 80%」)
247
-
248
- 這兩個資訊**完全不依賴顏色**:
249
- - 灰階 thumb 在灰階 track 上,x 座標位置跟 primary 版本一模一樣
250
- - 灰階填滿段的長度跟 primary 版本一模一樣
251
-
252
- **失去藍色沒有任何資訊損失**。Slider 跟 Checkbox 同類——semantic state 由形狀/位置承載,顏色是純裝飾。
198
+ ### 論證摘要
253
199
 
254
- ### 對照 Switch 的差異
255
-
256
- Switch 是**唯一需要 opacity 的特例**,因為它的 on/off 沒有任何形狀差異:
257
- - On:track + thumb
258
- - Off:track + thumb(同樣形狀,thumb 在不同位置)
259
-
260
- 唯一的 state 視覺載體是 track 顏色(primary vs border)。灰階 swap 會讓 on/off 視覺無法區分——必須靠 opacity 保留顏色。
261
-
262
- **這個 rationale 不適用 Slider**,因為 Slider 的 state 載體不是顏色。
200
+ Disabled slider 要傳達的兩件事——thumb 位置、range 長度——**完全不依賴顏色**(灰階後 x 座標與填滿佔比一模一樣),失去藍色零資訊損失,故走灰階派(同 Checkbox:state 由形狀 / 位置承載、顏色是裝飾)。Switch 是唯一 opacity 特例:on/off 無任何形狀差異、顏色本身就是 state,灰階會讓兩態無法區分——此 rationale 不適用 Slider。
263
201
 
264
202
  ### 為什麼 shadcn / Material 3 用 opacity 仍然不是理由
265
203
 
@@ -322,11 +260,7 @@ Slider 無獨立 `readOnly` prop;但在 `<Field mode="readonly">` 內(2026-06-12
322
260
 
323
261
  ## Inspector 用途
324
262
 
325
- Slider 的可調維度是 `min` / `max` / `step` / `defaultValue`(值域與步進)× `size`(容器外高 tier)。`Inspector` story 提供右側 Controls 即時切這些 prop,讓設計師看值域行為與容器高度 tier(track / thumb 視覺固定,只有容器外高隨 `size` 變)。
326
-
327
- `size` / `range mode` / track / range / thumb 的色彩綁定規則這類「對照題」(side-by-side 比較,非單值拉動),另由 `SizeMatrix` / `StateBehavior` / `ColorMatrix` / 元件特有 `ColorBindingRule`(range ↔ thumb border 綁定規則) / `KeyboardMatrix`(Radix 原生鍵盤) 各自的 matrix story 完整覆蓋——綁定規則與鍵盤對照用 matrix 呈現比 Inspector 試玩更清楚。
328
-
329
- 對應 anatomy story:`Overview` + `Inspector` + `ColorMatrix` + `SizeMatrix` + `StateBehavior` + 元件特有 `ColorBindingRule` + `KeyboardMatrix` + `Accessibility`。
263
+ Inspector 提供 `min` / `max` / `step` / `defaultValue` × `size` 即時調整(track / thumb 視覺固定,只有容器外高隨 size 變);色彩綁定 / 鍵盤等 side-by-side 對照題由各 matrix story 覆蓋。對應 anatomy story:`Overview` + `Inspector` + `ColorMatrix` + `SizeMatrix` + `StateBehavior` + 元件特有 `ColorBindingRule` + `KeyboardMatrix` + `Accessibility`。
330
264
 
331
265
  ---
332
266
 
@@ -346,7 +280,7 @@ Slider 的可調維度是 `min` / `max` / `step` / `defaultValue`(值域與步
346
280
 
347
281
  **Keyboard 行為**:完整鍵盤對照見上方「鍵盤操作(Radix 原生,免手工)」節,不重複列。
348
282
 
349
- **Focus**:Radix primitive 自管 focus / restoration;thumb 鍵盤聚焦時 `outline-none focus-visible:border-primary-hover`(border 加深成 primary-hover,跟 hover 同視覺,不加 ring / halo)。
283
+ **Focus**:Radix primitive 自管 focus / restoration;thumb 鍵盤聚焦時 `outline-none focus-visible:border-primary-hover`(border hover 階,跟 hover 同視覺,不加 ring / halo)。
350
284
 
351
285
  **驗證**:Storybook a11y addon panel 應 0 critical violation;鍵盤完整可操作(無需滑鼠)。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。
352
286
 
@@ -50,7 +50,9 @@ const sliderRootVariants = cva(
50
50
  )
51
51
 
52
52
  export interface SliderProps
53
- extends Omit<React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>, 'children'>,
53
+ // 'orientation' 一併 Omit(2026-07-07 deep-audit A.1b 殘項):spec「不支援 vertical」宣稱的
54
+ // 型別面機械封鎖 — DS 樣式(track h-1 / w-full / items-center)假設 horizontal,透傳 vertical 壞版。
55
+ extends Omit<React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>, 'children' | 'orientation'>,
54
56
  VariantProps<typeof sliderRootVariants> {}
55
57
 
56
58
  // code-quality-allow: long-function — foundational composite(thumb-count 推導 + Field disabled 整合 + multi-mode render)
@@ -121,7 +123,9 @@ const Slider = React.forwardRef<
121
123
  - Rest: `border-primary` ↔ Range `bg-primary`
122
124
  - Disabled: `border-border` ↔ Range `bg-border`
123
125
  這個一致性讓 thumb border 跟 range 融為一體,看起來像「range 包住 thumb」
124
- 的連續視覺。不論 state,thumb border range 永遠同色。
126
+ 的連續視覺。**同色綁定 scope = rest disabled 兩態**;互動態(hover/active/focus)
127
+ 只動 thumb border 色階(+ elevation),range 填色恆定 —— 2026-07-06 修正原
128
+ 「不論 state 永遠同色」過度宣稱(hover/active 一直都只動 thumb,claim 與 code 不符)。
125
129
 
126
130
  **Thumb bg 兩態(2026-06-12 user 拍板,深色模式破洞修正)**:
127
131
  - Rest/hover/active:`bg-on-emphasis`(固定白、深淺主題不反轉)— 對齊自家
@@ -142,10 +146,14 @@ const Slider = React.forwardRef<
142
146
  'block h-4 w-4 shrink-0 rounded-full cursor-grab',
143
147
  'bg-on-emphasis border-2 border-primary',
144
148
  'transition-all duration-150',
145
- // Hover:border 加深到 primary-hover + elevation 陰影
149
+ // Hover:border hover 階(light mode 淺一階 lift,= Button primary hover 變色邏輯)
150
+ // + elevation 陰影。2026-07-06 修語:原註解寫「加深」但 primary-hover 比 base 淺,
151
+ // 措辭與 token 相反 — token 不遷就錯註解,註解修正。
146
152
  'hover:border-primary-hover hover:[box-shadow:var(--elevation-100)]',
147
- 'active:cursor-grabbing active:border-primary-hover active:[box-shadow:var(--elevation-200)]',
148
- // Focus:border 加深(跟 hover 同視覺),不加 ring halo
153
+ // Active(按壓拖曳):深一階 primary-active(= Button active 邏輯 button.tsx active:*-active
154
+ // 對照組;原誤用 hover = DS 唯一 active-用-hover 偏移,2026-07-06 雙向全掃修正)
155
+ 'active:cursor-grabbing active:border-primary-active active:[box-shadow:var(--elevation-200)]',
156
+ // Focus:跟 hover 同視覺(hover 階),不加 ring 或 halo
149
157
  'outline-none focus-visible:border-primary-hover',
150
158
  // Disabled:border 跟 Range 一起退成 border(n-5),bg 沉回 canvas(不透明背景色)
151
159
  'data-[disabled]:cursor-not-allowed data-[disabled]:border-border data-[disabled]:bg-canvas',
@@ -382,7 +382,7 @@ export const StateBehavior: Story = {
382
382
  <span className="text-caption font-medium text-fg-secondary">行為 2:linear 控制 upcoming step 可否點擊</span>
383
383
  <Desc>
384
384
  `linear=true`(預設)= 只能依序前進,回查僅限 completed(對應 checkout 這種「不可跳過」流程)。
385
- `linear=false` + `reachableValues` = 自由跳轉,任何 reachable step 可點(對應「多 tab 表單編輯器」)。
385
+ `linear=false` = 自由跳轉,任何非 disabled step 都可點(對應「多 tab 表單編輯器」;reachable 是 linear 模式的內部推導態,非 prop)。
386
386
  </Desc>
387
387
  <LinearVsNonlinear />
388
388
  </div>
@@ -420,9 +420,9 @@ Description 在 error state 下維持 `text-fg-secondary`(跟其他 state 一樣
420
420
 
421
421
  ---
422
422
 
423
- ## 為何無 Inspector
423
+ ## Inspector 與矩陣分工
424
424
 
425
- Steps 的決策是「展示所有步驟進度」,需要一整條鏈才能呈現設計——單互動切換 value 不如 side-by-side 矩陣清楚。關鍵維度由 `OrientationMatrix` / `ColorMatrix`(含 4 狀態色)/ `SizeMatrix` / `StateBehavior`(進度流轉 / linear / error interrupt)/ 元件特有 `IndentAlignment` 五張 story 完整覆蓋。
425
+ Steps 的決策是「展示所有步驟進度」,需要一整條鏈才能呈現設計——關鍵維度由 `OrientationMatrix` / `ColorMatrix`(含 4 狀態色)/ `SizeMatrix` / `StateBehavior`(進度流轉 / linear / error interrupt)/ 元件特有 `IndentAlignment` 五張 side-by-side 矩陣 story 完整覆蓋;`Inspector`(元件檢閱器)另補單一互動 playground(Controls 切 value / completedValues / errorValues / size / orientation / linear / expansion,對齊 anatomy 6-canonical)。矩陣是設計比對主路徑,Inspector 供即時 prop 試切。
426
426
 
427
427
  ## StateBehavior 說明(Steps 層級特有)
428
428
 
@@ -432,7 +432,7 @@ Item-level **內容狀態色彩**(completed / current / upcoming / error indicat
432
432
 
433
433
  ## 邊界案例
434
434
 
435
- - **Disabled step**:`disabled` step 視覺繼承 SelectionItem disabled token(`text-fg-disabled`,M24),click 不觸發 navigate;`linear` mode 下 upcoming steps 預設 disabled 直到前面 completed
435
+ - **Disabled step**:`disabled` step 視覺繼承 SelectionItem disabled token(`text-fg-disabled`,M24),click 不觸發 navigate;`linear` mode 下 upcoming steps 預設不可點(non-clickable:無 `role="button"` / 不進 Tab 順序 / cursor-not-allowed,SR 念 sr-only「未開始」;與 `disabled` prop 不同,**不**設 `aria-disabled` / `data-disabled`)直到前面 completed 解鎖。
436
436
  - **Loading(async step state fetch)**:Steps primitive 為 sync render,async data fetch 應由 consumer 在外層 Skeleton 取代整個 Steps;Steps 內部不獨立 own loading prop,**也無 per-step loading state**(content state 集合不含 loading)— 單一步驟 async 進行中即 current,進度細節由 consumer 在 `StepContent` 內呈現。
437
437
  - **Empty(0 steps)**:無任何 `<StepItem>` children 時無進度可呈現;consumer 應條件性不渲 Steps 或渲 `<Empty>` 替代,不渲空 Steps container。
438
438
  - **Single step / `value` 指向第一步**:合法初始狀態,第一個 step 渲染 current,後續依推導為 reachable / upcoming。
@@ -448,7 +448,7 @@ Item-level **內容狀態色彩**(completed / current / upcoming / error indicat
448
448
  - `../Tabs/tabs.spec.md` — 平行視圖切換(非進度場景)
449
449
  - `../Breadcrumb/breadcrumb.spec.md` — 位置路徑(非進度場景)
450
450
  - `../RadioGroup/radio-group.spec.md` — 選值(非進度場景)
451
- - `../Avatar/avatar.tsx` — Indicator 32px 尺寸依據(`AVATAR_SIZE.block.sm/md = 32`)
451
+ - `../../patterns/element-anatomy/item-anatomy.tsx` — Indicator 32px 尺寸依據(`AVATAR_SIZE.block.sm/md = 32`;`AVATAR_SIZE` 常數住 item-anatomy,非 avatar.tsx — Avatar 本身 size 為任意 number)
452
452
  - `../../tokens/uiSize/uiSize.spec.md` — `field-height-xs` 地板規則 + Icon 尺寸 Tier
453
453
  - `../../tokens/color/color.spec.md` — Primary token
454
454
  - CLAUDE.md「選擇 / 狀態視覺必須對齊既有 canonical」— Steps 不用 `bg-neutral-selected` 的理由
@@ -459,6 +459,7 @@ Item-level **內容狀態色彩**(completed / current / upcoming / error indicat
459
459
 
460
460
  - **root `aria-label`**:consumer 透過 `<Steps aria-label="註冊流程進度">` 提供(透傳到 `<ol>`),命名此流程。對齊 Angular Material「stepper 必須有 label」。
461
461
  - **sr-only 狀態文字**:每個 step header 含 visually-hidden `<span>`「第 N 步,共 M 步,{已完成 / 進行中 / 錯誤 / 未開始}」——indicator 是 `aria-hidden` 純視覺,故 sr-only 是螢幕報讀器**唯一**狀態來源(對齊 Carbon `--assistive-text` 慣例)。
462
+ - **展開狀態 ARIA**(2026-07-05 D4 加):垂直模式 step 含 `<StepContent>` 時,clickable header(`role="button"`)輸出 `aria-expanded`(反映 content 展開狀態;`multiple` toggle 與 `follow-active` 皆同步),並在 content 實際渲染(展開)時以 `aria-controls` 指向 content 區(收合時 content 不在 DOM,不輸出避免 dangling reference)——對齊 WAI-ARIA disclosure pattern trigger 最低要求。chevron 維持 `aria-hidden` 純視覺。
462
463
 
463
464
  **Keyboard 行為**(Carbon 模型 — sequential Tab,非 tablist roving):
464
465
 
@@ -476,4 +477,5 @@ Item-level **內容狀態色彩**(completed / current / upcoming / error indicat
476
477
 
477
478
  - `horizontal-overflow.spec.md`
478
479
  - `item-anatomy.spec.md`
480
+ - `pagination.spec.md`
479
481
  - `progress-bar.spec.md`
@@ -436,7 +436,7 @@ const STEP_STATUS_TEXT: Record<StepContentState, string> = {
436
436
  upcoming: '未開始',
437
437
  }
438
438
 
439
- function StepItemHeader({ children, className, style }: { children: React.ReactNode; className?: string; style?: React.CSSProperties }) {
439
+ function StepItemHeader({ children, className, style, contentId }: { children: React.ReactNode; className?: string; style?: React.CSSProperties; contentId?: string }) {
440
440
  const item = useStepItemContext()
441
441
  const steps = useStepsContext()
442
442
  const index = React.useContext(StepIndexContext)
@@ -454,6 +454,12 @@ function StepItemHeader({ children, className, style }: { children: React.ReactN
454
454
  onClick={item.clickable ? item.activate : undefined}
455
455
  onKeyDown={item.clickable ? onKeyDown : undefined}
456
456
  aria-disabled={item.disabled || undefined}
457
+ // 2026-07-05 D4 修:step 有 content 的可點 header(role=button)必帶 aria-expanded —
458
+ // WAI-ARIA disclosure pattern trigger 最低要求(multiple 模式 toggle;follow-active 亦
459
+ // 同步反映展開狀態)。aria-controls 只在 content 實際 render(展開)時輸出,避免
460
+ // dangling id reference(content 收合時不在 DOM)。
461
+ aria-expanded={item.clickable && contentId ? item.expanded : undefined}
462
+ aria-controls={item.clickable && contentId && item.expanded ? contentId : undefined}
457
463
  className={cn(
458
464
  // leading-compact:scanning-family header 行高 = 1.3(item-anatomy.spec.md:776 掃描模式 label 行高)。
459
465
  // 設在 header 而非 li 根 → prefix h-[1lh] + 水平 connector h-[1lh] + label(StepLabel 亦 leading-compact)
@@ -490,10 +496,12 @@ function VerticalLayout({
490
496
  const item = useStepItemContext()
491
497
  const showContent = !!content && item.expanded
492
498
  const indicatorBox = INDICATOR_BOX_WIDTH[steps.size]
499
+ // 2026-07-05 D4 修:content 區 id 供 header aria-controls 指向(disclosure 配線)
500
+ const contentId = React.useId()
493
501
 
494
502
  return (
495
503
  <>
496
- <StepItemHeader className="flex items-start gap-3">
504
+ <StepItemHeader className="flex items-start gap-3" contentId={content ? contentId : undefined}>
497
505
  {/* Row prefix slot — 消費 item-anatomy <ItemPrefix>(h-[1lh] 對齊 label 第一行 SSOT);
498
506
  width = INDICATOR_BOX_WIDTH 固定欄寬,base justify-center 讓 sm dot 在欄內置中 */}
499
507
  <ItemPrefix style={{ width: indicatorBox }}>
@@ -520,7 +528,7 @@ function VerticalLayout({
520
528
  </div>
521
529
  </StepItemHeader>
522
530
  {showContent && (
523
- <div className="flex items-start gap-3 mt-3">
531
+ <div id={contentId} className="flex items-start gap-3 mt-3">
524
532
  <div className="shrink-0" style={{ width: indicatorBox }} />
525
533
  <div className="flex-1 min-w-0">{content}</div>
526
534
  </div>
@@ -75,6 +75,7 @@ Track(pill 形,rounded-full)
75
75
  - Track 寬 = 2 × 高(pill 比例)
76
76
  - Thumb 直徑 = track 高度
77
77
  - ON 狀態 thumb 右滑 `translateX(trackHeight)`
78
+ - **Hover**(2026-07-06 補,「選中之上 hover 升階」家族):ON track `bg-primary → bg-primary-hover`(Checkbox checked hover 同款;Ant Switch checked hover = `colorPrimaryHover` 源碼實錘);OFF track `bg-border → bg-border-hover` 深一階(Checkbox 未選 hover 同慣例)
78
79
 
79
80
  ---
80
81
 
@@ -102,7 +103,7 @@ sm 和 md 視覺相同(純粹命名 mapping,讓消費者可直接傳同一
102
103
  | OFF | `bg-border`(neutral-5) | 白色 + 2px `border-border`(neutral-5,與 OFF track 同色) | 無 |
103
104
  | ON | `bg-primary` | 白色 + 2px primary border | primary check |
104
105
  | Disabled | 套 `opacity-disabled`(整體透明度降級) | 同 ON/OFF | 同 ON/OFF |
105
- | Readonly(standalone)| 視覺同一般態 | 但 `pointer-events-none` + `aria-readonly` | — |
106
+ | Readonly(standalone)| 視覺同一般態 | 但 `pointer-events-none` + click guard + `aria-readonly` | — |
106
107
  | Readonly(Field 內,2026-06-12 user 拍板)| 不渲染 toggle — 改渲染 `fieldWrapperStyles` readonly 灰框(= Input readonly 同源)+ ✓/—(display 同款值語言) | role="switch" + aria-checked + aria-readonly + 可 focus | — |
107
108
 
108
109
  ### Disabled 用 `opacity`
@@ -123,7 +124,7 @@ sm 和 md 視覺相同(純粹命名 mapping,讓消費者可直接傳同一
123
124
  | | Readonly(standalone) | Disabled |
124
125
  |---|---|---|
125
126
  | 視覺 | 正常顏色(可讀) | 降透明度(弱化) |
126
- | 互動 | 不可切換(pointer-events-none) | 不可切換(cursor-not-allowed) |
127
+ | 互動 | 不可切換(pointer-events-none + click guard) | 不可切換(cursor-not-allowed) |
127
128
  | aria | `aria-readonly` | `disabled` |
128
129
  | Tab 焦點 | 不在 tab order | 不在 tab order |
129
130
  | 用途 | settings list 鎖定呈現 | 外部條件造成不可操作 |
@@ -181,7 +182,7 @@ Switch 可透過 `label` / `description` props 內部直接渲染緊鄰文字:
181
182
 
182
183
  ## 邊界案例
183
184
 
184
- - **Readonly 的滑鼠 / 鍵盤**(standalone):`pointer-events-none` 使點擊無反應;`tabIndex=-1` 不入 tab order,故無鍵盤互動(視覺正常、僅鎖互動)。Field 內 readonly 灰框則 tabIndex=0 可聚焦(對齊 readonly input 可聚焦慣例)
185
+ - **Readonly 的滑鼠 / 鍵盤**(standalone):`pointer-events-none` 使直接點擊無反應;`<label htmlFor>` 的 synthetic click 非 pointer event,由 label + Root 兩道 onClick preventDefault guard 攔(2026-07-05 D4 補,點 label 文字不再 toggle);`tabIndex=-1` 不入 tab order,故無鍵盤互動(視覺正常、僅鎖互動)。Field 內 readonly 灰框則 tabIndex=0 可聚焦(對齊 readonly input 可聚焦慣例)
185
186
  - **Disabled 的鍵盤**:native `disabled`——不可聚焦、無鍵盤行為
186
187
  - **Label 過長**:自動換行(label 容器 `flex-1 min-w-0`),Switch 錨定第一行行高置中(`h-[1lh]`)且不被擠壓(`shrink-0`)
187
188
  - **無 loading state**:Switch 無 loading prop——async 進度不屬 Switch(見「何時不用」,用 Button loading)
@@ -60,6 +60,13 @@ const switchVariants = cva(
60
60
  // OFF → ON 背景色
61
61
  'data-[state=unchecked]:bg-border',
62
62
  'data-[state=checked]:bg-primary',
63
+ // 選中之上的 hover = 同色相升 hover 階(2026-07-06 user 掃出 Switch 是「藍色選中家族」
64
+ // 唯一漏網:Checkbox checked hover 早有同款、Ant Switch checked hover = colorPrimaryHover
65
+ // 源碼實錘;順修 meta states 宣稱 'hover' 但 code 原本零 hover 樣式的 claim-vs-code 落差)。
66
+ // unchecked hover:border(n-5)→ border-hover(n-6)深一階,對齊 Checkbox 未選 hover
67
+ // 加深一階慣例 + Ant unchecked hover(colorTextTertiary 加深)同方向。
68
+ 'data-[state=unchecked]:hover:bg-border-hover',
69
+ 'data-[state=checked]:hover:bg-primary-hover',
63
70
  ],
64
71
  {
65
72
  variants: {
@@ -73,16 +80,18 @@ const switchVariants = cva(
73
80
  }
74
81
  )
75
82
 
76
- const SPECS: Record<string, { thumb: number; check: number; checkStroke: number; translate: string }> = {
83
+ const SPECS: Record<string, { thumb: number; check: number; checkStroke: number }> = {
77
84
  // checkStroke:16px 以下 icon 視覺不夠顯眼 → 加粗 stroke 補償(跟 Checkbox 共用原則,
78
85
  // 見 checkbox.tsx 的 checkStrokeWidth 註解)。12px 用 3.5(render ≈ 1.75px 線寬,比 Lucide
79
86
  // 預設的 1px render 明顯更粗,視覺跟 16px 預設 stroke 的 1.33px 有足夠區別),16px 用 2.5
80
87
  // (render ≈ 1.67px,比預設 1.33px 稍粗讓 toggle check 夠顯眼)。跨 size 配對值由 checkbox.tsx 共用。
81
88
  // 2026-05-18 簡化 per user 視覺證 + Checkbox 同步(3.5 → 3 sm/md):effective render 差
82
89
  // 0.08px 視覺看不出,保留 compensation 主旨但不過度差異化。
83
- sm: { thumb: 20, check: 12, checkStroke: 3, translate: 'translateX(20px)' },
84
- md: { thumb: 20, check: 12, checkStroke: 3, translate: 'translateX(20px)' },
85
- lg: { thumb: 24, check: 16, checkStroke: 2.5, translate: 'translateX(24px)' },
90
+ // 2026-07-04 dead field translate:整檔零消費,實際位移 SSOT = Thumb className
91
+ // translate-x-5(20px)/ translate-x-6(24px)(見下方 Thumb),消除雙住所漂移風險。
92
+ sm: { thumb: 20, check: 12, checkStroke: 3 },
93
+ md: { thumb: 20, check: 12, checkStroke: 3 },
94
+ lg: { thumb: 24, check: 16, checkStroke: 2.5 },
86
95
  }
87
96
 
88
97
  export interface SwitchProps
@@ -174,23 +183,41 @@ const Switch = React.forwardRef<
174
183
  const generatedId = React.useId()
175
184
  const inputId = idProp ?? fieldCtx?.id ?? generatedId
176
185
 
186
+ // 2026-07-04 修:display / readonly-in-Field 分支的裸 span/div 原丟棄剩餘 props
187
+ // (id/data-*/aria-*)且不轉發 forwardRef ref → consumer 的 aria-label 等靜默失效。
188
+ // 抽出 Radix 專屬 non-DOM props,其餘 DOM props 於兩分支 spread 轉發(與 Checkbox/
189
+ // RadioGroup 同修);edit 主路徑不受影響(rootEl 仍 spread 完整 props)。
190
+ const {
191
+ checked: checkedProp,
192
+ defaultChecked: defaultCheckedProp,
193
+ onCheckedChange: _onCheckedChange,
194
+ required: _required,
195
+ name: _name,
196
+ value: _value,
197
+ asChild: _asChild,
198
+ children: _children,
199
+ ...restDomProps
200
+ } = props
201
+
177
202
  // ── mode='display'(下移至所有 hooks 之後,per #35 Rules of Hooks)──────────
178
203
  // 純展示模式:無互動 toggle、渲染 ✓ / —。與 Checkbox display 對齊(同 boolean primitive)。
179
204
  if (resolvedMode === 'display') {
180
- const isChecked = props.checked === true
205
+ const isChecked = checkedProp === true
181
206
  return isChecked
182
- ? <span className="text-foreground">✓</span>
183
- : <span className="text-fg-muted">—</span>
207
+ ? <span {...restDomProps} ref={ref as React.Ref<HTMLSpanElement>} className="text-foreground">✓</span>
208
+ : <span {...restDomProps} ref={ref as React.Ref<HTMLSpanElement>} className="text-fg-muted">—</span>
184
209
  }
185
210
 
186
211
  // ── mode='readonly' in Field(2026-06-12 user 拍板「灰框 + ✓/—」,與 Checkbox 同款)──
187
212
  // Field 內 readonly boolean = fieldWrapperStyles readonly 灰框(= Input readonly 同源)
188
213
  // + ✓/— 值語言;standalone readOnly(settings list)維持原樣鎖互動。詳 checkbox.tsx 同段註解。
189
214
  if (effectiveReadOnly && insideField) {
190
- const isChecked = (props.checked ?? props.defaultChecked) === true
215
+ const isChecked = (checkedProp ?? defaultCheckedProp) === true
191
216
  const boxSize = resolvedBoxSize
192
217
  return (
193
218
  <div
219
+ {...(restDomProps as React.HTMLAttributes<HTMLDivElement>)}
220
+ ref={ref as React.Ref<HTMLDivElement>}
194
221
  role="switch"
195
222
  aria-checked={isChecked}
196
223
  aria-readonly="true"
@@ -220,6 +247,17 @@ const Switch = React.forwardRef<
220
247
  tabIndex={effectiveReadOnly ? -1 : undefined}
221
248
  aria-describedby={fieldCtx?.descriptionId}
222
249
  {...props}
250
+ // 2026-07-05 D4 修:readOnly 鎖互動補 label 繞道 — <label htmlFor> 的 synthetic click
251
+ // 不是 pointer event,cva pointer-events-none 攔不到,點 label 文字仍 toggle。
252
+ // preventDefault 讓 Radix composeEventHandlers(checkForDefaultPrevented)跳過內部 toggle;
253
+ // 必列在 {...props} 之後才不被 consumer onClick 覆蓋(與 Checkbox/RadioGroupItem 同修)。
254
+ onClick={(event) => {
255
+ if (effectiveReadOnly) {
256
+ event.preventDefault()
257
+ return
258
+ }
259
+ props.onClick?.(event)
260
+ }}
223
261
  >
224
262
  <SwitchPrimitives.Thumb
225
263
  className={cn(
@@ -251,6 +289,11 @@ const Switch = React.forwardRef<
251
289
  return (
252
290
  <label
253
291
  htmlFor={inputId}
292
+ // 2026-07-05 D4 修:readOnly 時在 label 端取消 synthetic activation(第一道防線;
293
+ // Root onClick guard 為第二道 — 兩道對齊 finding suggestion 防 consumer 自寫 label 繞過)。
294
+ onClick={(event) => {
295
+ if (effectiveReadOnly) event.preventDefault()
296
+ }}
254
297
  className={cn(
255
298
  'inline-flex items-start gap-3 select-none',
256
299
  effectiveDisabled ? 'cursor-not-allowed' : readOnly ? 'cursor-default' : 'cursor-pointer'
@@ -288,6 +331,7 @@ const Switch = React.forwardRef<
288
331
  </span>
289
332
  )}
290
333
  </span>
334
+ {/* @row-slot-handcraft-allow: Field 控件的 1lh 首行對齊 wrapper(selection-control 家族 Checkbox/RadioGroup 同構),非 row item slot,不消費 MenuItem */}
291
335
  <span className="h-[1lh] flex items-center shrink-0">
292
336
  {rootEl}
293
337
  </span>
@@ -310,9 +354,14 @@ export const switchMeta = {
310
354
  md: { fieldHeight: 32, iconSize: 12, typography: 'body' },
311
355
  lg: { fieldHeight: 36, iconSize: 16, typography: 'body-lg' },
312
356
  },
313
- states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
357
+ // 2026-07-04 stale meta:移除 'active' — switchVariants / Thumb 無 active-state 樣式
358
+ // (cva 只有 unchecked/checked/disabled/readonly/focus-visible;對照 checkboxMeta 正確不含 active)
359
+ // 'checked' = ON 持續態(bg-primary;2026-07-07 meta 詞彙統一補列,toggle 家族用 'checked')
360
+ states: ['default', 'hover', 'checked', 'focus-visible', 'disabled'],
314
361
  tokens: {
315
- bg: ['bg-primary'],
362
+ // 2026-07-04 補齊漏列:bg-border = OFF track(cva data-[state=unchecked]:bg-border)、
363
+ // bg-on-emphasis = Thumb 白圓(Thumb className)— 兩者 code 實際消費,meta 原漏列
364
+ bg: ['bg-border', 'bg-on-emphasis', 'bg-primary'],
316
365
  fg: ['text-fg-disabled', 'text-fg-secondary', 'text-foreground', 'text-primary'],
317
366
  ring: ['ring-ring'],
318
367
  },
@@ -36,7 +36,7 @@ export const Overview: Story = {
36
36
  </TabsContent>
37
37
  </Tabs>
38
38
  </div>
39
- <p className="text-footnote text-fg-muted mt-3">Selected underline 用 ::after 絕對定位在 bottom: -1px(2px primary-hover),蓋住 TabsList 的 1px gray border(單一視覺線條)</p>
39
+ <p className="text-footnote text-fg-muted mt-3">Selected underline 用 ::after 絕對定位在 bottom: -1px(2px primary),蓋住 TabsList 的 1px gray border(單一視覺線條)</p>
40
40
  </div>
41
41
 
42
42
  <div>
@@ -66,9 +66,10 @@ export const Overview: Story = {
66
66
  <tbody>
67
67
  {[
68
68
  ['Tabs', '', '', ''],
69
+ [' defaultValue / value', 'string', '—', '當前 tab 值(受控 / 非受控)'],
70
+ ['TabsList', '', '', ''],
69
71
  [' size', "'sm' | 'md' | 'lg'", "'sm'", 'tab 高度 tier(對齊 --tab-height-*);size 選擇指引詳「設計原則 → 尺寸選擇」'],
70
72
  [' overflow', "'none' | 'scroll' | 'menu'", "'none'", '水平溢出處理(捲動 / 下拉選單)'],
71
- [' defaultValue / value', 'string', '—', '當前 tab 值(受控 / 非受控)'],
72
73
  ['TabsTrigger', '', '', ''],
73
74
  [' value', 'string', '必填', '唯一識別碼,對應 TabsContent value'],
74
75
  [' startIcon', 'LucideIcon', '—', '前綴 icon(描述 tab 類型)'],
@@ -227,7 +228,7 @@ export const ColorMatrix: Story = {
227
228
  <H3>TabsTrigger 四態色彩</H3>
228
229
  <Desc>
229
230
  Tabs 未選狀態用 fg-secondary(不搶視覺),hover 時文字色轉 foreground(text-foreground,
230
- neutral 最深)。Selected 用 foreground + bottom 2px primary-hover 下線
231
+ neutral 最深)。Selected 用 foreground + bottom 2px primary 下線
231
232
  ——下線是「當前位置」的明確指示器,不靠底色區分。
232
233
  </Desc>
233
234
  <div className="overflow-x-auto mb-4">
@@ -306,7 +307,7 @@ export const StateBehavior: Story = {
306
307
  <div className="flex flex-col gap-10">
307
308
  <div>
308
309
  <H3>互動狀態對照</H3>
309
- <Desc>「總覽」為 selected(當前 value,有 primary-hover 底部下線);「成員」「通知」為 unselected(移 hover 上去文字色轉 foreground);「設定」為 disabled(停用)。</Desc>
310
+ <Desc>「總覽」為 selected(當前 value,有 primary 底部下線);「成員」「通知」為 unselected(移 hover 上去文字色轉 foreground);「設定」為 disabled(停用)。</Desc>
310
311
  <div className="border border-border rounded-lg p-4">
311
312
  <Tabs defaultValue="overview">
312
313
  <TabsList>
@@ -318,7 +319,7 @@ export const StateBehavior: Story = {
318
319
  </Tabs>
319
320
  </div>
320
321
  <p className="text-footnote text-fg-muted mt-3">
321
- Selected underline:`::after bottom: -1px height: 2px bg: primary-hover`——蓋住 TabsList 的 1px gray border
322
+ Selected underline:`::after bottom: -1px height: 2px bg: primary`——蓋住 TabsList 的 1px gray border
322
323
  </p>
323
324
  </div>
324
325
  </div>
@@ -395,7 +396,7 @@ export const SpacingTokens: Story = {
395
396
 
396
397
  <div>
397
398
  <H3>Trigger 無水平 padding</H3>
398
- <Desc>Trigger **不加任何水平 padding**——寬度 = 內容寬度(hug content)。selected underline(`::after left-0 right-0`)因此剛好 fit label;若加橫向 padding,underline 會多出鬆散空白。trigger 之間的分隔靠 TabsList 的 `gap-[--layout-space-loose]`,不是 padding。</Desc>
399
+ <Desc>Trigger **不加任何水平 padding**——寬度 = 內容寬度(hug content)。selected underline(`::after left-0 right-0`)因此剛好 fit label;若加橫向 padding,underline 會多出鬆散空白。trigger 之間的分隔靠 TabsList 的 `gap-[var(--layout-space-loose)]`,不是 padding。</Desc>
399
400
  </div>
400
401
  </div>
401
402
  ),
@@ -115,10 +115,11 @@ export const UsageGuidance: Story = {
115
115
  <Label>✅ Tabs:電商後台,每個 view 是獨立子頁(各自有 filters / table / actions)</Label>
116
116
  <Tabs defaultValue="orders">
117
117
  <TabsList>
118
+ {/* 2026-07-04:同組 startIcon 全有或全無(spec 禁止事項)— 原 顧客/設定 混用 icon 已移除 */}
118
119
  <TabsTrigger value="orders">訂單</TabsTrigger>
119
- <TabsTrigger value="customers" startIcon={Users}>顧客</TabsTrigger>
120
+ <TabsTrigger value="customers">顧客</TabsTrigger>
120
121
  <TabsTrigger value="products">產品</TabsTrigger>
121
- <TabsTrigger value="settings" startIcon={Settings}>設定</TabsTrigger>
122
+ <TabsTrigger value="settings">設定</TabsTrigger>
122
123
  </TabsList>
123
124
  <TabsContent value="orders" className="text-body text-fg-muted">(訂單頁的 toolbar、filters、table…)</TabsContent>
124
125
  </Tabs>
@@ -199,8 +200,9 @@ export const SizeSelection: Story = {
199
200
  >
200
201
  <Tabs defaultValue="a">
201
202
  <TabsList size="md">
203
+ {/* 2026-07-04:同組 icon 全有或全無(spec 禁止事項)— 原 成員 單獨帶 icon 已移除 */}
202
204
  <TabsTrigger value="a">總覽</TabsTrigger>
203
- <TabsTrigger value="b" startIcon={Users}>成員</TabsTrigger>
205
+ <TabsTrigger value="b">成員</TabsTrigger>
204
206
  <TabsTrigger value="c" badge={<Badge count={3} />}>通知</TabsTrigger>
205
207
  </TabsList>
206
208
  </Tabs>