@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
@@ -35,6 +35,20 @@ export interface DateFormatOptions {
35
35
  * (跟 ISO date 視覺對不上,跟 sort 順序也對不上)。Ant / Material X / Apple HIG
36
36
  * 一致 year-first。Consumer 想自訂可傳 `formatOptions` + `locale`。
37
37
  */
38
+ // 2026-07-06 D3 perf:formatOptions/locale 顯式傳入時走 Intl.DateTimeFormat(貴 constructor)。
39
+ // DataTable date 欄轉發此 config → 每 cell × 每次表格 re-render 重建。module-level cache 重用實例
40
+ // (無狀態可安全重用),key = locale|JSON.stringify(options)。行為 Δ=0。
41
+ const dtfCache = new Map<string, Intl.DateTimeFormat>()
42
+ function getDateTimeFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {
43
+ const key = locale + '|' + JSON.stringify(options)
44
+ let fmt = dtfCache.get(key)
45
+ if (!fmt) {
46
+ fmt = new Intl.DateTimeFormat(locale, options)
47
+ dtfCache.set(key, fmt)
48
+ }
49
+ return fmt
50
+ }
51
+
38
52
  function formatDate(
39
53
  value: string | number | Date,
40
54
  options: DateFormatOptions = {},
@@ -43,7 +57,7 @@ function formatDate(
43
57
  if (Number.isNaN(date.getTime())) return String(value)
44
58
  // 若 consumer 顯式傳 formatOptions / locale → 走 Intl.DateTimeFormat
45
59
  if (options.formatOptions || options.locale) {
46
- return new Intl.DateTimeFormat(options.locale ?? 'en-US', options.formatOptions ?? { year: 'numeric', month: '2-digit', day: '2-digit' }).format(date)
60
+ return getDateTimeFormatter(options.locale ?? 'en-US', options.formatOptions ?? { year: 'numeric', month: '2-digit', day: '2-digit' }).format(date)
47
61
  }
48
62
  // 預設:YYYY/MM/DD(直接組,locale-independent + 視覺穩定)
49
63
  const y = date.getFullYear()
@@ -537,13 +551,9 @@ const DatePicker = React.forwardRef<HTMLDivElement, DatePickerProps>(
537
551
  if (e.key === 'Escape') setOpen(false)
538
552
  }}
539
553
  className={cn(
540
- fieldWrapperStyles({ mode: 'edit', variant: variant, size }),
554
+ fieldWrapperStyles({ mode: 'edit', variant: variant, size, error }),
541
555
  'text-left cursor-pointer',
542
556
  'focus-visible:outline-none',
543
- error && [
544
- 'border-error hover:border-error-hover',
545
- 'focus-within:border-error focus-within:hover:border-error',
546
- ],
547
557
  className,
548
558
  )}
549
559
  {...props}
@@ -640,10 +650,13 @@ const DatePicker = React.forwardRef<HTMLDivElement, DatePickerProps>(
640
650
  }
641
651
  />
642
652
  </div>
643
- {showTime && (
653
+ {(showTime || needConfirm) && (
644
654
  // Footer:消費 SurfaceFooter SSOT(border-t + canonical px-loose py-tight padding,
645
655
  // 不再 hand-coded p-2 / Separator / ml-auto wrapper 三層垃圾)。
646
656
  // 「此刻」加 mr-auto 把後面 button 推右(對齊 Ant `marginInlineStart: auto` on OK)。
657
+ // 2026-07-05 D4:gate 由 `showTime` 改 `(showTime || needConfirm)` 對齊 Range(:1068)—
658
+ // date-only `needConfirm` 時 onSelect 只寫 draft 且不關 popover,footer「確定」是唯一
659
+ // commit 路徑,原 gate 漏渲 footer = draft 永遠無法 commit 的死路。
647
660
  <SurfaceFooter>
648
661
  <Button variant="tertiary" size="sm" onClick={handleNow} className="mr-auto">此刻</Button>
649
662
  {needConfirm ? (
@@ -756,6 +769,15 @@ const DatePickerRange = React.forwardRef<HTMLDivElement, DatePickerRangeProps>(
756
769
  const [draft, setDraft] = React.useState<[string | null, string | null]>(value ?? [null, null])
757
770
  const [activeEnd, setActiveEnd] = React.useState<'start' | 'end'>('start')
758
771
 
772
+ // 2026-07-05 D4 focus 回復:Range 只用 PopoverAnchor(start/end button 自行 onClick 開啟),
773
+ // Radix triggerRef 恆 null → 內建 onCloseAutoFocus 的 triggerRef.focus() no-op 後又
774
+ // preventDefault 掉 FocusScope fallback → Esc/確定/選完自動關閉後焦點掉到 document.body
775
+ // (WCAG 2.4.3)。手動回焦 active 端 button(對齊 Ant RangePicker);outside-click 關閉
776
+ // 依 Radix non-modal 慣例不搶焦(hasInteractedOutside 鏡射 Radix 自家 guard)。
777
+ const startBtnRef = React.useRef<HTMLButtonElement>(null)
778
+ const endBtnRef = React.useRef<HTMLButtonElement>(null)
779
+ const hasInteractedOutsideRef = React.useRef(false)
780
+
759
781
  // Sync draft from value ONLY on open false→true(canonical 2026-05-02 v3):
760
782
  // 之前用 `[value, open]` 雙 dep,popover 開啟期間 value 任何 reference 變更 → useEffect
761
783
  // 觸發 → 直接 clobber user 的 draft 編輯。改成只在 open 從 false→true 同步。
@@ -921,18 +943,15 @@ const DatePickerRange = React.forwardRef<HTMLDivElement, DatePickerRangeProps>(
921
943
  data-error={error ? '' : undefined}
922
944
  data-state={open ? 'open' : 'closed'}
923
945
  className={cn(
924
- fieldWrapperStyles({ mode: 'edit', size }),
946
+ fieldWrapperStyles({ mode: 'edit', size, error }),
925
947
  'cursor-text',
926
- error && [
927
- 'border-error hover:border-error-hover',
928
- 'focus-within:border-error focus-within:hover:border-error',
929
- ],
930
948
  className,
931
949
  )}
932
950
  {...props}
933
951
  >
934
952
  <button
935
953
  type="button"
954
+ ref={startBtnRef}
936
955
  onClick={() => openWithActive('start')}
937
956
  data-active-end={open && activeEnd === 'start' ? 'true' : undefined}
938
957
  aria-label={resolvedPlaceholder[0]}
@@ -950,6 +969,7 @@ const DatePickerRange = React.forwardRef<HTMLDivElement, DatePickerRangeProps>(
950
969
  <ArrowRight size={iconSize} className="shrink-0 text-fg-muted mx-2" aria-hidden />
951
970
  <button
952
971
  type="button"
972
+ ref={endBtnRef}
953
973
  onClick={() => openWithActive('end')}
954
974
  data-active-end={open && activeEnd === 'end' ? 'true' : undefined}
955
975
  aria-label={resolvedPlaceholder[1]}
@@ -979,7 +999,22 @@ const DatePickerRange = React.forwardRef<HTMLDivElement, DatePickerRangeProps>(
979
999
  </ItemSuffix>
980
1000
  </div>
981
1001
  </PopoverAnchor>
982
- <PopoverContent className="w-auto p-0" align="start">
1002
+ <PopoverContent
1003
+ className="w-auto p-0"
1004
+ align="start"
1005
+ // 2026-07-05 D4 focus 回復(機制詳 :758 comment):outside-click 標記不搶焦(鏡射
1006
+ // Radix non-modal 自家 hasInteractedOutside guard);其餘關閉路徑(Esc / 確定 /
1007
+ // 選完自動關)preventDefault 掉內建 triggerRef.focus()(Anchor-only 恆 null no-op)
1008
+ // 後手動回焦 active 端 button — 對齊 Ant RangePicker 關閉後焦點回 active 端輸入格。
1009
+ onInteractOutside={() => { hasInteractedOutsideRef.current = true }}
1010
+ onCloseAutoFocus={(e) => {
1011
+ e.preventDefault()
1012
+ if (!hasInteractedOutsideRef.current) {
1013
+ ;(activeEnd === 'start' ? startBtnRef : endBtnRef).current?.focus()
1014
+ }
1015
+ hasInteractedOutsideRef.current = false
1016
+ }}
1017
+ >
983
1018
  {/* 2026-05-06 v9.1 M25 chain — 同 single DatePicker 修法,viewport 壓縮 calendar 內滾 + footer 永遠 in-view */}
984
1019
  <div role="dialog" aria-label="日期區間選擇" className="flex flex-col flex-1 min-h-0">
985
1020
  <div className="flex-1 min-h-0 overflow-y-auto">
@@ -1123,9 +1158,11 @@ export const datePickerMeta = {
1123
1158
  sizes: {
1124
1159
 
1125
1160
  },
1126
- states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
1161
+ // 'selected' = range 帶持續選中(before:bg-neutral-selected,本檔自有);'active' 移除 — 全檔無按壓
1162
+ // 視覺(data-active-end 是定位 attr 非按壓 token;2026-07-07 詞彙統一 DS-wide 交叉掃補修)。
1163
+ states: ['default', 'hover', 'selected', 'focus-visible', 'disabled'],
1127
1164
  tokens: {
1128
- bg: [],
1165
+ bg: ['bg-neutral-selected'],
1129
1166
  fg: ['text-fg-disabled', 'text-fg-muted'],
1130
1167
  ring: [],
1131
1168
  },
@@ -194,7 +194,7 @@ DescriptionList 是**唯讀 label / value 資料呈現**(非互動 / 非 variant
194
194
 
195
195
  ## 邊界案例
196
196
 
197
- - **Disabled(action cell)**:DescriptionList 主體為唯讀資料呈現,本身無 disabled state;若 `<dd>` consumer 嵌入 inline action(Button / Link),該 action 自行 own disabled,DL 不干預(token Button SSOT)
197
+ - **Disabled(action cell)**:DescriptionList 主體為唯讀資料呈現,本身無 disabled state。**dd 內互動內容 canonical(2026-07-04 Q9 拍板,三處立場收斂)**:操作型互動(Button / Input)**禁止**(見「禁止事項」— 需要動作 → Field read-only / inline-edit / action row / 卡片 header action);展示 / 導覽型 ReactNode(Tag / Badge / **Link**)**允許**,Link 自行 own disabled,DL 不干預。對齊 Polaris(ReactNode 型別 + 純敘述範例)/ Ant(action 放 extra 不放 dd)/ Carbon(read-only default)三家共識。
198
198
  - **Loading**:DL 非 async surface,無 loading prop。若 page-level data 載入中,consumer 應在 DL 外層用 `<Skeleton>`(每組 dt/dd 對應一條 skeleton line)取代;DL 本體不渲染 loading state。
199
199
  - **Empty(no items / empty value)**:no items → consumer 應 conditional 渲 `<Empty>` 取代 DL,不渲空 `<dl>`(SR 會讀「empty list」造成混淆);individual value 空 → consumer 應自行傳入 `—`(em dash,包 `text-fg-muted`;元件不自動 normalize,已在「無障礙」段 codify)。
200
200
  - **長 label / 長值**:horizontal 模式 label(`dt`)`shrink-0` 不收縮、不截斷,value(`dd`)`break-all min-w-0` 斷行吸收擠壓;vertical 模式兩者佔滿欄寬自然換行。無空白長字串(URL / ID)詳「無障礙」段「長值換行」。
@@ -112,7 +112,9 @@ const DescriptionItem = React.forwardRef<HTMLDivElement, DescriptionItemProps>(
112
112
  return (
113
113
  <div ref={ref} className={cn('flex flex-col', className)} {...props}>
114
114
  <dt className="text-body text-fg-secondary">{label}</dt>
115
- <dd className="text-body mt-[var(--item-gap-label-desc-reading)]">{children}</dd>
115
+ {/* text-foreground 顯式宣告(2026-07-05 fix):與 horizontal 分支一致,不靠容器繼承 —
116
+ spec Typography 段 / tsx docblock / anatomy Overview 三處 canonical 皆宣稱 dd = text-foreground */}
117
+ <dd className="text-body text-foreground mt-[var(--item-gap-label-desc-reading)]">{children}</dd>
116
118
  </div>
117
119
  )
118
120
  },
@@ -103,7 +103,7 @@ Modal 與 viewport 四邊保持 `--layout-space-bottom`(48px)最小間距。
103
103
  - Header 只有 title + close X → max layout = 24 → header = 24 + 2×`py-tight`(隨 page:md 12 / lg 16)= **48 md / 56 lg**(= `--chrome-header-height` 同 tier 對齊)✓
104
104
  - Header 塞 bounded primary(無 `data-unbounded`)→ header 自然長高
105
105
 
106
- **Canonical 來源**:Dialog 是 overlay chrome,corner close X 屬 action group region,必用 Button(非 Inline Action / 非自刻 button)。詳見 `patterns/element-anatomy/item-anatomy.spec.md`「Dismiss canonical」+ `patterns/overlay-surface/overlay-surface.spec.md`「Chrome dismiss size canonical」。
106
+ **Canonical 來源**:Dialog 是 overlay chrome,corner close X 屬 action group region,必用 Button(非 Inline Action / 非自刻 button)。詳見 `patterns/element-anatomy/inline-action.spec.md`「Dismiss canonical — X close only」+ `patterns/overlay-surface/overlay-surface.spec.md`「Chrome dismiss size canonical」。
107
107
 
108
108
  ## Title
109
109
 
@@ -244,7 +244,8 @@ export const dialogMeta = {
244
244
  sizes: {
245
245
 
246
246
  },
247
- states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
247
+ // 2026-07-04 audit 對齊:容器無 hover/active/disabled 態(spec「狀態處理的職責邊界」明文;對齊 popover meta 已修 pattern)
248
+ states: ['default'],
248
249
  tokens: {
249
250
  bg: ['bg-surface-raised'],
250
251
  fg: ['text-fg-secondary'],
@@ -253,6 +253,7 @@ export const Overview = {
253
253
  ['SubTrigger', 'badge', 'ReactNode', '—', '子選單狀態 badge'],
254
254
  ['CheckboxItem', 'checked', 'boolean', '—', '勾選狀態'],
255
255
  ['CheckboxItem', 'startIcon', 'LucideIcon', '—', '左側 icon'],
256
+ ['CheckboxItem', 'description', 'ReactNode', '—', '次要說明文字'],
256
257
  ].map(([comp, p, t, d, desc], i) => (
257
258
  <tr key={i}><Td>{comp}</Td><Td mono>{p}</Td><Td mono>{t}</Td><Td mono>{d}</Td><Td>{desc}</Td></tr>
258
259
  ))}
@@ -647,7 +648,8 @@ export const ColorMatrix = {
647
648
  ['suffix icon (direction)', '--fg-muted', 'ChevronRight, ExternalLink — via endIcon prop'],
648
649
  ['suffix value text', '--fg-muted', '"深色", "已啟用"'],
649
650
  ['shortcut text', '--fg-muted', '⌘C, ⌘E — via shortcut prop'],
650
- ['all icons (disabled)', '--fg-disabled', 'unified'],
651
+ ['prefix icon / label (disabled)', '--fg-disabled', 'startIcon 與 label 同步切 disabled'],
652
+ ['suffix icon / shortcut (disabled)', '--fg-muted', '容器套 opacity-disabled 減淡,非 fg-disabled(menu-item.spec.md「狀態」)'],
651
653
  ].map(([role, token, ex], i) => (
652
654
  <tr key={i}>
653
655
  <Td>{role}</Td>
@@ -162,12 +162,14 @@ SubContent 使用相同規格,包括 sideOffset。
162
162
 
163
163
  透過 `DropdownMenuContent` 的 `size` prop 設定,經 SizeContext 向下傳遞,所有子項目自動套用。
164
164
 
165
- | Size | Label | Icon | Checkbox/Radio 控件 |
165
+ | Size | Label | Icon | Checkbox 控件 |
166
166
  |---|---|---|---|
167
167
  | sm | `text-body leading-compact` (14px, 1.3) | 16px | 16px |
168
168
  | md | `text-body leading-compact` (14px, 1.3) | 16px | 16px |
169
169
  | lg | `text-body-lg leading-compact` (16px, 1.3) | 20px | 20px |
170
170
 
171
+ RadioItem 不渲染 radio 控件——選中以 `bg-neutral-selected` 背景高亮(見 `../Menu/menu-item.spec.md`「選中指示器」),故控件尺寸欄僅適用 CheckboxItem。
172
+
171
173
  DropdownMenu 是掃描模式——使用者快速瀏覽選項,所有尺寸使用 `leading-compact`。
172
174
 
173
175
  ---
@@ -180,7 +182,7 @@ DropdownMenu 是掃描模式——使用者快速瀏覽選項,所有尺寸使
180
182
  |---|---|
181
183
  | `↑` `↓` | 移動焦點 |
182
184
  | `Home` / `End` | 跳到第一/最後一個 item(`PageUp` / `PageDown` 同) |
183
- | 首字 typeahead | 打字元自動跳到符合的 item |
185
+ | 首字 typeahead | 打字元自動跳到 **label 符合**的 item——children 為字串時元件自動 forward `textValue`,description / badge / shortcut 不進比對;複雜 children(非純字串)由 consumer 顯式給 `textValue` |
184
186
  | `Enter` / `Space` | 執行目前焦點的動作 |
185
187
  | `Escape` | 關閉整個選單(含所有展開子層;Radix root close)。逐層收合用 `←` |
186
188
  | `→` | 展開子選單 |
@@ -207,7 +209,7 @@ DropdownMenu 是掃描模式——使用者快速瀏覽選項,所有尺寸使
207
209
 
208
210
  ## StateBehavior(DropdownMenu 層級特有)
209
211
 
210
- Item-level default / hover / focused / selected / disabled **色彩**由 MenuItem primitive SSOT 擁有(`patterns/element-anatomy/item-anatomy.spec.md`「選擇 / 狀態視覺規則」),在本元件 `ColorMatrix` story 承載並直接引用 MenuItem tokenDropdownMenu 的 `StateBehavior` story 展示**浮層層級特有的動態行為**:open / close 動畫(Radix `data-state` 驅動)、Submenu 展開 / 收起、CheckboxItem toggle(多選不 close)——這些是 item primitive 沒有的維度。
212
+ Item-level default / hover / focused / selected / disabled **色彩**由 MenuItem primitive SSOT 擁有(`patterns/element-anatomy/item-anatomy.spec.md`「選擇 / 狀態視覺規則」),在本元件 `ColorMatrix` story 承載並直接引用 MenuItem token。**selected × highlighted 疊加(2026-07-04 Q2 拍板)**:selected bg 勝 Radix `data-[highlighted]`(hover + keyboard cursor)— 對齊 MenuItem「selected 勝 hover」canonical(menu-item.spec.md 狀態表),`selected && 'data-[highlighted]:bg-neutral-selected'` override;CheckboxItem/RadioItem checked 同規則。DropdownMenu 的 `StateBehavior` story 展示**浮層層級特有的動態行為**:open / close 動畫(Radix `data-state` 驅動)、Submenu 展開 / 收起、CheckboxItem toggle(多選不 close)——這些是 item primitive 沒有的維度。
211
213
 
212
214
  ---
213
215
 
@@ -218,6 +220,7 @@ Item-level default / hover / focused / selected / disabled **色彩**由 MenuIte
218
220
  - **Loading(async submenu / async items)**:DropdownMenu primitive 不獨立 own loading prop。async submenu 場景應由 consumer 在 `<DropdownMenuSub>` 內條件性渲 `<DropdownMenuItem disabled>` + spinner label(如「載入中...」);或 fetch options 前先 disable 整個 menu trigger。完整 loading body 替換 pattern 屬 SelectMenu scope,不在 DropdownMenu scope。
219
221
  - **Empty(no items / async fetch result empty)**:consumer 應條件性渲 `<DropdownMenuItem disabled>` 顯示「無可用動作」字樣;不渲空 menu。
220
222
  - **Dark mode / density**:走 MenuItem + Popover SSOT 自動 adapt;density 繼承 page(不鎖;DropdownMenu item 高度 = field-height-{size} 隨 page density 變,鎖 md 會釘死 md-scale 對不上 lg 觸發點),SSOT 見 `tokens/density/density.spec.md`。
223
+ - **大量 item(> 100)**:建議上游分頁 / 搜尋收斂或考慮 windowing(對齊 Ant Select / MUI Autocomplete 慣例);本元件無虛擬化——action menu 本不該長到這個量級,超過先重想資訊架構(選值場景改用 SelectMenu 系 + searchable)。
221
224
 
222
225
  ---
223
226
 
@@ -237,11 +240,11 @@ Item-level default / hover / focused / selected / disabled **色彩**由 MenuIte
237
240
  - Tab — focus trigger
238
241
  - Enter / Space / ↓ — 開啟
239
242
  - ↑/↓ — 導覽 items(Home/End 跳首尾項,PageUp/PageDown 同)
240
- - 首字 typeahead — 打字元自動跳到符合的 item
243
+ - 首字 typeahead — 打字元自動跳到 label 符合的 item(比對 `textValue`,見「鍵盤操作」表)
241
244
  - Enter / Space — 選擇目前焦點的選項
242
245
  - Esc — 關閉
243
246
 
244
- **Focus**:Radix primitive 自管 focus trap / restoration。鍵盤導覽到的項目以 highlight **底色**標示(Radix `data-[highlighted]:bg-neutral-hover`,本元件刻意採 `data-highlighted` 而非 `:focus-visible` outline ring,跨瀏覽器一致 — 見 dropdown-menu.tsx docblock「Hover / highlight canonical」),不畫 `outline: 2px solid var(--ring)` 外框。
247
+ **Focus**:Radix primitive 自管 focus trap / restoration。鍵盤導覽到的項目以 highlight **底色**標示(Radix `data-[highlighted]:bg-neutral-hover`,本元件刻意採 `data-highlighted` 而非 `:focus-visible` outline ring,跨瀏覽器一致 — 見 dropdown-menu.tsx docblock「Hover / highlight canonical」),不畫 `outline: 2px solid var(--ring)` 外框。**Trigger focus 指示**:非 asChild 裸用的 Trigger 顯示 Button focus canonical ring(`focus-visible:ring-2 ring-ring ring-offset-1`,SSOT = button.tsx);asChild 場景由子元件(通常 Button)自帶同款 ring。
245
248
 
246
249
  **驗證**:Storybook a11y addon panel 應 0 critical violation;鍵盤完整可操作(無需滑鼠)。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。
247
250
 
@@ -71,7 +71,13 @@ const DropdownMenuTrigger = React.forwardRef<
71
71
  >(({ className, ...props }, ref) => (
72
72
  <DropdownMenuPrimitive.Trigger
73
73
  ref={ref}
74
- className={cn('outline-none', className)}
74
+ // 2026-07-05:非 asChild 裸用需可見 focus 指示(WCAG 2.4.7)——原 `outline-none` 讓裸 Trigger
75
+ // 完全無 focus ring。消費 Button focus canonical(button.tsx buttonVariants base)。
76
+ // asChild+Button 場景同名 class 重複,無影響。
77
+ className={cn(
78
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',
79
+ className,
80
+ )}
75
81
  {...props}
76
82
  />
77
83
  ))
@@ -243,9 +249,20 @@ const DropdownMenuItem = React.forwardRef<
243
249
  <DropdownMenuPrimitive.Item
244
250
  ref={ref}
245
251
  disabled={disabled}
252
+ // 2026-07-05:typeahead 只比對 label——children 為字串時 auto-forward textValue,避免 Radix
253
+ // fallback 拿整段 textContent(label+description+badge+shortcut 串接)比對。consumer 顯式
254
+ // textValue 在 {...props} 較後 spread,仍優先。
255
+ textValue={typeof children === 'string' ? children : undefined}
246
256
  className={cn(
247
257
  radixItemClass,
248
- selected && 'bg-neutral-selected',
258
+ // 2026-07-04 Q2 拍板:selected bg 勝 hover/highlighted(bg 是唯一選中指示器,被 hover 洗掉
259
+ // = 選中資訊消失)。對齊 MenuItem(menu-item.tsx selected 時關 hover bg)+ Ant Menu
260
+ // `:not(-item-selected)` + VS Code list `:hover:not(.selected)`;Radix highlighted 同時是
261
+ // keyboard cursor,selected 項停留時維持 selected bg(DS bg 通道單一職責)。
262
+ // 2026-07-05 D4 補完 Q2 附帶條件:selected×highlighted(鍵盤 cursor 停留)用加深一階
263
+ // bg-neutral-selected-active(neutral-3;M23 nearest canonical = Button toggle active 同
264
+ // family)— 深化而非洗掉 selection,cursor 可感知(WCAG 2.4.7),對齊 MUI selectedOpacity+hover idiom。
265
+ selected && 'bg-neutral-selected data-[highlighted]:bg-neutral-selected-active',
249
266
  className,
250
267
  )}
251
268
  {...props}
@@ -300,6 +317,8 @@ const DropdownMenuSubTrigger = React.forwardRef<
300
317
  return (
301
318
  <DropdownMenuPrimitive.SubTrigger
302
319
  ref={ref}
320
+ // 2026-07-05:同 DropdownMenuItem——typeahead 只比對 label(endContent 的 value/badge 不進比對)。
321
+ textValue={typeof children === 'string' ? children : undefined}
303
322
  className={cn(
304
323
  radixItemClass,
305
324
  'data-[state=open]:bg-neutral-hover',
@@ -342,6 +361,8 @@ const DropdownMenuCheckboxItem = React.forwardRef<
342
361
  ref={ref}
343
362
  checked={checked}
344
363
  disabled={disabled}
364
+ // 2026-07-05:同 DropdownMenuItem——typeahead 只比對 label(M10 同 pattern:帶 description)。
365
+ textValue={typeof children === 'string' ? children : undefined}
345
366
  onSelect={(e) => e.preventDefault()}
346
367
  className={cn(radixItemClass, className)}
347
368
  {...props}
@@ -421,11 +442,14 @@ const DropdownMenuRadioItem = React.forwardRef<
421
442
  <DropdownMenuPrimitive.RadioItem
422
443
  ref={ref}
423
444
  disabled={disabled}
445
+ // 2026-07-05:同 DropdownMenuItem——typeahead 只比對 label(M10 同 pattern:帶 description)。
446
+ textValue={typeof children === 'string' ? children : undefined}
424
447
  onSelect={(e) => e.preventDefault()}
425
448
  // 2026-05-31 #10:selected 底色套在外層 Radix RadioItem 本身(非 `[&>*]` 子 MenuItem),
426
449
  // 因內層 MenuItem 自帶 `!bg-transparent` 會蓋掉子層 bg → 選中底色從不顯示。
427
450
  // 改 parent-bg pattern(對齊 DropdownMenuItem selected):RadioItem 上底色,MenuItem 透明讓它透出。
428
- className={cn(radixItemClass, 'data-[state=checked]:bg-neutral-selected', className)}
451
+ // 2026-07-04 Q2:checked 亦勝 highlighted(同 DropdownMenuItem selected 規則)
452
+ className={cn(radixItemClass, 'data-[state=checked]:bg-neutral-selected data-[state=checked]:data-[highlighted]:bg-neutral-selected-active', className)}
429
453
  {...props}
430
454
  >
431
455
  <MenuItem
@@ -461,18 +485,20 @@ DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'
461
485
  // Phase 2 fill needed: purpose descriptions + when rationale + world-class refs
462
486
  export const dropdownMenuMeta = {
463
487
  component: 'DropdownMenu',
464
- family: null, // non-family composite / overlay / layout
488
+ family: 1, // 對齊 dropdown-menu.spec.md frontmatter L3(Family 1 消費者;DS 慣例 = meta 對齊 spec frontmatter,原 null 是 Phase 1 boilerplate 殘留)
465
489
  variants: {
466
490
 
467
491
  },
468
492
  sizes: {
469
493
 
470
494
  },
471
- states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
495
+ // 'selected' = 單選/checked item 持續選中(bg-neutral-selected);'active' 保留 — highlight-on-selected
496
+ // 走 bg-neutral-selected-active(active 階 token,M23 nearest canonical;2026-07-07 詞彙統一補修)。
497
+ states: ['default', 'hover', 'active', 'selected', 'focus-visible', 'disabled'],
472
498
  tokens: {
473
- bg: ['bg-neutral-hover', 'bg-surface-raised', 'bg-transparent'],
499
+ bg: ['bg-neutral-hover', 'bg-neutral-selected', 'bg-neutral-selected-active', 'bg-surface-raised', 'bg-transparent'],
474
500
  fg: ['text-fg-disabled', 'text-fg-muted'],
475
- ring: [],
501
+ ring: ['ring-ring'], // 2026-07-05:Trigger focus canonical ring(對齊 Checkbox/Tabs 等 meta 慣例)
476
502
  },
477
503
  } as const
478
504
 
@@ -81,7 +81,7 @@ export const Overview: Story = {
81
81
  <thead><tr><Th>Prop</Th><Th>Type</Th><Th>Default</Th><Th>說明</Th></tr></thead>
82
82
  <tbody>
83
83
  {[
84
- ['icon', 'LucideIcon', '—', 'Avatar 48px 內的 icon'],
84
+ ['icon', 'LucideIcon | ReactElement', '—', 'LucideIcon 自動包 Avatar 48px;ReactElement 原樣渲染(自訂 Avatar / img / CircularProgress)'],
85
85
  ['title', 'string', '—', '主要標題(16px medium)'],
86
86
  ['description', 'string', '必填(預設唯一 slot)', '說明文字(14px;lg RowSizeContext 子樹內 16px · fg-secondary)'],
87
87
  ['action', 'ReactNode', '—', 'CTA button / 操作區'],
@@ -32,6 +32,7 @@ benchmark:
32
32
  - **SelectMenu / Combobox 下拉空**:「無選項」「找不到符合的項目」
33
33
  - **Page section 無內容**:dashboard widget 暫無資料、設定頁未建立任何項目
34
34
  - **初次引導**:讓使用者首次使用時知道「這裡會放什麼」+ 有 CTA 建立
35
+ - **容器級無權限提示**(2026-07-04 Q7 拍板):卡片 / 區塊 / 面板內容因權限不可見 — **必附 request-access 類 action**(對齊 Atlassian「Request access」empty state 官方範例 + Carbon error-management scenario);整頁級 403 / 404 仍禁用(→ 專屬錯誤頁面,見「何時不用」)
35
36
 
36
37
  ## 何時不用
37
38
 
@@ -113,7 +114,7 @@ Empty 是 **non-interactive layout primitive**——本身無 ARIA role(讓 cons
113
114
  | Page section 暫無內容 | `<section aria-labelledby={titleId}>` + `<h2>` 包 title slot | 對齊 WCAG 2.1 SC 2.4.6 標題明確語意 |
114
115
  | 初次引導(有 CTA) | Action Button 自帶 a11y;無需 Empty 層額外 ARIA | CTA 是真實互動 element,自己的 a11y 足夠 |
115
116
 
116
- **Title slot heading level**:Empty 不渲染 heading tag(`<h1>` / `<h2>`),只 `<div>` + 視覺 typography。Consumer 若需 heading semantic,在外層自包 `<h2>{title}</h2>` + 不傳 Empty 的 title prop(避免重複)。
117
+ **Title slot heading level**:Empty 不渲染 heading tag(`<h1>` / `<h2>`),只 `<span>` + 視覺 typography。Consumer 若需 heading semantic,在外層自包 `<h2>{title}</h2>` + 不傳 Empty 的 title prop(避免重複)。
117
118
 
118
119
  **Icon slot**:LucideIcon 自動渲染 `aria-hidden="true"`(decorative);ReactElement(自帶 Avatar / Illustration)由 consumer 控制 `aria-hidden` / `alt`(若是 `<img>` 必傳 `alt=""` 表示 decorative)。
119
120
 
@@ -210,7 +211,6 @@ Empty 是 **pure layout primitive**(排列 icon / title / description / action
210
211
  - `file-upload.spec.md`
211
212
  - `file-viewer.spec.md`
212
213
  - `select-menu.spec.md`
213
- - `select.spec.md`
214
214
  - `sheet.spec.md`
215
215
  - `skeleton.spec.md`
216
216
  - `tree-view.spec.md`
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react'
2
- import { SearchX, WifiOff, Lock, Inbox } from 'lucide-react'
2
+ import { SearchX, Lock, Inbox } from 'lucide-react'
3
3
  import { Empty } from './empty'
4
4
  import { Button } from '@/design-system/components/Button/button'
5
5
 
@@ -49,20 +49,9 @@ export const NoTasks: Story = {
49
49
  ),
50
50
  }
51
51
 
52
- /* ── 錯誤無法載入(action-oriented)────────────────────────────────── */
53
- export const LoadFailure: Story = {
54
- name: '錯誤無法載入',
55
- render: () => (
56
- <div className="border border-border rounded-lg p-8 max-w-md">
57
- <Empty
58
- icon={WifiOff}
59
- title="連線中斷,無法載入資料"
60
- description="請檢查網路連線後重試"
61
- action={<Button variant="secondary">重新載入</Button>}
62
- />
63
- </div>
64
- ),
65
- }
52
+ // Retired 2026-07-04 audit(Q 拍板):原 LoadFailure story 正面示範 spec 明文禁止的 error-state
53
+ // 用法(empty.spec.md「何時不用」+ 禁止事項「錯誤/失敗狀態 改用 Alert」)— showcase 不得把
54
+ // 反例當正例教。錯誤示範保留在 principles 的 ❌ 反例(empty.principles.stories.tsx)。
66
55
 
67
56
  /* ── 權限不足 ─────────────────────────────────────────────────────── */
68
57
  export const NoPermission: Story = {
@@ -50,7 +50,10 @@ const Empty = React.forwardRef<HTMLDivElement, EmptyProps>(
50
50
  let iconElement: React.ReactNode = null
51
51
  if (icon) {
52
52
  if (React.isValidElement(icon)) {
53
- iconElement = icon
53
+ // disabled:自訂 element 同樣變淡(2026-07-07 補漏 — docblock/spec 無條件宣稱「icon glyph →
54
+ // fg-disabled」但原僅 LucideIcon 分支生效)。`contents` wrapper 不進 layout,[&_svg] token swap
55
+ // 只染 svg 系自訂 icon;非 svg(img 照片)不強制染色。
56
+ iconElement = disabled ? <span className="contents [&_svg]:!text-fg-disabled">{icon}</span> : icon
54
57
  } else {
55
58
  const Icon = icon as LucideIcon
56
59
  // disabled:glyph → fg-disabled([&_svg]:!… 蓋過 Avatar 內聯 color;circle bg 維持 muted)。icon 是文字一環,隨 disabled 變淡。
@@ -267,7 +267,7 @@ tagPadding 只在有 Tag 時才套用。Placeholder/空值狀態使用 fieldWrap
267
267
 
268
268
  Field 內部用 `<ItemInlineAction>`(`patterns/element-anatomy/item-anatomy.tsx` 共用元件)渲染 — 跟 Sidebar / TreeView / DropdownMenu 的 inline action **完全同一套** canonical 實作,不再有「每個 host 自己複製 18 行 button JSX」的漂移。
269
269
 
270
- - 共用規則見 `patterns/element-anatomy/item-anatomy.spec.md`「Inline Action 設計規格」節
270
+ - 共用規則見 `patterns/element-anatomy/inline-action.spec.md`(canonical SSOT,2026-04-24 自 item-anatomy 抽出;Field endAction 屬 neutral host 支)
271
271
  - Helper 規格與 API 見 `item-anatomy.spec.md` 的「Inline action 共用元件」節
272
272
  - Field host 必須傳 `size={size}` 給 `<ItemInlineAction>`(field 不在 `RowSizeContext` 內,需明確覆寫)
273
273
 
@@ -380,3 +380,4 @@ col.accessor('status', {
380
380
  - `switch.spec.md`
381
381
  - `textarea.spec.md`
382
382
  - `time-picker.spec.md`
383
+ - `uiSize.spec.md`
@@ -61,6 +61,16 @@ export const fieldWrapperStyles = cva(
61
61
  md: 'text-body h-field-md px-[var(--field-px)] gap-2',
62
62
  lg: 'text-body-lg h-field-lg px-[var(--field-px)] gap-2',
63
63
  },
64
+ // error(2026-07-04 Q1 拍板,SSOT 集中):原散在各控件的 error border classes 因
65
+ // focus-within:!border-primary(!important)被蓋 = dead code(聚焦永遠藍)。
66
+ // Benchmark 實查證偽舊宣稱:MUI(紅框加粗)/ Ant(紅框紅 glow)/ Polaris(紅 border
67
+ // + 獨立藍 focus ring)聚焦時 error 紅皆保留;唯一切藍的 Carbon 靠 in-field 紅 icon 補償。
68
+ // DS 邊框是唯一 error 載體 → error 勝 focus 的「顏色」通道。錯誤清除(edit-clears-error,
69
+ // useFormValidation)後自然回 focus 藍。
70
+ error: {
71
+ true: '',
72
+ false: '',
73
+ },
64
74
  },
65
75
  // mode x variant 交叉:visual chrome 由 compoundVariants 決定
66
76
  //
@@ -90,10 +100,13 @@ export const fieldWrapperStyles = cva(
90
100
  // 副作用 — Ant 風「選後藍 / 取消灰」自動達成:
91
101
  // - 選 option close popover → Radix focus return to trigger → focus-within fires → 藍
92
102
  // - 點外取消 close popover → focus 移外 → focus-within 不 fire → 灰
93
- 'focus-within:!border-primary focus-within:hover:!border-primary',
103
+ //
104
+ // 2026-07-04 Q1 拍板修訂:「focus dominates everything」對 error 讓位(見 error variant
105
+ // 註解)— focus 藍只在 error:false compound(下方),error:true 走 error compound 紅。
94
106
  'data-[state=open]:border-border-hover',
95
107
  ],
96
108
  },
109
+ { mode: 'edit', variant: 'default', error: false, className: 'focus-within:!border-primary focus-within:hover:!border-primary' },
97
110
  {
98
111
  mode: 'display',
99
112
  variant: 'default',
@@ -125,10 +138,17 @@ export const fieldWrapperStyles = cva(
125
138
  'bg-transparent border border-transparent',
126
139
  'hover:border-border',
127
140
  // 同 default chrome v13.3 SSOT:focus-within !important 強制勝過 data-state
128
- 'focus-within:!border-primary focus-within:hover:!border-primary',
141
+ //(2026-07-04 Q1:同 default,focus 藍移到 error:false compound)
129
142
  'data-[state=open]:border-border',
130
143
  ],
131
144
  },
145
+ { mode: 'edit', variant: 'bare', error: false, className: 'focus-within:!border-primary focus-within:hover:!border-primary' },
146
+ // error chrome(mode=edit 限定,variant 不分 — naked cell 內 error 同樣紅框,保留既有控件層行為):
147
+ {
148
+ mode: 'edit',
149
+ error: true,
150
+ className: 'border-error hover:border-error-hover focus-within:!border-error focus-within:hover:!border-error',
151
+ },
132
152
  {
133
153
  mode: 'display',
134
154
  variant: 'bare',
@@ -173,12 +193,13 @@ export const fieldWrapperStyles = cva(
173
193
  '!px-[var(--table-cell-px)] !py-[var(--table-cell-py)]',
174
194
  'border border-border',
175
195
  'hover:border-border-hover',
176
- // v13.3 SSOT canonical:focus-within !important(同 default + bare)
177
- 'focus-within:!border-primary focus-within:hover:!border-primary',
196
+ // v13.3 SSOT canonical:focus-within !important(同 default + bare;
197
+ // 2026-07-04 Q1:focus 藍移到 error:false compound,error:true 走共用 error compound)
178
198
  'data-[state=open]:border-border-hover',
179
199
  'group-data-[row-mode=auto]/cell:!items-start',
180
200
  ],
181
201
  },
202
+ { mode: 'edit', variant: 'naked', error: false, className: 'focus-within:!border-primary focus-within:hover:!border-primary' },
182
203
  {
183
204
  // 2026-05-12 fix v2(M32 root invariant audit):
184
205
  // Q1 root invariant?:cell-as-input display 視覺位置 = `cell.items-{X}` × `Field.height`
@@ -215,8 +236,6 @@ export const fieldWrapperStyles = cva(
215
236
  // 移除 `opacity-disabled` blanket — naked wrapper 只負責 substrate(透明 border + 抑制 cursor),
216
237
  // 內部 content(text / icon / avatar)各自 disabled context 處理(text-fg-disabled / Avatar opacity 等具體 token)。
217
238
  // 對齊 DataTable cell-disabled TD 加 `bg-disabled` 表達 cell-level disabled state 的 SSOT 分權。
218
- // **default/bare disabled variant(line 107, 135)deferred R3.5** — 仍依賴 wrapper opacity-disabled
219
- // for Avatar dim(Avatar 尚未 fieldCtx-aware self-dim);Avatar self-dim 實作後再連帶移除。
220
239
  mode: 'disabled',
221
240
  variant: 'naked',
222
241
  className: [
@@ -229,6 +248,7 @@ export const fieldWrapperStyles = cva(
229
248
  mode: 'edit',
230
249
  variant: 'default',
231
250
  size: 'md',
251
+ error: false,
232
252
  },
233
253
  }
234
254
  )
@@ -274,8 +294,8 @@ export const bareInputStyles = [
274
294
  // - Material X-Grid `gridClasses.cell` wrapper 不允許 cell content override alignment
275
295
  // - Notion / Airtable cell content 從 host 繼承,不 hardcode self alignment
276
296
  //
277
- // Hook:`check_naked_row_mode_propagation.sh`(write-time BLOCKER)
278
- // Audit:design-system-audit Group N M27(periodic batch verify)
297
+ // Hook:`check_field_family_invariants.sh` A.1(原 check_naked_row_mode_propagation.sh 已 folded,write-time BLOCKER)
298
+ // Audit:design-system-audit Group N(periodic batch verify)
279
299
  export const nakedCellRowModeAlign = 'group-data-[row-mode=auto]/cell:items-start'
280
300
 
281
301
  // ── Cell-as-input Display Hover Ring(2026-05-05 v9 — sole remaining ring const)─
@@ -151,8 +151,8 @@ export const Inspector: Story = {
151
151
  options: ['edit', 'display', 'readonly', 'disabled'],
152
152
  description: 'Context 傳給控制元件:edit 可編輯 / display 純展示 / readonly 鎖定但保留輸入外觀 / disabled 停用',
153
153
  },
154
- required: { control: 'boolean', description: 'label 後加 * + aria-required' },
155
- invalid: { control: 'boolean', description: '觸發 error border + FieldError 取代 FieldDescription' },
154
+ required: { control: 'boolean', description: 'label 文字前加 * + aria-required' },
155
+ invalid: { control: 'boolean', description: '觸發 error border + aria-invalid;本例以 FieldError 條件替換 FieldDescription(consumer 慣例,非元件行為)' },
156
156
  disabled: { control: 'boolean', description: 'Field disabled context 傳給 control' },
157
157
  labelWidth: {
158
158
  control: 'select',
@@ -264,7 +264,7 @@ export const SizeMatrix: Story = {
264
264
  <div className="flex flex-col gap-10">
265
265
  <div>
266
266
  <H3>三種 Size — 對齊 field-height 系統</H3>
267
- <Desc>Field 的 size 透過 context 傳遞給 input-class control(Input / NumberInput / Select / Combobox / DatePicker / TimePicker / PeoplePicker 等,皆經 useResolvedFieldSize 解析),控制其內部高度對齊 `--field-height-*` tier(見 uiSize.spec.md)。Slider / Switch / Checkbox 等 primitive 維持原生尺寸不讀 context size( field.spec.md「為什麼 primitive 不自己變高」),其行高節奏改由 Field control-area 的 min-h-field-{'{size}'} 提供——同一 Field 內所有 control 因此自動對齊高度。</Desc>
267
+ <Desc>Field 的 size 透過 context 傳遞給 input-class control(Input / NumberInput / Select / Combobox / DatePicker / TimePicker / PeoplePicker 等,皆經 useResolvedFieldSize 解析),控制其內部高度對齊 `--field-height-*` tier(見 uiSize.spec.md)。Slider / Switch / Checkbox 等 primitive edit 態維持原生尺寸、不隨 Field size 變高(Switch / Checkbox 仍經 useResolvedFieldSize 解析 size,僅用於 readonly 灰框 chrome;Slider 完全不讀 context size。見 field.spec.md「為什麼 primitive 不自己變高」),其行高節奏改由 Field control-area 的 min-h-field-{'{size}'} 提供——同一 Field 內所有 control 因此自動對齊高度。</Desc>
268
268
  <div className="grid grid-cols-3 gap-6">
269
269
  {(['sm', 'md', 'lg'] as const).map(size => (
270
270
  <div key={size} className="border border-dashed border-divider rounded-md p-4">
@@ -300,7 +300,7 @@ export const StateBehavior: Story = {
300
300
  render: () => (
301
301
  <div className="flex flex-col gap-10">
302
302
  <div>
303
- <H3>Required — label 後加 * 星號</H3>
303
+ <H3>Required — label 前加 * 星號</H3>
304
304
  <Desc>Field 的 required 透過 context 傳遞,FieldLabel 自動在文字前加上星號(僅視覺,對讀屏隱藏);aria-required 由內部的輸入控件(如 Input)負責設定。</Desc>
305
305
  <div className="border border-dashed border-divider rounded-md p-4 max-w-md">
306
306
  <Field required>
@@ -313,7 +313,7 @@ export const StateBehavior: Story = {
313
313
 
314
314
  <div>
315
315
  <H3>Invalid — 觸發 error 視覺 + FieldError 顯示</H3>
316
- <Desc>Field invalid 傳 context(觸發 aria-invalid + error border),FieldError 取代 FieldDescription 顯示。</Desc>
316
+ <Desc>Field invalid 傳 context(觸發 aria-invalid + error border),FieldError 顯示錯誤訊息(若與 FieldDescription 並存則兩者並列渲染;條件替換屬 consumer 慣例,非元件行為)。</Desc>
317
317
  <div className="border border-dashed border-divider rounded-md p-4 max-w-md">
318
318
  <Field invalid>
319
319
  <FieldLabel>Email</FieldLabel>
@@ -406,7 +406,7 @@ export const ColorMatrix: Story = {
406
406
  <tr>
407
407
  <Td mono>FieldDescription</Td>
408
408
  <Td><TokenCell token="--fg-secondary" display="fg-secondary" /></Td>
409
- <Td>—(隱藏,被 FieldError 取代)</Td>
409
+ <Td>fg-secondary(不變;元件不自動隱藏——本例以 FieldError 條件替換屬 consumer 慣例)</Td>
410
410
  <Td><TokenCell token="--fg-disabled" display="fg-disabled" /></Td>
411
411
  </tr>
412
412
  <tr>
@@ -453,7 +453,7 @@ export const ColorMatrix: Story = {
453
453
  <Field required>
454
454
  <FieldLabel>姓名</FieldLabel>
455
455
  <Input placeholder="請輸入姓名" />
456
- <FieldDescription>本欄位為必填(label 後有星號 *)</FieldDescription>
456
+ <FieldDescription>本欄位為必填(label 前有星號 *)</FieldDescription>
457
457
  </Field>
458
458
  </div>
459
459
  <div>
@@ -34,7 +34,7 @@ const Label = ({ children, warn }: { children: React.ReactNode; warn?: boolean }
34
34
 
35
35
  const Section = ({ title, children }: { title: string; children: React.ReactNode }) => (
36
36
  <section className="mb-12">
37
- <h2 className="text-heading-3 font-bold text-foreground mb-4 pb-2 border-b border-border">{title}</h2>
37
+ <h2 className="text-h3 font-bold text-foreground mb-4 pb-2 border-b border-border">{title}</h2>
38
38
  {children}
39
39
  </section>
40
40
  )