@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
@@ -22,7 +22,8 @@ import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio'
22
22
  * Consumer 傳 `ratio={n/m}` 數字計算(如 16/9 = 1.7777)。
23
23
  *
24
24
  * ── 常見消費者 ──
25
- * Coachmark media / Carousel item image / Card thumbnail(未來)/ Chart preview
25
+ * Coachmark media(coachmark.tsx)/ FileViewer 圖片預覽(file-viewer.tsx)/ Carousel item imageCard thumbnail(未來)
26
+ *(Chart 現用 raw aspect-video 非消費者 — 2026-07-04 grep 驗證)
26
27
  */
27
28
 
28
29
  export type AspectRatioProps = React.ComponentPropsWithoutRef<typeof AspectRatioPrimitive.Root>
@@ -47,7 +48,7 @@ export const aspectRatioMeta = {
47
48
  sizes: {
48
49
 
49
50
  },
50
- // 2026-06-05 fix(deep-audit):純 structural container,無互動狀態(spec.md L101「無 hover/focus/active/disabled」)。
51
+ // 2026-06-05 fix(deep-audit):純 structural container,無互動狀態(spec「為何無 ColorMatrix / SizeMatrix / StateBehavior」段;段名指法免行號漂移)。
51
52
  // 原 ['default','hover','active','focus-visible','disabled'] 是 Phase 1 mechanical placeholder,與 spec 矛盾。
52
53
  states: ['default'],
53
54
  tokens: {
@@ -32,7 +32,7 @@ const Label = ({ children, warn }: { children: React.ReactNode; warn?: boolean }
32
32
 
33
33
  const Section = ({ title, children }: { title: string; children: React.ReactNode }) => (
34
34
  <section className="mb-12">
35
- <h2 className="text-heading-3 font-bold text-foreground mb-4 pb-2 border-b border-border">{title}</h2>
35
+ <h2 className="text-h3 font-bold text-foreground mb-4 pb-2 border-b border-border">{title}</h2>
36
36
  {children}
37
37
  </section>
38
38
  )
@@ -71,51 +71,31 @@ Avatar 是視覺身份標識——代表一個人、一個實體(專案、組
71
71
  hover ProfileCard 是 preview,所有 preview 必提供「看完整資料」的路徑。
72
72
  缺 `onViewMore` 使用者點不到完整 profile → hover 成單向死路。對齊 Slack / Linear / Notion / Figma / Gmail hover-profile popover 必有 "View profile" link 的世界級 pattern。詳 `../ProfileCard/profile-card.spec.md`「View more」canonical。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
73
73
 
74
- **適用範圍(ALL person avatars,無例外)**:
75
- - PeoplePicker tag / table cell 頭像 / sidebar user / comment author / avatar 堆疊
76
- - Dialog / Sheet body member list
77
- - FileItem rich 的 author avatar
78
- - Menu / Dropdown 的 assignee / owner
79
- - Chat / thread / notification 頭像
80
-
81
- **世界級對照**(hover → profile 是 default,不是 opt-in):
82
- - Slack:hover user avatar → profile popover(default)
83
- - Figma:hover member avatar → profile card(default)
84
- - Linear:hover assignee avatar → user card(default)
85
- - Notion:hover person tag → user card(default)
86
- - GitHub:hover username + avatar → user hover card(default)
74
+ **適用範圍(ALL person avatars,無例外)**:PeoplePicker tag / table cell 頭像 / sidebar user / comment author / avatar 堆疊 / Dialog·Sheet member list / FileItem rich author / Menu·Dropdown assignee·owner / chat·thread·notification 頭像。
75
+
76
+ **世界級對照**(hover → profile 是 default,不是 opt-in):Slack / Figma / Linear / Notion / GitHub —— hover person avatar(或 username)→ profile popover / user card,全部 default 行為。
87
77
 
88
78
  **例外(ProfileCard 不適用,但 hoverCard 通用行為仍可用)**:
89
79
  - Entity avatar(專案 / 組織 logo / app icon)— 不代表人員,**不自動套 ProfileCard**(ProfileCard 是人專用內容模板:64px 圓形 + presence 狀態 + Chat/通話 動作,實體沒有)。但 `hoverCard` prop 本身是**通用行為**(型別 `React.ReactNode`,avatar.tsx `AvatarProps.hoverCard`),entity / square avatar **仍可配 hoverCard 顯示其他內容**(如實體資訊卡);此時 focus ring 自動跟隨 square 形狀(rounded-md)。未來若需實體資訊卡,做成**平行的內容元件**(對齊 Atlassian `ProfileCard` / `TeamProfileCard` 分離 + GitHub `data-hovercard-type` 各 type 獨立內容),**不泛化 ProfileCard**。
90
80
  - Bot / automated account avatar(Atlas / Octocat 等)— 本 DS 後續視需求決
91
81
 
92
- **為什麼必 default**:
93
- - Person avatar 單獨無 context(只有照片 + 首字),user 不知是誰(世界級對照詳上方清單)
94
- - Opt-in 模式會讓一半的 consumer 忘記加,DS 品質不可控
95
-
96
- **違規檢測(M10 proactive scan)**:
97
- `grep '<Avatar.*alt=\{'` 全 src,檢查有無 person name(非 entity)alt 且沒 `hoverCard` prop。
98
- 未來會加 hook `check_avatar_hovercard.sh` 自動攔截。
82
+ **為什麼必 default**:Person avatar 單獨無 context(只有照片 + 首字),user 不知是誰;opt-in 模式會讓一半 consumer 忘記加,DS 品質不可控。**違規檢測(M10 proactive scan)**:`grep '<Avatar.*alt=\{'` 全 src,檢查 person name(非 entity)alt 且沒 `hoverCard` prop 者;目前無專用 hook,靠 grep 掃描。
99
83
 
100
84
  **Keyboard 可達 canonical(2026-04-22 D4 UX audit 補齊)**:
101
85
  當 Avatar 有 `hoverCard` prop,Avatar wrapper **必 keyboard focusable**,讓 keyboard-only user 也能 reach ProfileCard popover。Avatar 元件內部自動套:
102
- - `tabIndex=0`(可 Tab )
103
- - `role="button"` + `aria-haspopup="dialog"`(告訴 AT 這是可觸發 popover)
86
+ - `tabIndex=0`(可 Tab 到;focus 時 Radix hover-card 自動開卡)
87
+ - `role="img"` + `aria-label`(取 `alt`)——2026-07-07 CI axe 補修:aria-label 掛在無 role 的 generic `<div>` 是 ARIA 禁用組合(axe `aria-prohibited-attr`,serious);Avatar 語義本質 = 身份圖像,`role="img"` 讓命名合法、子孫自動 presentational、不對 AT 承諾任何互動行為
104
88
  - `focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1`(keyboard focus 視覺 indicator)
89
+ - **不掛 `role="button"` / `aria-haspopup`**(2026-07-06 user 拍板拆除):原本宣告了卻無 Enter/Space 行為、無 `aria-expanded` = 對 AT 的空承諾(D4 finding);對齊 Radix hover-card 官方(刻意零 popup ARIA,定位 sighted-user 輔助)+ GitHub hovercard(trigger 無 popup ARIA)
105
90
  - 若無 `hoverCard` → 純展示 `<div>` 不 focusable(避免 tabTrap 噪音)
106
91
 
107
- Rationale:Radix `HoverCardTrigger asChild` 不自動加 tabIndex 給 non-focusable child。若 Avatar wrapper 是 `<div>` 沒 tabIndex,keyboard user 無法 reach ProfileCard content,違反 WCAG 2.1.1(Keyboard)+ 4.1.2(Name, Role, Value)。此 canonical 確保 DS-wide 「person avatar → ProfileCard」同時對 keyboard / screen reader 可達。
92
+ Rationale:Radix `HoverCardTrigger asChild` 不自動加 tabIndex 給 non-focusable child。若 Avatar wrapper 是 `<div>` 沒 tabIndex,keyboard user 無法 reach ProfileCard content,違反 WCAG 2.1.1(Keyboard)。此 canonical 確保 DS-wide「person avatar → ProfileCardkeyboard 可達,且 ARIA 只宣告真的有的行為。
108
93
 
109
94
  **Status dot a11y(2026-04-22 D4 UX audit 調整)**:Avatar status dot 內部 span 從原 `role="status"` + `aria-label="presence: online"` 改為 `aria-hidden`。Rationale:`role="status"` 是 live region,多 Avatars(member list / mention picker)會造成 SR 洪水噪音;presence 資訊應整合進 parent avatar 的 `aria-label`(world-class Slack / Teams / Discord 共通)— consumer 傳 `alt` 時自帶語意(e.g. `alt="Alan Chen (online)"`)。
110
95
 
111
96
  ### 溢出人員列表(+N)
112
97
 
113
- Avatar 堆疊的「+N」hover 也出 **HoverCard**(不是 Tooltip),因為:
114
- - 列表內的 person tag 需要 dismiss 功能(互動)
115
- - 每個 person tag hover 需要再出 name card(嵌套 HoverCard)
116
- - Tooltip 是非互動的,不支援上述需求
117
-
118
- 架構:`+N → HoverCard(人員列表)→ 每個人名 → HoverCard(name card)`
98
+ Avatar 堆疊的「+N」hover 也出 **HoverCard**(不是 Tooltip)——列表內 person tag 需要 dismiss 互動、每個 person tag hover 需再出 name card(嵌套 HoverCard),Tooltip 非互動不支援。架構:`+N → HoverCard(人員列表)→ 每個人名 → HoverCard(name card)`
119
99
 
120
100
  ### 純文字截斷 vs 人員頭像
121
101
 
@@ -264,7 +244,7 @@ Avatar `badgeCount` 內部消費 DS `<Badge variant="critical" max={99}>`,加上
264
244
  - **無 `alt` 時 fallback**:image 模式自動降級 initials / icon;一律不靜默渲染無 SR 標的元素。
265
245
  - **Status dot SR 處理**:status dot 內部 span `aria-hidden`(presence 訊號整合進 parent `alt`),避免 `role="status"` live region 在 member list 造成 SR 洪水(詳「Status dot a11y」段)。
266
246
  - **`badgeCount` 語意**:內部消費 `<Badge variant="critical">`,由 Avatar 傳入 `aria-label="N unread"` 提供計數語義(Badge 本身只有 `role="status"`,不自產 aria-label);`badgeCount <= 0` 不渲染避免空 announce。
267
- - **HoverCard 整合**:Avatar 帶 `hoverCard` prop 時自動 `tabIndex=0` + `role="button"` + `aria-haspopup="dialog"` + `focus-visible:ring-2`,確保 keyboard user 能 Tab 進入觸發 ProfileCard popover(詳「Keyboard 可達 canonical」段)。
247
+ - **HoverCard 整合**:Avatar 帶 `hoverCard` prop 時自動 `tabIndex=0` + `role="img"` + `aria-label`(取 `alt`)+ `focus-visible:ring-2`,確保 keyboard user 能 Tab 進入觸發 ProfileCard popover;**不掛 `role="button"` / `aria-haspopup`**(2026-07-06 拆除;role="img" 為 2026-07-07 axe 補修,詳「Keyboard 可達 canonical」段)。
268
248
  - **Image alt 語意**:meaningful image(person photo / brand logo)用實質 `alt`;decorative-only(極少)走 `alt=""`,但 Avatar 本質是身份識別,decorative 用法應改用 Icon 元件。
269
249
 
270
250
  ---
@@ -41,7 +41,7 @@ export const Modes = {
41
41
  }
42
42
 
43
43
  /* @story-trait-rationale: Shapes / Colors / AllSizes retired 2026-05-17 per audit Dim 24 —
44
- * anatomy.stories.tsx SizeMatrix(含 circle/square shape axis)+ ColorMatrix(9 hue × subtle/solid)
44
+ * anatomy.stories.tsx SizeMatrix(含 circle/square shape axis)+ ColorMatrix(12 hue + neutral × subtle/solid)
45
45
  * 已 cover trait grids。展示層保留 typical real-product 情境(Modes / Fallback / Status / Count badge 等),
46
46
  * 避免跟 anatomy trait grid 重複。 */
47
47
 
@@ -145,6 +145,16 @@ export interface AvatarProps extends React.HTMLAttributes<HTMLDivElement> {
145
145
  const AvatarInner = React.forwardRef<HTMLDivElement, AvatarProps>(
146
146
  ({ size = 32, shape = 'circle', src, alt, icon: Icon, color = 'neutral', solid = false, status, badgeCount, badgeAriaLabel, hoverCard, className, style, ...props }, ref) => {
147
147
  const [imgError, setImgError] = React.useState(false)
148
+ // 2026-07-05 D4:imgError 隨 src 變更 reset — 原 state 首次 onError 後永久 true,consumer 之後
149
+ // 換有效新 src(重傳頭像 / signed URL 刷新)時 showImage 永遠 false,<img> 不再掛載 → 新圖
150
+ // 永不嘗試載入、終身卡 initials fallback。React 官方 adjust-state-on-prop-change 寫法
151
+ //(render-time derived reset,較 useEffect 少一輪 render);對齊 Radix useImageLoadingStatus /
152
+ // MUI useLoaded 皆隨 src reset 的 canonical。
153
+ const [prevSrc, setPrevSrc] = React.useState(src)
154
+ if (prevSrc !== src) {
155
+ setPrevSrc(src)
156
+ setImgError(false)
157
+ }
148
158
  const documentTheme = useDocumentTheme()
149
159
  const isTableScrolling = useTableIsScrolling()
150
160
  // 2026-05-13 R3.5(per codex Q3 verdict + user 拍「想盡辦法 auto-handle prereq」):
@@ -227,16 +237,22 @@ const AvatarInner = React.forwardRef<HTMLDivElement, AvatarProps>(
227
237
  )
228
238
 
229
239
  const hasOverlay = status || typeof badgeCount === 'number'
230
- // Keyboard access canonical(D4 UX audit 2026-04-22 finding):Avatar with `hoverCard`
231
- // 需 keyboard 可達 — Radix `HoverCardTrigger asChild` 不自動加 tabIndex,non-focusable
232
- // `<div>` 會讓 keyboard-only user 無法 reach ProfileCard popover(WCAG 2.1.1 / 4.1.2 違反)。
233
- // 解:當 `hoverCard` 存在時,wrapper `<div>` 變 focusable(`tabIndex=0` + `role="button"` +
234
- // `aria-haspopup="dialog"` + focus-visible ring)。若無 hoverCard 則維持純展示 `<div>`。
240
+ // Keyboard access canonical(D4 2026-04-22 finding;2026-07-06 user 拍板修正 ARIA 空承諾):
241
+ // Avatar with `hoverCard` 需 keyboard 可達 — Radix `HoverCardTrigger asChild` 不自動加
242
+ // tabIndex,non-focusable `<div>` 會讓 keyboard-only user 無法 reach popover(WCAG 2.1.1)。
243
+ // 解:`tabIndex=0` + `role="img"` + `aria-label` + focus ring(focus 時 Radix 自動開卡)。
244
+ // **不掛 `role="button"` / `aria-haspopup`**(2026-07-06 拆除):原本宣告了卻沒有
245
+ // Enter/Space 行為、沒有 aria-expanded = 對 AT 的空承諾;對齊 Radix hover-card 官方
246
+ // (刻意零 popup ARIA,hover card 定位 sighted-user 輔助)+ GitHub hovercard(trigger
247
+ // 純連結無 popup ARIA)。若無 hoverCard 則維持純展示 `<div>`。
248
+ // **`role="img"`(2026-07-07 CI axe 補修)**:aria-label 在無 role 的 generic `<div>` 上
249
+ // 是 ARIA 禁用組合(axe aria-prohibited-attr,serious;2026-07-06 拆 role="button" 後裸露)。
250
+ // Avatar 語義本質 = 身份圖像 → `role="img"` 讓 aria-label 合法、子孫自動 presentational、
251
+ // 且不對 AT 承諾任何互動行為 — 與「拆假承諾」拍板意圖一致(emoji span / MUI Avatar img alt 同款語義)。
235
252
  const focusableProps = hoverCard
236
253
  ? {
237
254
  tabIndex: 0,
238
- role: 'button' as const,
239
- 'aria-haspopup': 'dialog' as const,
255
+ role: 'img',
240
256
  'aria-label': alt ?? 'View profile',
241
257
  }
242
258
  : {}
@@ -320,7 +336,6 @@ const AvatarInner = React.forwardRef<HTMLDivElement, AvatarProps>(
320
336
  )
321
337
  }
322
338
  )
323
- AvatarInner.displayName = 'AvatarInner'
324
339
 
325
340
  // ── AvatarData ─────────────────────────────────────────────────────────────
326
341
  // 資料型別,讓 consumer 傳資料而非 ReactNode。
@@ -336,8 +351,9 @@ export interface AvatarData {
336
351
  /**
337
352
  * Person avatar hover ProfileCard(DS-wide canonical,person avatar 預設必有,見 avatar.spec.md)。
338
353
  * Entity avatar(專案 / 組織 logo)不帶 → consumer 不傳 hoverCard 即豁免。
339
- * 所有消費 AvatarData 的 primitive(MenuItem / DropdownMenu / SelectMenu / SelectionItem / ProfileCard)
340
- * 需 forward 此 prop 到內部 <Avatar hoverCard={avatar.hoverCard} />。
354
+ * 消費 AvatarData 的 row primitive(MenuItem menu-item.tsx / SelectionItem selection-item.tsx,
355
+ * DropdownMenu / SelectMenu 經 MenuItem)需 forward 此 prop 到內部 <Avatar hoverCard={...} />。
356
+ * 例外:ProfileCard 內部 Avatar 不 forward(卡片本身就是 hover 目的地,nested hover-in-hover 不合理)。
341
357
  */
342
358
  hoverCard?: React.ReactNode
343
359
  }
@@ -347,12 +363,10 @@ export interface AvatarData {
347
363
  export const avatarMeta = {
348
364
  component: 'Avatar',
349
365
  family: null, // non-family composite / overlay / layout
350
- variants: {
351
-
352
- },
353
- sizes: {
354
-
355
- },
366
+ // 2026-07-04 audit 標記:Avatar 無 variant 軸、size 為任意 number(px,由 consumer context 決定
367
+ // — item-anatomy AVATAR_SIZE 查表 / ProfileCard 40 等),無 discrete tier → 兩欄留空為 intentional 非漏填
368
+ variants: {},
369
+ sizes: {},
356
370
  states: ['default', 'focus-visible', 'disabled'], // 2026-06-11 R2:本身無自有 hover/active(spec L279;hoverCard 互動屬 HoverCard),
357
371
  tokens: {
358
372
  bg: ['bg-surface-raised'],
@@ -25,8 +25,8 @@ const VARIANTS: VariantKey[] = ['critical', 'high', 'medium', 'low']
25
25
  type ColorSpec = { bg: string; text: string }
26
26
 
27
27
  const TOKEN_MAP: Record<VariantKey, ColorSpec> = {
28
- critical: { bg: '--notification', text: 'white' },
29
- high: { bg: '--info', text: 'white' },
28
+ critical: { bg: '--notification', text: '--on-emphasis' },
29
+ high: { bg: '--info', text: '--on-emphasis' },
30
30
  medium: { bg: '--info-subtle', text: '--info-text' },
31
31
  low: { bg: '--secondary', text: '--fg-muted' },
32
32
  }
@@ -167,13 +167,13 @@ Contrast floor **是下限不是上限**——不限制 semantic 繼續往上。
167
167
 
168
168
  ---
169
169
 
170
- ## 字體例外:`text-[10px]`(documented sub-footnote)
170
+ ## 字體例外:`text-[10px]`(documented micro-indicator 例外)
171
171
 
172
- Badge count 模式的數字使用 `text-[10px]`,低於 typography 系統最小的 `--font-footnote`(12px)。這是 **Badge 獨有的合法例外**:
172
+ Badge count 模式的數字使用 `text-[10px]`(10px,與 typography 系統最小的 `--font-footnote` 同值;12px 是 `--font-caption`)。不消費 `text-footnote` utility——該 utility 綁 line-height 1.3 且語意為法律文字/來源標注,Badge count 需要 `leading-none`(circle 內數字置中 canonical)。這是 **Badge 獨有的合法例外**:
173
173
 
174
174
  - **為什麼不加新 typography token**: 10px 是「count indicator 專用尺寸」,全系統**只有 Badge / OverflowIndicator 兩處**使用;創一個 `--font-micro` token 只為這兩個消費者會過度 generalization
175
- - **為什麼合法**: Badge count 是輔助 indicator(次要訊號),12px 字寬在 16×16 的圓形 Badge 內缺乏 padding 呼吸(字徑/圓徑比例過密)。對照世界級:Material Badge 10sp、Polaris Badge 字級小於 body、GitHub Counter 10-11px 都小於 body footnote <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
176
- - **其他元件不得比照**: 若新元件覺得需要 `text-[10px]`,先問「真的不是 footnote 12px 可以解決嗎」,99% 情境 footnote 夠用;真有需求跟 Badge / OverflowIndicator 歸同一 micro-indicator family 討論
175
+ - **為什麼合法**: Badge count 是輔助 indicator(次要訊號),caption(12px)字寬在 16×16 的圓形 Badge 內缺乏 padding 呼吸(字徑/圓徑比例過密)。對照世界級:Material Badge 10sp、Polaris Badge 字級小於 body、GitHub Counter 10-11px 都小於 body <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
176
+ - **其他元件不得比照**: 若新元件覺得需要 `text-[10px]`,先問「真的不是 caption 12px 可以解決嗎」,99% 情境 caption 夠用;真有需求跟 Badge / OverflowIndicator 歸同一 micro-indicator family 討論
177
177
 
178
178
  ---
179
179
 
@@ -395,4 +395,5 @@ Badge 是**計數 / 狀態視覺指示器**,本身**非互動元件**:
395
395
 
396
396
  - `avatar.spec.md`
397
397
  - `button.spec.md`
398
+ - `overflow-indicator.spec.md`
398
399
  - `tag.spec.md`
@@ -77,8 +77,11 @@ const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
77
77
  ({ variant, dot = false, count, max, className, role, ...props }, ref) => {
78
78
  // dot 預設 attention 色 = critical(非 cva 的 count default 'low');count 沿用 cva default 'low'。
79
79
  const effectiveVariant = variant ?? (dot ? 'critical' : 'low')
80
+ // 2026-07-05 D4 修(續 07-04 guard):count 模式 count == null → 整顆 return null 不渲染 —
81
+ // 原 guard 只擋文字,仍渲染 16px 空 pill + 空 role=status live region(Ant/Material 皆不渲染)。
82
+ if (!dot && count == null) return null
80
83
  const display = dot ? null : (
81
- max != null && count != null && count > max ? `${max}+` : `${count}`
84
+ max != null && count! > max ? `${max}+` : `${count}`
82
85
  )
83
86
 
84
87
  // a11y(2026-04-25 axe aria-prohibited-attr fix):
@@ -36,7 +36,7 @@ export const Overview: Story = {
36
36
  <div className="flex flex-col gap-10">
37
37
  <div>
38
38
  <H3>Anatomy</H3>
39
- <Desc>Breadcrumb 是純 HTML + Tailwind 元件( Radix primitive),基於 shadcn/ui Breadcrumb 結構橋接 DS token。使用 `&lt;nav aria-label="Breadcrumb"&gt;` 確保正確 a11y。</Desc>
39
+ <Desc>Breadcrumb 基於 shadcn/ui Breadcrumb 結構橋接 DS token,基礎結構為原生 HTML + Tailwind(asChild Radix Slot;truncate Tooltip 與收合 DropdownMenu 消費 DS 內部元件)。使用 `&lt;nav aria-label="Breadcrumb"&gt;` 確保正確 a11y。</Desc>
40
40
  <Breadcrumb>
41
41
  <BreadcrumbList>
42
42
  <BreadcrumbItem>
@@ -439,7 +439,7 @@ export const CollapseMatrix: Story = {
439
439
  </BreadcrumbItem>
440
440
  </BreadcrumbList>
441
441
  </Breadcrumb>
442
- <p className="text-footnote text-fg-muted mt-3">點 Ellipsis 按鈕(包在 DropdownMenuTrigger 內)以 DropdownMenu 展開中間層;BreadcrumbEllipsis 渲染的是完整可點擊按鈕(消費 ItemInlineActionButton,帶 aria-label / focus / hover bg)declarative `items` 與 compositional 兩種用法都會自動接上 DropdownMenu 收合。</p>
442
+ <p className="text-footnote text-fg-muted mt-3">本 story 為靜態結構展示:手動 `&lt;BreadcrumbItem&gt;&lt;BreadcrumbEllipsis /&gt;` 只渲染 按鈕本身(消費 ItemInlineActionButton,帶 aria-label / focus / hover bg),未接 DropdownMenu — 此處點擊無展開行為。完整互動接線(Ellipsis 包在 DropdownMenuTrigger 內展開中間層)見展示層「可互動省略」story;declarative `items` 與 compositional 超過 `maxItems` 的自動收合路徑則由元件自動接上 DropdownMenu。</p>
443
443
  </div>
444
444
  </div>
445
445
  ),
@@ -244,16 +244,21 @@ export const LongPathRule: Story = {
244
244
  <BreadcrumbLink href="#">組織</BreadcrumbLink>
245
245
  </BreadcrumbItem>
246
246
  <BreadcrumbSeparator />
247
- <DropdownMenu>
248
- <DropdownMenuTrigger asChild>
249
- <BreadcrumbEllipsis />
250
- </DropdownMenuTrigger>
251
- <DropdownMenuContent>
252
- <DropdownMenuItem>部門</DropdownMenuItem>
253
- <DropdownMenuItem>設計團隊</DropdownMenuItem>
254
- <DropdownMenuItem>成員管理</DropdownMenuItem>
255
- </DropdownMenuContent>
256
- </DropdownMenu>
247
+ {/* 2026-07-04 修 broken demo:裸 DropdownMenu 會被 BreadcrumbList children filter 丟棄
248
+ (breadcrumb.tsx compositionalContent 只認 BreadcrumbItem)→ 包 BreadcrumbItem
249
+ (對齊 declarative 模式內部 ellipsis 作法) */}
250
+ <BreadcrumbItem>
251
+ <DropdownMenu>
252
+ <DropdownMenuTrigger asChild>
253
+ <BreadcrumbEllipsis />
254
+ </DropdownMenuTrigger>
255
+ <DropdownMenuContent>
256
+ <DropdownMenuItem>部門</DropdownMenuItem>
257
+ <DropdownMenuItem>設計團隊</DropdownMenuItem>
258
+ <DropdownMenuItem>成員管理</DropdownMenuItem>
259
+ </DropdownMenuContent>
260
+ </DropdownMenu>
261
+ </BreadcrumbItem>
257
262
  <BreadcrumbSeparator />
258
263
  <BreadcrumbItem>
259
264
  <BreadcrumbLink href="#">權限</BreadcrumbLink>
@@ -30,7 +30,7 @@ benchmark:
30
30
  ## 定位
31
31
 
32
32
  Breadcrumb 顯示「當前頁面在資訊階層中的位置」,同時提供快速回到上層的路徑導覽。
33
- 基於 shadcn/ui Breadcrumb 結構(純 HTML + Tailwind,無 Radix primitive),橋接設計系統 token。
33
+ 基於 shadcn/ui Breadcrumb 結構(原生 nav / ol / li 語意 + Tailwind;`asChild` 多型渲染用 Radix Slot,truncate Tooltip 與 collapse DropdownMenu 消費 DS 內部 Radix-based 元件),橋接設計系統 token。
34
34
 
35
35
  **Breadcrumb 是「你在哪裡」的指示器,不是「你可以去哪裡」的選單**——反映當前路徑,不是全部可能路徑。
36
36
 
@@ -113,7 +113,7 @@ Breadcrumb 顯示「當前頁面在資訊階層中的位置」,同時提供快
113
113
  ## 對齊既有設計語言
114
114
 
115
115
  - **色彩三階層**(fg-secondary → foreground / primary-hover → fg-muted)跟 MenuItem / Tabs trigger / SegmentedControl 的三階層結構一致:**次要 → 主要(或 hover 強調) → 弱化**
116
- - **`primary-hover` 在 Breadcrumb 的用法是「互動時文字變藍」**,跟 Tabs underline / Chip border / SegmentedControl border 用 `primary-hover` canonical 選中規則屬於同一個色彩語言(`primary-hover` = 互動高亮)
116
+ - **`primary-hover` 在 Breadcrumb 的用法是「互動時文字變藍」**——屬於全系統「瞬時互動高亮」色彩語言(連結 hover 換色 canonical 同族,`primary-hover` = 瞬時態專用階)。注意與「持續選中」分軌:選中規則(Tabs underline / Chip / SegmentedControl selected)自 2026-07-06 拍板起用 primary **base**(semantic.css「選中狀態」段 SSOT)——瞬時弱、持續強
117
117
  - **分隔 icon 用 `ChevronRight`**(不用 `/` 字元)對齊 Button `endIcon` 的「方向指示」icon 選擇
118
118
 
119
119
  ---
@@ -177,6 +177,8 @@ Breadcrumb 顯示「當前頁面在資訊階層中的位置」,同時提供快
177
177
  | `ellipsis`(BreadcrumbEllipsis 包裝)| **0** | 永遠完整顯示 ⋯ |
178
178
  | `BreadcrumbSeparator` | **0** | 永遠完整(否則 path 視覺斷裂)|
179
179
 
180
+ **Compositional 手動 ellipsis 注意**:compositional path 按位置自動分派 role(first=root / last=current / 其餘=middle,`breadcrumb.tsx` `cloneWithRole` 尊重顯式 role),手動 `<BreadcrumbItem><BreadcrumbEllipsis/></BreadcrumbItem>` 需顯式傳 `role="ellipsis"` 才拿到 shrink:0(否則按位置拿 middle / shrink:2);declarative `items` 與自動收合路徑由元件自動標 `role="ellipsis"`。
181
+
180
182
  **為何 flex-shrink hierarchy 不用 fixed max-width**:
181
183
  - 容器寬 / items 少 → 各 item 自然寬度,**不浪費剩餘空間**
182
184
  - 容器窄 / items 多 → 按 priority 縮(root 先 → middle → current 最後)
@@ -247,7 +249,7 @@ ColorMatrix 已建:展示 BreadcrumbLink / Page / Separator / Ellipsis 四種節
247
249
 
248
250
  ## A11y 預設
249
251
 
250
- **ARIA / Pattern**:純 HTML 結構元件(nav + ol + li + a/span),無第三方 primitive。a11y 來自原生 HTML 語意 — 外層 `<nav aria-label="Breadcrumb">`、當前頁 `<span role="link" aria-disabled="true" aria-current="page">`、分隔符 `aria-hidden`(不進無障礙樹)。對齊 [WAI-ARIA Breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/)。
252
+ **ARIA / Pattern**:結構為原生 HTML(nav + ol + li + a/span),a11y 來自原生 HTML 語意(`asChild` 用 Radix Slot 只合併 prop、不管理焦點 / ARIA / 鍵盤;truncate Tooltip 與 collapse DropdownMenu 各自帶自身 a11y)— 外層 `<nav aria-label="Breadcrumb">`、當前頁 `<span role="link" aria-disabled="true" aria-current="page">`、分隔符 `aria-hidden`(不進無障礙樹)。對齊 [WAI-ARIA Breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/)。
251
253
 
252
254
  **為何當前頁加 `role="link" aria-disabled="true"`**:BreadcrumbPage 是「被 disable 的連結」— 把它標記成連結家族(`role="link"`)讓螢幕閱讀器把整條 breadcrumb 念成同一個連結序列,再以 `aria-disabled="true"` 告知不可操作(對齊 shadcn/ui Breadcrumb canonical `BreadcrumbPage`)。WAI-ARIA APG 允許 current 用 link + `aria-current`,shadcn 加 `aria-disabled` 是合理增強。
253
255
 
@@ -205,8 +205,10 @@ const BreadcrumbList = React.forwardRef<HTMLOListElement, BreadcrumbListProps>(
205
205
  // Declarative mode(items prop provided):自動 render + auto-collapse
206
206
  const declarativeContent = React.useMemo(() => {
207
207
  if (!items) return null
208
- const renderItem = (spec: BreadcrumbItemSpec, role: 'root' | 'middle' | 'current') => (
209
- <BreadcrumbItem key={`${role}-${typeof spec.label === 'string' ? spec.label : Math.random()}`} role={role}>
208
+ // 2026-07-04 修:非字串 label key 原用 Math.random() 每次 render key 造成
209
+ // BreadcrumbItem unmount/remount(重置 TruncatedLabel ResizeObserver state)→ stable idx key
210
+ const renderItem = (spec: BreadcrumbItemSpec, role: 'root' | 'middle' | 'current', idx: number) => (
211
+ <BreadcrumbItem key={`${role}-${typeof spec.label === 'string' ? spec.label : idx}`} role={role}>
210
212
  {role === 'current'
211
213
  ? <BreadcrumbPage startIcon={spec.startIcon}>
212
214
  <TruncatedLabel fullText={typeof spec.label === 'string' ? spec.label : undefined}>{spec.label}</TruncatedLabel>
@@ -264,7 +266,7 @@ const BreadcrumbList = React.forwardRef<HTMLOListElement, BreadcrumbListProps>(
264
266
  </BreadcrumbItem>
265
267
  )
266
268
  } else {
267
- rendered.push(renderItem(entry.spec, entry.role))
269
+ rendered.push(renderItem(entry.spec, entry.role, i))
268
270
  }
269
271
  })
270
272
  return rendered
@@ -596,7 +598,11 @@ export const breadcrumbMeta = {
596
598
 
597
599
  },
598
600
  sizes: {
599
-
601
+ // 值源 = code SSOT:BREADCRUMB_SIZE_TEXT(sm/md=text-body, lg=text-body-lg)+ BREADCRUMB_ICON_SIZE(sm/md=16, lg=20)。
602
+ // Breadcrumb 是文字列無固定高度 token,故不列 height(對照 Tag meta 有自身 h-* 才列)。
603
+ sm: { iconSize: 16, typography: 'body' },
604
+ md: { iconSize: 16, typography: 'body' },
605
+ lg: { iconSize: 20, typography: 'body-lg' },
600
606
  },
601
607
  states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
602
608
  tokens: {
@@ -54,7 +54,8 @@ export const Overview: Story = {
54
54
  <section>
55
55
  <H3>Selection state-driven 渲染(0 / N)</H3>
56
56
  <Desc>
57
- <code>selection.length === 0</code> null(不佔 layout)。<code>{`> 0`}</code> → bar 浮現。
57
+ <code>selection.length === 0</code> 且無 <code>totalSelected</code> → null(不佔 layout);
58
+ 有選取(含反向選取 <code>totalSelected {'>'} 0</code>,見 spec「Extend dataset pattern」)→ 主 bar 浮現。
58
59
  擴 dataset 的 hint banner 由 consumer 用 <code>Alert</code> 元件包在 BulkActionBar 上方/下方,
59
60
  不在 BulkActionBar 內部 hardcode(對齊 ref 圖)。
60
61
  </Desc>
@@ -185,7 +186,7 @@ export const Accessibility: Story = {
185
186
  <section>
186
187
  <H3>ARIA roles</H3>
187
188
  <ul className="list-disc list-inside text-caption text-fg-secondary space-y-1">
188
- <li><code>role="toolbar"</code> on root + <code>aria-label="批次操作"</code>(可 i18n override)</li>
189
+ <li><code>role="group"</code> on root + <code>aria-label="批次操作"</code>(可 i18n override;2026-07-06 自 toolbar 降級——未實作 APG toolbar 方向鍵契約,group 語意誠實,Gmail / Linear 同款)</li>
189
190
  <li>Clear icon:<code>aria-label="清除選取"</code></li>
190
191
  <li>actions slot 內各 button 由 consumer 提供 aria-label / 文字</li>
191
192
  <li>Hint banner 用 Alert(自帶 <code>role="status"</code> + <code>aria-live="polite"</code>)</li>
@@ -17,9 +17,9 @@ benchmark:
17
17
 
18
18
  ## 定位
19
19
 
20
- 選取多項 item(table row / list item)後浮現的批次操作列。**不獨立於選取狀態存在**——`selection.length === 0` 時隱藏,`> 0` 時浮現。
20
+ 選取多項 item(table row / list item)後浮現的批次操作列。**不獨立於選取狀態存在**——無選取時隱藏,有選取時浮現(完整判準含 `totalSelected` 反向選取例外,見「Extend dataset pattern」)。
21
21
 
22
- **Layout Family**:非 1-4 family — composite / multi-section(自 own layout,batch action 區 + count 區 + page-action + hint banner)。
22
+ **Layout Family**:非 1-4 family — composite / multi-section(自 own layout:clear 區 + count 區 + batch actions 區;**不含** page-level primary / hint banner,見下方同名段)。
23
23
 
24
24
  **世界級對照**:Linear bulk action toolbar / Polaris IndexTable bulk actions / Material DataGrid `<GridToolbar>` selection mode / Notion database row selection bar / Gmail / GitHub Issues 多選 toolbar。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
25
25
 
@@ -66,10 +66,10 @@ benchmark:
66
66
  ## 結構
67
67
 
68
68
  ```
69
- ┌──────────────────────────────────────────────────────────────┐
70
- │ [✕] [{N} 已選 · M hidden by filter] │ [Action 1] [Action 2] │
71
- │ ↑ clear ↑ count + filter inline ↑ batch actions(consumer)│
72
- └──────────────────────────────────────────────────────────────┘
69
+ ┌────────────────────────────────────────────────────────────────────┐
70
+ │ [✕] [已選 {N} · {M} 個被 filter 隱藏] │ [Action 1] [Action 2] │
71
+ │ ↑ clear ↑ count + filter inline ↑ batch actions(consumer)│
72
+ └────────────────────────────────────────────────────────────────────┘
73
73
  ```
74
74
 
75
75
  - 全 md Buttons(`same-row consistency`,close X 同 size;rationale 見下方「Size canonical」)
@@ -82,7 +82,7 @@ benchmark:
82
82
  ### Slot
83
83
 
84
84
  - **`actions`**:consumer 提供 **md** Buttons(size rationale 見「Size canonical」);`variant=tertiary`(主)/ `tertiary danger`(destructive)— **不用 primary**(留 dialog 確認最終 action)
85
- - **count 區**:`{N} 已選`(內建)+ inline filter hidden status `· {M} 個被 filter 隱藏`(`hiddenByFilter` prop 傳入時)
85
+ - **count 區**:`已選 {N} 項`(內建)+ inline filter hidden status `· {M} 個被 filter 隱藏`(`hiddenByFilter` prop 傳入時)
86
86
  - **clear**:`<Button iconOnly size=md variant=text dismiss />`(內建,觸發 `onClear`)
87
87
 
88
88
  #### Size canonical(2026-05-04 升 SSOT)
@@ -120,7 +120,7 @@ BulkActionBar 是 plain block(無 positioning 邏輯),consumer 用 flex column
120
120
  </div>
121
121
  ```
122
122
 
123
- **為什麼撤 top-replace**:Polaris IndexTable / Material DataGrid / GitHub / Gmail 等「替代 toolbar」做法在 selection 期間**喪失 filter / sort / search 功能**,user workflow 斷裂(「我選了 50 個再 filter 出 status=error 子集 batch action」這種常見 workflow 卡關)。本 DS 採 Linear / Notion / Apple Mail / iOS Files / Notion / Atlassian additive 派 — toolbar 永遠保留。
123
+ **為什麼撤 top-replace**:Polaris IndexTable / Material DataGrid / GitHub / Gmail 等「替代 toolbar」做法在 selection 期間**喪失 filter / sort / search 功能**,user workflow 斷裂(「我選了 50 個再 filter 出 status=error 子集 batch action」這種常見 workflow 卡關)。本 DS 採 Linear / Notion / Apple Mail / iOS Files / Atlassian additive 派 — toolbar 永遠保留。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
124
124
 
125
125
  ### Layout 行為(4 use case 全 covered by inline composition)
126
126
 
@@ -138,18 +138,19 @@ case 4 比較少見,consumer 知道自己 layout 時自加 wrapper 即可,DS pri
138
138
  ## API
139
139
 
140
140
  ```ts
141
- interface BulkActionBarProps {
142
- /** 已選 ID,length === 0 時自動隱藏(回 null) */
141
+ // extends HTMLAttributes:其餘 div 屬性(id / data-* / event handlers)spread 到 root
142
+ interface BulkActionBarProps extends React.HTMLAttributes<HTMLDivElement> {
143
+ /** 已選 ID;length === 0 且無 totalSelected 時自動隱藏(回 null,完整判準見「Extend dataset pattern」) */
143
144
  selection: readonly string[]
144
145
  /** Clear 觸發,user 點 X icon(consumer 在 page-level 監聽 Esc 觸發) */
145
146
  onClear?: () => void
146
147
  /** 批次 actions(consumer 提供 md Button,variant=tertiary 或 tertiary+danger,不用 primary) */
147
148
  actions?: React.ReactNode
148
- /** Filter 模式:hidden 數量,顯示在 count 區 inline {N} 已選 · {M} 個被 filter 隱藏」 */
149
+ /** Filter 模式:hidden 數量,顯示在 count 區 inline 「已選 {N} · {M} 個被 filter 隱藏」 */
149
150
  hiddenByFilter?: number
150
151
  /** 擴選整個 dataset 後的真總數;number 時 count 顯示此值,否則 fallback selection.length(見「Extend dataset pattern」) */
151
152
  totalSelected?: number | null
152
- /** i18n labels(Partial,merge with default;對齊 Material localeText / Polaris i18n 慣例) */
153
+ /** i18n labels(Partial,merge with default;對齊 Material localeText / Polaris i18n 慣例 — @benchmark-unverified) */
153
154
  labels?: Partial<BulkActionBarLabels>
154
155
  className?: string
155
156
  }
@@ -168,7 +169,7 @@ interface BulkActionBarLabels {
168
169
 
169
170
  ### Extend dataset pattern(totalSelected)
170
171
 
171
- 「本頁全選 → hint 點擊 → 擴選整個 dataset」2-step 後,consumer 把 `totalSelected` 設為 dataset 真總數,count 區改顯示該值(否則 fallback `selection.length`)——避免 Alert 顯「已選 5370」但 bar 仍顯「已選 50」的不同步(2026-05-13 ship)。對齊 Gmail / Linear / Notion 全選 dataset hint pattern。
172
+ 「本頁全選 → hint 點擊 → 擴選整個 dataset」2-step 後,consumer 把 `totalSelected` 設為 dataset 真總數,count 區改顯示該值(否則 fallback `selection.length`)——避免 Alert 顯「已選 5370」但 bar 仍顯「已選 50」的不同步(2026-05-13 ship)。對齊 Gmail / Linear / Notion 全選 dataset hint pattern。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
172
173
 
173
174
  **可見性與反向選取(DataTable all 模式,2026-06-22)**:顯示判準 = `selection.length > 0 || (totalSelected ?? 0) > 0`。反向選取(`{ mode:'all', excluded }`)下若可見列全被 excluded、`selection`(可見代表)為空但 `totalSelected > 0`(全集仍有選取),bar **仍顯示**(否則「已選 N 個但 bar 消失」矛盾)。對應 `data-table.spec.md`「L2 選取」inverted 模型。
174
175
 
@@ -177,7 +178,7 @@ interface BulkActionBarLabels {
177
178
  ## a11y 預設
178
179
 
179
180
  - BulkActionBar 整體用 `role="toolbar"` + `aria-label`(default `"批次操作"`,可 override)
180
- - count 文字用 `aria-live="polite"` 通知 selection 變更(SR 讀「3 selected」)
181
+ - count 文字用 `aria-live="polite"` + `aria-atomic="true"`(selection 變更時 SR 整句重讀「已選 3 項」)
181
182
  - Clear button:`aria-label="清除選取"`
182
183
  - Hint banner 用 `role="status"` + `aria-live="polite"`(state 切換時通知)
183
184
  - 鍵盤:Esc → `onClear()`(consumer 應監聽 page-level keydown 觸發);Tab 序按 DOM 順序 = clear(X)→ actions(count 是純文字 span,非互動元素 → 不參與 Tab)
@@ -21,7 +21,10 @@ export interface BulkActionBarLabels {
21
21
  count: (n: number) => string
22
22
  clear: string
23
23
  hiddenSuffix: (hidden: number) => string
24
+ /** @deprecated 2026-07-06 role 已降級 "group"(user 拍板),改用 `ariaLabel`;此 key 保留 backward-compat(值仍作 fallback)。 */
24
25
  toolbarAriaLabel: string
26
+ /** root `role="group"` 的 aria-label(2026-07-06 新名;未傳 fallback toolbarAriaLabel)。 */
27
+ ariaLabel?: string
25
28
  }
26
29
 
27
30
  // code-quality-allow: dead-export — public API per spec.md(consumer spread + override)
@@ -41,7 +44,7 @@ export interface BulkActionBarProps extends React.HTMLAttributes<HTMLDivElement>
41
44
  onClear?: () => void
42
45
  /** 批次 actions(consumer 提供 md Button,variant=tertiary 或 tertiary+danger;不用 primary) */
43
46
  actions?: React.ReactNode
44
- /** Filter 模式:hidden 數量,顯示在 count 區 inline{N} 已選 · {M} 個被 filter 隱藏」 */
47
+ /** Filter 模式:hidden 數量,顯示在 count 區 inline「已選 {N} · {M} 個被 filter 隱藏」 */
45
48
  hiddenByFilter?: number
46
49
  /**
47
50
  * 「擴選整個 dataset」狀態(2026-05-13 ship,per user 抓 Alert「已選 5370」但 BulkActionBar
@@ -89,8 +92,11 @@ const BulkActionBar = React.forwardRef<HTMLDivElement, BulkActionBarProps>(
89
92
  return (
90
93
  <div
91
94
  ref={ref}
92
- role="toolbar"
93
- aria-label={labels.toolbarAriaLabel}
95
+ // 2026-07-06 user 拍板:role "toolbar" → "group" — 原宣告 toolbar 但未實作 APG toolbar
96
+ // 方向鍵 roving 契約(AT 告知「工具列」但方向鍵無反應 = 空承諾);group 語意誠實,
97
+ // Tab 序照 DOM(spec 本有 canonical),Gmail / Linear bulk bar 實務同款。
98
+ role="group"
99
+ aria-label={labels.ariaLabel ?? labels.toolbarAriaLabel}
94
100
  className={cn(
95
101
  'flex items-center gap-2',
96
102
  'px-[var(--layout-space-loose)] py-[var(--layout-space-tight)]',
@@ -120,7 +126,7 @@ const BulkActionBar = React.forwardRef<HTMLDivElement, BulkActionBarProps>(
120
126
  理由:count 是 state-bearing 主資訊(「你在 selection mode + N items」),非裝飾
121
127
  World-class 共識:Linear/Notion/Carbon/Polaris 均用 primary foreground;muted 化弱化 state signal
122
128
  hiddenByFilter suffix 維持 muted(這是次資訊,視覺層次正確) */}
123
- {/* 2026-05-31 #3:aria-live 通知 SR 選取數變更;#20:補 font-medium 對齊 spec L90 + 上方 comment(state-bearing 主資訊) */}
129
+ {/* 2026-05-31 #3:aria-live 通知 SR 選取數變更;#20:補 font-medium 對齊 spec「Count text color canonical」表 + 上方 comment(state-bearing 主資訊) */}
124
130
  <span className="text-body text-foreground font-medium tabular-nums" aria-live="polite" aria-atomic="true">
125
131
  {/* 2026-05-13:totalSelected override 走 dataset 擴選後真總數,否則 fallback page-level selection.length */}
126
132
  {labels.count(typeof totalSelected === 'number' ? totalSelected : selection.length)}
@@ -26,9 +26,9 @@ const STATES: StateKey[] = ['default', 'hover', 'active', 'disabled']
26
26
 
27
27
  const TOKEN_MAP: Record<VariantKey, Record<StateKey, ColorSpec>> = {
28
28
  primary: {
29
- default: { bg: '--primary', text: 'white', border: 'transparent' },
30
- hover: { bg: '--primary-hover', text: 'white', border: 'transparent' },
31
- active: { bg: '--primary-active', text: 'white', border: 'transparent' },
29
+ default: { bg: '--primary', text: '--on-emphasis', border: 'transparent' },
30
+ hover: { bg: '--primary-hover', text: '--on-emphasis', border: 'transparent' },
31
+ active: { bg: '--primary-active', text: '--on-emphasis', border: 'transparent' },
32
32
  disabled: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent' },
33
33
  },
34
34
  secondary: {
@@ -59,9 +59,9 @@ const TOKEN_MAP: Record<VariantKey, Record<StateKey, ColorSpec>> = {
59
59
 
60
60
  const DANGER_MAP: Partial<Record<VariantKey, Record<StateKey, ColorSpec>>> = {
61
61
  primary: {
62
- default: { bg: '--error', text: 'white', border: 'transparent' },
63
- hover: { bg: '--error-hover', text: 'white', border: 'transparent' },
64
- active: { bg: '--error-active', text: 'white', border: 'transparent' },
62
+ default: { bg: '--error', text: '--on-emphasis', border: 'transparent' },
63
+ hover: { bg: '--error-hover', text: '--on-emphasis', border: 'transparent' },
64
+ active: { bg: '--error-active', text: '--on-emphasis', border: 'transparent' },
65
65
  disabled: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent' },
66
66
  },
67
67
  secondary: {
@@ -168,7 +168,7 @@ Tag md 和 Button xs 同 24px,但:
168
168
 
169
169
  - `startIcon`:最多一個,放在最左側
170
170
  - `badge` 和 `endIcon` 可同時出現在右側
171
- - `badge` 傳入 Badge 元件(通知計數指示器)。Badge 層級應匹配按鈕的視覺重量——深色底按鈕(primary、secondary+danger)只適合 `critical`,詳見 `badge.spec.md`
171
+ - `badge` 傳入 Badge 元件(通知計數指示器)。Badge 層級受按鈕底色的 contrast floor 約束——深色底按鈕(primary、secondary+danger)floor `high`(`high` / `critical` 可直接用;`low` / `medium` 被迫 bump 是設計錯配訊號),詳見 `badge.spec.md`「各容器的 contrast floor 對照」
172
172
 
173
173
  ---
174
174
 
@@ -362,7 +362,7 @@ const ICON_ONLY_BASE = 'aspect-square p-0 min-w-0 gap-0'
362
362
  - FileItem / DataTable **row delete(Trash icon)**→ 一般 Button xs 或 Inline Action(看 row size),**不套 dismiss**
363
363
  - Tag dismiss X / Input clear X → Inline Action(host chrome padding 內,不是 Button)
364
364
 
365
- 完整 predicate 見 `patterns/element-anatomy/item-anatomy.spec.md`「Predicate」+「Dismiss canonical」。
365
+ 完整 predicate 見 `patterns/element-anatomy/inline-action.spec.md`「Predicate:Inline Action vs Button iconOnly」+「Dismiss canonical — X close only」(2026-04-24 自 item-anatomy.spec.md 抽出)。
366
366
 
367
367
  #### `data-unbounded` attribute(2026-04-22 v5 canonical)
368
368
 
@@ -456,6 +456,7 @@ N/A(action trigger,無資料層)。
456
456
  - `header-canonical.spec.md`
457
457
  - `item-anatomy.spec.md`
458
458
  - `overlay-surface.spec.md`
459
+ - `pagination.spec.md`
459
460
  - `segmented-control.spec.md`
460
461
  - `switch.spec.md`
461
462
  - `tabs.spec.md`