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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (481) hide show
  1. package/AGENTS.md +210 -0
  2. package/CLAUDE.md +17 -191
  3. package/cli-init.mjs +30 -2
  4. package/dist/components/Accordion/accordion.d.ts +2 -2
  5. package/dist/components/Accordion/accordion.d.ts.map +1 -1
  6. package/dist/components/Accordion/accordion.js.map +1 -1
  7. package/dist/components/Avatar/avatar.js +1 -1
  8. package/dist/components/Avatar/avatar.js.map +1 -1
  9. package/dist/components/BulkActionBar/bulk-action-bar.d.ts +1 -1
  10. package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
  11. package/dist/components/BulkActionBar/bulk-action-bar.js +3 -3
  12. package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
  13. package/dist/components/Button/button-group.d.ts +2 -2
  14. package/dist/components/Button/button-group.d.ts.map +1 -1
  15. package/dist/components/Button/button-group.js +2 -2
  16. package/dist/components/Button/button-group.js.map +1 -1
  17. package/dist/components/Button/button.d.ts +2 -1
  18. package/dist/components/Button/button.d.ts.map +1 -1
  19. package/dist/components/Button/button.js +1 -1
  20. package/dist/components/Button/button.js.map +1 -1
  21. package/dist/components/Carousel/carousel.d.ts +14 -8
  22. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  23. package/dist/components/Carousel/carousel.js +64 -37
  24. package/dist/components/Carousel/carousel.js.map +1 -1
  25. package/dist/components/Checkbox/checkbox-group.d.ts.map +1 -1
  26. package/dist/components/Checkbox/checkbox-group.js +21 -14
  27. package/dist/components/Checkbox/checkbox-group.js.map +1 -1
  28. package/dist/components/Checkbox/checkbox.d.ts +2 -2
  29. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  30. package/dist/components/Checkbox/checkbox.js +49 -10
  31. package/dist/components/Checkbox/checkbox.js.map +1 -1
  32. package/dist/components/Combobox/combobox.d.ts +15 -6
  33. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  34. package/dist/components/Combobox/combobox.js +13 -7
  35. package/dist/components/Combobox/combobox.js.map +1 -1
  36. package/dist/components/Command/command.d.ts.map +1 -1
  37. package/dist/components/Command/command.js +11 -2
  38. package/dist/components/Command/command.js.map +1 -1
  39. package/dist/components/DataTable/cell-registry.d.ts +5 -5
  40. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  41. package/dist/components/DataTable/cell-registry.js +41 -38
  42. package/dist/components/DataTable/cell-registry.js.map +1 -1
  43. package/dist/components/DataTable/column-types.d.ts +1 -17
  44. package/dist/components/DataTable/column-types.d.ts.map +1 -1
  45. package/dist/components/DataTable/column-types.js.map +1 -1
  46. package/dist/components/DataTable/data-table-column-visibility-panel.d.ts.map +1 -1
  47. package/dist/components/DataTable/data-table-column-visibility-panel.js +71 -64
  48. package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -1
  49. package/dist/components/DataTable/data-table-filter-panel.d.ts +0 -2
  50. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  51. package/dist/components/DataTable/data-table-filter-panel.js +3 -4
  52. package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
  53. package/dist/components/DataTable/data-table-interaction-layer.d.ts +2 -2
  54. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  55. package/dist/components/DataTable/data-table-sort-manager.js +2 -2
  56. package/dist/components/DataTable/data-table-sort-manager.js.map +1 -1
  57. package/dist/components/DataTable/data-table.d.ts +1 -1
  58. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  59. package/dist/components/DataTable/data-table.js +44 -32
  60. package/dist/components/DataTable/data-table.js.map +1 -1
  61. package/dist/components/DatePicker/date-picker.d.ts +3 -3
  62. package/dist/components/DatePicker/date-picker.js +4 -4
  63. package/dist/components/DatePicker/date-picker.js.map +1 -1
  64. package/dist/components/DescriptionList/description-list.d.ts +3 -3
  65. package/dist/components/DescriptionList/description-list.d.ts.map +1 -1
  66. package/dist/components/DescriptionList/description-list.js +6 -6
  67. package/dist/components/DescriptionList/description-list.js.map +1 -1
  68. package/dist/components/Dialog/dialog.d.ts +2 -2
  69. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  70. package/dist/components/Dialog/dialog.js.map +1 -1
  71. package/dist/components/DropdownMenu/dropdown-menu.d.ts +8 -3
  72. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  73. package/dist/components/DropdownMenu/dropdown-menu.js +7 -7
  74. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  75. package/dist/components/Field/field-context.d.ts +16 -10
  76. package/dist/components/Field/field-context.d.ts.map +1 -1
  77. package/dist/components/Field/field-context.js.map +1 -1
  78. package/dist/components/Field/field-types.d.ts +2 -2
  79. package/dist/components/Field/field-types.d.ts.map +1 -1
  80. package/dist/components/Field/field-types.js.map +1 -1
  81. package/dist/components/Field/field-wrapper.d.ts +20 -4
  82. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  83. package/dist/components/Field/field-wrapper.js +30 -31
  84. package/dist/components/Field/field-wrapper.js.map +1 -1
  85. package/dist/components/Field/field.d.ts +1 -1
  86. package/dist/components/Field/field.d.ts.map +1 -1
  87. package/dist/components/Field/field.js +25 -3
  88. package/dist/components/Field/field.js.map +1 -1
  89. package/dist/components/Field/use-form-validation.d.ts +1 -1
  90. package/dist/components/Field/use-form-validation.js.map +1 -1
  91. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  92. package/dist/components/FileItem/file-item.js +2 -5
  93. package/dist/components/FileItem/file-item.js.map +1 -1
  94. package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
  95. package/dist/components/FileUpload/file-upload.js +5 -1
  96. package/dist/components/FileUpload/file-upload.js.map +1 -1
  97. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  98. package/dist/components/FileViewer/file-viewer.js +2 -1
  99. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  100. package/dist/components/HoverCard/hover-card.d.ts +2 -2
  101. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  102. package/dist/components/HoverCard/hover-card.js +4 -2
  103. package/dist/components/HoverCard/hover-card.js.map +1 -1
  104. package/dist/components/InlineEdit/inline-edit.d.ts +23 -16
  105. package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -1
  106. package/dist/components/InlineEdit/inline-edit.js +104 -62
  107. package/dist/components/InlineEdit/inline-edit.js.map +1 -1
  108. package/dist/components/Input/input.d.ts +1 -1
  109. package/dist/components/Input/input.d.ts.map +1 -1
  110. package/dist/components/Input/input.js +6 -6
  111. package/dist/components/Input/input.js.map +1 -1
  112. package/dist/components/LinkInput/link-input.d.ts +5 -5
  113. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  114. package/dist/components/LinkInput/link-input.js +3 -3
  115. package/dist/components/LinkInput/link-input.js.map +1 -1
  116. package/dist/components/Notice/notice.d.ts +17 -1
  117. package/dist/components/Notice/notice.d.ts.map +1 -1
  118. package/dist/components/Notice/notice.js +7 -1
  119. package/dist/components/Notice/notice.js.map +1 -1
  120. package/dist/components/NumberInput/number-input.d.ts +1 -1
  121. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  122. package/dist/components/NumberInput/number-input.js +12 -1
  123. package/dist/components/NumberInput/number-input.js.map +1 -1
  124. package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts +1 -1
  125. package/dist/components/PeoplePicker/avatar-stack-overflow.js.map +1 -1
  126. package/dist/components/PeoplePicker/people-picker.d.ts +5 -5
  127. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  128. package/dist/components/PeoplePicker/people-picker.js +7 -5
  129. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  130. package/dist/components/PeoplePicker/person-display.d.ts +1 -1
  131. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  132. package/dist/components/ProfileCard/profile-card.d.ts +1 -1
  133. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  134. package/dist/components/RadioGroup/radio-group.d.ts +3 -3
  135. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  136. package/dist/components/RadioGroup/radio-group.js +1 -1
  137. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  138. package/dist/components/Rating/rating.d.ts.map +1 -1
  139. package/dist/components/Rating/rating.js +4 -0
  140. package/dist/components/Rating/rating.js.map +1 -1
  141. package/dist/components/ScrollArea/scroll-area.d.ts +3 -2
  142. package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
  143. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  144. package/dist/components/SegmentedControl/segmented-control.d.ts +4 -3
  145. package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
  146. package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
  147. package/dist/components/Select/select.d.ts +16 -6
  148. package/dist/components/Select/select.d.ts.map +1 -1
  149. package/dist/components/Select/select.js +17 -5
  150. package/dist/components/Select/select.js.map +1 -1
  151. package/dist/components/SelectMenu/select-menu.d.ts +17 -1
  152. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  153. package/dist/components/SelectMenu/select-menu.js +17 -3
  154. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  155. package/dist/components/SelectionControl/boolean-value.d.ts +1 -1
  156. package/dist/components/SelectionControl/boolean-value.js.map +1 -1
  157. package/dist/components/Sheet/sheet.d.ts +2 -2
  158. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  159. package/dist/components/Sheet/sheet.js.map +1 -1
  160. package/dist/components/Sidebar/sidebar.d.ts +1 -1
  161. package/dist/components/Sidebar/sidebar.js +2 -2
  162. package/dist/components/Sidebar/sidebar.js.map +1 -1
  163. package/dist/components/Slider/slider.d.ts.map +1 -1
  164. package/dist/components/Slider/slider.js +6 -3
  165. package/dist/components/Slider/slider.js.map +1 -1
  166. package/dist/components/Steps/steps.d.ts +6 -4
  167. package/dist/components/Steps/steps.d.ts.map +1 -1
  168. package/dist/components/Steps/steps.js +19 -5
  169. package/dist/components/Steps/steps.js.map +1 -1
  170. package/dist/components/Switch/switch.d.ts +4 -4
  171. package/dist/components/Switch/switch.d.ts.map +1 -1
  172. package/dist/components/Switch/switch.js +3 -1
  173. package/dist/components/Switch/switch.js.map +1 -1
  174. package/dist/components/Tabs/tabs.d.ts +1 -1
  175. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  176. package/dist/components/Tabs/tabs.js +73 -23
  177. package/dist/components/Tabs/tabs.js.map +1 -1
  178. package/dist/components/Tag/tag.d.ts +3 -1
  179. package/dist/components/Tag/tag.d.ts.map +1 -1
  180. package/dist/components/Tag/tag.js +2 -2
  181. package/dist/components/Tag/tag.js.map +1 -1
  182. package/dist/components/Textarea/textarea.d.ts +4 -4
  183. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  184. package/dist/components/Textarea/textarea.js +15 -15
  185. package/dist/components/Textarea/textarea.js.map +1 -1
  186. package/dist/components/TimePicker/time-picker.d.ts +4 -4
  187. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  188. package/dist/components/TimePicker/time-picker.js +8 -5
  189. package/dist/components/TimePicker/time-picker.js.map +1 -1
  190. package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
  191. package/dist/components/Tooltip/tooltip.js +27 -23
  192. package/dist/components/Tooltip/tooltip.js.map +1 -1
  193. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  194. package/dist/components/TreeView/tree-view.js +16 -0
  195. package/dist/components/TreeView/tree-view.js.map +1 -1
  196. package/dist/index.d.ts +7 -7
  197. package/dist/index.d.ts.map +1 -1
  198. package/dist/index.js +6 -13
  199. package/dist/index.js.map +1 -1
  200. package/dist/lib/drag-visual.d.ts +2 -2
  201. package/dist/lib/drag-visual.js.map +1 -1
  202. package/dist/lib/utils.d.ts +1 -0
  203. package/dist/lib/utils.d.ts.map +1 -1
  204. package/dist/lib/utils.js +21 -1
  205. package/dist/lib/utils.js.map +1 -1
  206. package/dist/patterns/element-anatomy/item-anatomy.d.ts +2 -0
  207. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  208. package/dist/patterns/element-anatomy/item-anatomy.js +2 -2
  209. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  210. package/dist/tokens/categorical-color.d.ts +3 -3
  211. package/dist/tokens/categorical-color.d.ts.map +1 -1
  212. package/dist/tokens/categorical-color.js +2 -2
  213. package/dist/tokens/categorical-color.js.map +1 -1
  214. package/ds-canonical/fork/AGENTS.md +213 -0
  215. package/ds-canonical/fork/codex/agents/skills/independent-review/SKILL.md +33 -0
  216. package/ds-canonical/fork/codex/hooks.json +16 -0
  217. package/ds-canonical/fork/governance.lock +22 -7
  218. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +22 -1
  219. package/ds-canonical/fork/hooks/inject_deploy_url_after_push.sh +14 -2
  220. package/ds-canonical/fork/manifest.json +10 -1
  221. package/ds-canonical/fork/preamble.md +3 -2
  222. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +7 -7
  223. package/ds-canonical/fork/skills/ux-audit/SKILL.md +1 -1
  224. package/ds-canonical/fork/skills/visual-audit/SKILL.md +0 -1
  225. package/ds-canonical/hooks/check_codex_brief_invariants.sh +9 -3
  226. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +22 -1
  227. package/ds-canonical/hooks/check_overlay_open_focus_escape_probe.sh +19 -2
  228. package/ds-canonical/hooks/check_plugin_fork_health.sh +17 -1
  229. package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +21 -4
  230. package/ds-canonical/hooks/inject_deploy_url_after_push.sh +14 -2
  231. package/ds-canonical/hooks/session_start_governance_check.sh +10 -2
  232. package/ds-canonical/references/certified-surfaces.md +20 -0
  233. package/ds-canonical/references/preflight-gate-baseline.json +5 -0
  234. package/ds-canonical/references/principle-dim-map.json +20 -2
  235. package/ds-canonical/references/props-naming.md +2 -1
  236. package/ds-canonical/references/ssot-index.md +2 -2
  237. package/ds-canonical/references/story-baseline-registry.json +2 -2
  238. package/ds-canonical/rules/story-rules.md +1 -1
  239. package/ds-canonical/rules/ui-development.md +2 -1
  240. package/ds-canonical/skills/codex-collab/SKILL.md +7 -7
  241. package/ds-canonical/skills/codex-collab/references/brief-template.md +5 -1
  242. package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
  243. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +5 -5
  244. package/ds-canonical/skills/design-system-audit/SKILL.md +5 -5
  245. package/ds-canonical/skills/product-ui-audit/SKILL.md +7 -7
  246. package/ds-canonical/skills/ux-audit/SKILL.md +1 -1
  247. package/ds-canonical/skills/visual-audit/SKILL.md +0 -1
  248. package/ds-story-manifest.json +21 -21
  249. package/llms-full.txt +3 -3
  250. package/llms.txt +1 -1
  251. package/package.json +2 -1
  252. package/src/components/Accordion/accordion.anatomy.stories.tsx +11 -11
  253. package/src/components/Accordion/accordion.principles.stories.tsx +21 -6
  254. package/src/components/Accordion/accordion.spec.md +10 -1
  255. package/src/components/Accordion/accordion.stories.tsx +10 -5
  256. package/src/components/Accordion/accordion.tsx +7 -2
  257. package/src/components/Alert/alert.principles.stories.tsx +2 -8
  258. package/src/components/AppShell/app-shell.anatomy.stories.tsx +19 -13
  259. package/src/components/AppShell/app-shell.principles.stories.tsx +21 -21
  260. package/src/components/AppShell/app-shell.spec.md +4 -0
  261. package/src/components/AppShell/app-shell.stories.tsx +4 -5
  262. package/src/components/Avatar/avatar.tsx +1 -1
  263. package/src/components/Badge/badge.spec.md +2 -2
  264. package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +7 -3
  265. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +4 -1
  266. package/src/components/Breadcrumb/breadcrumb.stories.tsx +1 -1
  267. package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +6 -6
  268. package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +18 -6
  269. package/src/components/BulkActionBar/bulk-action-bar.spec.md +15 -5
  270. package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +8 -24
  271. package/src/components/BulkActionBar/bulk-action-bar.tsx +4 -4
  272. package/src/components/Button/button-group.tsx +4 -4
  273. package/src/components/Button/button.anatomy.stories.tsx +26 -7
  274. package/src/components/Button/button.principles.stories.tsx +1 -1
  275. package/src/components/Button/button.spec.md +2 -2
  276. package/src/components/Button/button.stories.tsx +22 -28
  277. package/src/components/Button/button.tsx +2 -1
  278. package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
  279. package/src/components/Calendar/calendar.spec.md +1 -1
  280. package/src/components/Carousel/carousel.spec.md +17 -1
  281. package/src/components/Carousel/carousel.tsx +93 -42
  282. package/src/components/Checkbox/checkbox-group.tsx +26 -15
  283. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +3 -3
  284. package/src/components/Checkbox/checkbox.spec.md +12 -3
  285. package/src/components/Checkbox/checkbox.stories.tsx +4 -4
  286. package/src/components/Checkbox/checkbox.tsx +79 -19
  287. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +5 -5
  288. package/src/components/Coachmark/coachmark.principles.stories.tsx +1 -1
  289. package/src/components/Combobox/combobox.anatomy.stories.tsx +11 -4
  290. package/src/components/Combobox/combobox.principles.stories.tsx +10 -11
  291. package/src/components/Combobox/combobox.spec.md +6 -2
  292. package/src/components/Combobox/combobox.stories.tsx +9 -36
  293. package/src/components/Combobox/combobox.tsx +42 -27
  294. package/src/components/Command/command.spec.md +3 -3
  295. package/src/components/Command/command.tsx +9 -2
  296. package/src/components/DataTable/cell-registry.tsx +75 -76
  297. package/src/components/DataTable/column-types.ts +4 -17
  298. package/src/components/DataTable/data-table-column-visibility-panel.tsx +20 -7
  299. package/src/components/DataTable/data-table-filter-panel.tsx +6 -6
  300. package/src/components/DataTable/data-table-interaction-layer.tsx +3 -3
  301. package/src/components/DataTable/data-table-sort-manager.tsx +2 -2
  302. package/src/components/DataTable/data-table.anatomy.stories.tsx +25 -24
  303. package/src/components/DataTable/data-table.css +2 -3
  304. package/src/components/DataTable/data-table.principles.stories.tsx +17 -11
  305. package/src/components/DataTable/data-table.spec.md +8 -7
  306. package/src/components/DataTable/data-table.stories.tsx +39 -29
  307. package/src/components/DataTable/data-table.tsx +90 -67
  308. package/src/components/DataTable/filter-operators.spec.md +2 -0
  309. package/src/components/DateGrid/date-grid.spec.md +1 -1
  310. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +9 -9
  311. package/src/components/DatePicker/date-picker.principles.stories.tsx +4 -4
  312. package/src/components/DatePicker/date-picker.spec.md +10 -9
  313. package/src/components/DatePicker/date-picker.stories.tsx +9 -9
  314. package/src/components/DatePicker/date-picker.tsx +13 -13
  315. package/src/components/DescriptionList/description-list.anatomy.stories.tsx +5 -5
  316. package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
  317. package/src/components/DescriptionList/description-list.spec.md +12 -12
  318. package/src/components/DescriptionList/description-list.stories.tsx +3 -3
  319. package/src/components/DescriptionList/description-list.tsx +10 -10
  320. package/src/components/Dialog/dialog.tsx +4 -1
  321. package/src/components/DropdownMenu/dropdown-menu.tsx +27 -14
  322. package/src/components/Empty/empty.spec.md +0 -1
  323. package/src/components/Field/field-context.ts +17 -11
  324. package/src/components/Field/field-controls.spec.md +46 -37
  325. package/src/components/Field/field-types.ts +14 -12
  326. package/src/components/Field/field-wrapper.tsx +59 -43
  327. package/src/components/Field/field.anatomy.stories.tsx +6 -6
  328. package/src/components/Field/field.principles.stories.tsx +5 -28
  329. package/src/components/Field/field.spec.md +5 -4
  330. package/src/components/Field/field.stories.tsx +103 -72
  331. package/src/components/Field/field.tsx +36 -4
  332. package/src/components/Field/form-validation.spec.md +4 -0
  333. package/src/components/Field/use-form-validation.ts +1 -1
  334. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
  335. package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
  336. package/src/components/FileItem/file-item.tsx +10 -8
  337. package/src/components/FileUpload/file-upload.principles.stories.tsx +7 -7
  338. package/src/components/FileUpload/file-upload.spec.md +3 -2
  339. package/src/components/FileUpload/file-upload.tsx +5 -1
  340. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
  341. package/src/components/FileViewer/file-viewer.principles.stories.tsx +1 -1
  342. package/src/components/FileViewer/file-viewer.spec.md +1 -1
  343. package/src/components/FileViewer/file-viewer.stories.tsx +1 -1
  344. package/src/components/FileViewer/file-viewer.tsx +3 -2
  345. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -4
  346. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -1
  347. package/src/components/HoverCard/hover-card.spec.md +2 -2
  348. package/src/components/HoverCard/hover-card.tsx +4 -2
  349. package/src/components/InlineEdit/inline-edit.anatomy.stories.tsx +184 -0
  350. package/src/components/InlineEdit/inline-edit.principles.stories.tsx +142 -0
  351. package/src/components/InlineEdit/inline-edit.spec.md +71 -46
  352. package/src/components/InlineEdit/inline-edit.stories.tsx +20 -19
  353. package/src/components/InlineEdit/inline-edit.tsx +211 -125
  354. package/src/components/Input/input.anatomy.stories.tsx +15 -15
  355. package/src/components/Input/input.principles.stories.tsx +3 -3
  356. package/src/components/Input/input.spec.md +4 -4
  357. package/src/components/Input/input.stories.tsx +3 -3
  358. package/src/components/Input/input.tsx +13 -13
  359. package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -2
  360. package/src/components/LinkInput/link-input.principles.stories.tsx +1 -1
  361. package/src/components/LinkInput/link-input.spec.md +6 -2
  362. package/src/components/LinkInput/link-input.stories.tsx +8 -8
  363. package/src/components/LinkInput/link-input.tsx +21 -19
  364. package/src/components/Notice/notice.principles.stories.tsx +1 -1
  365. package/src/components/Notice/notice.tsx +8 -2
  366. package/src/components/NumberInput/number-input.anatomy.stories.tsx +6 -6
  367. package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
  368. package/src/components/NumberInput/number-input.spec.md +6 -6
  369. package/src/components/NumberInput/number-input.stories.tsx +4 -4
  370. package/src/components/NumberInput/number-input.tsx +17 -5
  371. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +8 -4
  372. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +6 -7
  373. package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
  374. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -30
  375. package/src/components/PeoplePicker/avatar-stack-overflow.ts +1 -1
  376. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +13 -10
  377. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +5 -1
  378. package/src/components/PeoplePicker/people-picker.stories.tsx +5 -5
  379. package/src/components/PeoplePicker/people-picker.tsx +39 -20
  380. package/src/components/PeoplePicker/person-display.tsx +3 -3
  381. package/src/components/Popover/popover.anatomy.stories.tsx +10 -7
  382. package/src/components/Popover/popover.principles.stories.tsx +49 -28
  383. package/src/components/Popover/popover.spec.md +4 -2
  384. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
  385. package/src/components/ProfileCard/profile-card.tsx +1 -1
  386. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +14 -13
  387. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +8 -23
  388. package/src/components/ProgressBar/progress-bar.stories.tsx +8 -2
  389. package/src/components/README.md +1 -0
  390. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +8 -3
  391. package/src/components/RadioGroup/radio-group.stories.tsx +3 -3
  392. package/src/components/RadioGroup/radio-group.tsx +19 -14
  393. package/src/components/Rating/rating.anatomy.stories.tsx +1 -1
  394. package/src/components/Rating/rating.principles.stories.tsx +3 -3
  395. package/src/components/Rating/rating.spec.md +2 -2
  396. package/src/components/Rating/rating.stories.tsx +4 -18
  397. package/src/components/Rating/rating.tsx +5 -1
  398. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
  399. package/src/components/ScrollArea/scroll-area.spec.md +1 -1
  400. package/src/components/ScrollArea/scroll-area.tsx +5 -2
  401. package/src/components/SegmentedControl/segmented-control.principles.stories.tsx +4 -1
  402. package/src/components/SegmentedControl/segmented-control.spec.md +5 -10
  403. package/src/components/SegmentedControl/segmented-control.tsx +6 -3
  404. package/src/components/Select/select.anatomy.stories.tsx +13 -7
  405. package/src/components/Select/select.principles.stories.tsx +3 -3
  406. package/src/components/Select/select.spec.md +10 -1
  407. package/src/components/Select/select.stories.tsx +41 -27
  408. package/src/components/Select/select.tsx +44 -26
  409. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +44 -26
  410. package/src/components/SelectMenu/select-menu.principles.stories.tsx +0 -1
  411. package/src/components/SelectMenu/select-menu.stories.tsx +7 -19
  412. package/src/components/SelectMenu/select-menu.tsx +38 -5
  413. package/src/components/SelectionControl/boolean-value.tsx +2 -2
  414. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
  415. package/src/components/SelectionControl/selection-item.principles.stories.tsx +4 -4
  416. package/src/components/Separator/separator.principles.stories.tsx +1 -1
  417. package/src/components/Sheet/sheet.principles.stories.tsx +5 -3
  418. package/src/components/Sheet/sheet.tsx +4 -1
  419. package/src/components/Sidebar/sidebar.tsx +1 -1
  420. package/src/components/Slider/slider.anatomy.stories.tsx +5 -4
  421. package/src/components/Slider/slider.principles.stories.tsx +6 -6
  422. package/src/components/Slider/slider.spec.md +13 -1
  423. package/src/components/Slider/slider.stories.tsx +44 -32
  424. package/src/components/Slider/slider.tsx +13 -4
  425. package/src/components/Steps/steps.anatomy.stories.tsx +7 -7
  426. package/src/components/Steps/steps.principles.stories.tsx +1 -1
  427. package/src/components/Steps/steps.spec.md +4 -2
  428. package/src/components/Steps/steps.stories.tsx +4 -56
  429. package/src/components/Steps/steps.tsx +31 -7
  430. package/src/components/Switch/switch.anatomy.stories.tsx +3 -3
  431. package/src/components/Switch/switch.principles.stories.tsx +1 -1
  432. package/src/components/Switch/switch.spec.md +4 -4
  433. package/src/components/Switch/switch.stories.tsx +3 -3
  434. package/src/components/Switch/switch.tsx +16 -10
  435. package/src/components/Tabs/tabs.anatomy.stories.tsx +6 -0
  436. package/src/components/Tabs/tabs.principles.stories.tsx +10 -2
  437. package/src/components/Tabs/tabs.spec.md +5 -2
  438. package/src/components/Tabs/tabs.stories.tsx +36 -44
  439. package/src/components/Tabs/tabs.tsx +101 -36
  440. package/src/components/Tag/tag.anatomy.stories.tsx +2 -2
  441. package/src/components/Tag/tag.principles.stories.tsx +4 -4
  442. package/src/components/Tag/tag.spec.md +4 -1
  443. package/src/components/Tag/tag.tsx +5 -3
  444. package/src/components/Textarea/textarea.anatomy.stories.tsx +6 -6
  445. package/src/components/Textarea/textarea.spec.md +5 -5
  446. package/src/components/Textarea/textarea.tsx +24 -24
  447. package/src/components/TimePicker/time-picker.anatomy.stories.tsx +12 -11
  448. package/src/components/TimePicker/time-picker.principles.stories.tsx +37 -44
  449. package/src/components/TimePicker/time-picker.spec.md +6 -5
  450. package/src/components/TimePicker/time-picker.stories.tsx +3 -3
  451. package/src/components/TimePicker/time-picker.tsx +24 -14
  452. package/src/components/Toast/toast.principles.stories.tsx +1 -1
  453. package/src/components/Toast/toast.stories.tsx +1 -1
  454. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +15 -6
  455. package/src/components/Tooltip/tooltip.principles.stories.tsx +4 -4
  456. package/src/components/Tooltip/tooltip.spec.md +9 -3
  457. package/src/components/Tooltip/tooltip.stories.tsx +1 -1
  458. package/src/components/Tooltip/tooltip.tsx +12 -2
  459. package/src/components/TreeView/tree-view.anatomy.stories.tsx +16 -14
  460. package/src/components/TreeView/tree-view.principles.stories.tsx +44 -11
  461. package/src/components/TreeView/tree-view.spec.md +8 -6
  462. package/src/components/TreeView/tree-view.stories.tsx +9 -43
  463. package/src/components/TreeView/tree-view.tsx +36 -0
  464. package/src/index.ts +13 -10
  465. package/src/lib/drag-visual.ts +2 -2
  466. package/src/lib/utils.ts +19 -0
  467. package/src/patterns/action-bar/action-bar.spec.md +1 -0
  468. package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
  469. package/src/patterns/element-anatomy/item-anatomy.spec.md +4 -0
  470. package/src/patterns/element-anatomy/item-anatomy.tsx +4 -2
  471. package/src/patterns/header-canonical/header-canonical.spec.md +9 -0
  472. package/src/patterns/header-canonical/header-canonical.stories.tsx +31 -5
  473. package/src/tokens/categorical-color.ts +5 -5
  474. package/src/tokens/color/color.spec.md +4 -4
  475. package/src/tokens/color/primitives.css +9 -2
  476. package/src/tokens/motion/motion.spec.md +6 -7
  477. package/src/tokens/opacity/opacity.spec.md +1 -1
  478. package/src/tokens/orphan-tokens.spec.md +1 -1
  479. package/src/tokens/typography/typography.spec.md +2 -0
  480. package/src/tokens/uiSize/uiSize.css +5 -0
  481. package/src/tokens/uiSize/uiSize.spec.md +1 -1
@@ -3,9 +3,9 @@ component: TreeView
3
3
  family: 1
4
4
  variants: {}
5
5
  sizes:
6
- sm: { when: "緊湊清單(與 md 同視覺:text-body、indent 24;保留供密度語意)" }
6
+ sm: { when: "緊湊清單(與 md 同視覺,保留供密度語意)" }
7
7
  md: { when: "default 一般導覽樹" } # ★ default
8
- lg: { when: "touch / 大字級(text-body-lg、px-4 py-2、indent 28)" }
8
+ lg: { when: "touch / 大字級" }
9
9
  traits:
10
10
  - hasInteractiveStates
11
11
  - isStructural
@@ -41,7 +41,7 @@ TreeView 本身只負責三件事:
41
41
 
42
42
  - **階層結構資料**:檔案管理器資料夾樹、部門組織架構、專案 / 子專案 / 任務、權限群組
43
43
  - **Sidebar 內的分層導覽**:workspace > channel > thread、product > category > item
44
- - **可展開收合的清單**:FAQ(但多個可同時展開,非 Accordion 互斥)、程式碼 tree、JSON viewer
44
+ - **可展開收合的階層清單**:程式碼檔案樹、JSON viewer、DOM inspector(遞迴巢狀結構;平面章節如 FAQ / 設定分組屬 Accordion,見「何時不用」)
45
45
  - **任意多層**:從 1 層到 N 層深度都由同一個 TreeView 承載
46
46
 
47
47
  ## 何時不用
@@ -49,8 +49,8 @@ TreeView 本身只負責三件事:
49
49
  | 場景 | 改用 | 原因 |
50
50
  |------|------|------|
51
51
  | 平面資料(無階層)| `DataTable` / 自訂 list | TreeView 為階層而設計,沒 children 的平面用 list |
52
- | 同時只能展開一個(互斥)| `Accordion type="single"` | TreeView 允許任意多個展開,互斥模式語意不同 |
53
- | 簡單 2 nav(主分類 + 子分類)| `Sidebar` + SidebarMenuSubButton | 輕量 2 層用 Sidebar 的內建結構,不需遞迴 |
52
+ | 平面章節折疊(FAQ / 設定分組,無階層)| `Accordion`(`type="single"` 互斥 / `type="multiple"` 多開)| TreeView 是遞迴階層 node;平面章節不論單開或多開都屬 Accordion 範疇(accordion.spec.md「何時用」列 FAQ 為主要用途)|
53
+ | 純視覺分段 / 設計時可列舉的 1 層固定選單 | `SidebarGroup` / `SidebarMenu` | Sidebar 主選單只接受 1 層可列舉項目;任何真階層(深度 > 1、runtime 可新增、user data)一律 TreeView(見 sidebar.spec.md「判斷規則」)|
54
54
  | 選單式展開(點完就關)| `DropdownMenu` + sub-menu | DropdownMenu 的 sub menu 是 temporary,TreeView 是 persistent |
55
55
  | 階層選擇但需要搜尋 / 快速找 | 自訂 tree + search filter(未來 TreeView 可加 search prop)| 基本 TreeView 不含 search |
56
56
 
@@ -182,7 +182,7 @@ Chevron 是**展開/收合控件**,不是 prefix icon:`fg-muted`(指示色,hover
182
182
 
183
183
  | 元素 | Role | 屬性 |
184
184
  |---|---|---|
185
- | TreeView 容器 | `role="tree"` | `aria-label`,`aria-multiselectable`(多選時) |
185
+ | TreeView 容器 | `role="tree"` | **`aria-label` 或 `aria-labelledby`(擇一必填 — accessible name)**,`aria-multiselectable`(多選時) |
186
186
  | TreeItem 外層 | `role="treeitem"` | `aria-expanded`(expandable 才有),`aria-selected`,`aria-level` |
187
187
  | TreeItem children 容器 | `role="group"` | — |
188
188
 
@@ -393,6 +393,8 @@ TreeView 真實展示需要**多層巢狀結構**才有意義(單節點無法體
393
393
 
394
394
  **ARIA / Pattern**:自建 ARIA tree(非沿用 Radix 預設)。容器 `role="tree"`(多選時加 `aria-multiselectable`),每個 node `role="treeitem"` + `aria-expanded`(expandable 才有)/ `aria-selected` / `aria-level`,皆由元件手動設定(`tree-view.tsx` TreeItem render)。Radix `collapsible` 僅用於 children 展開 / 收合的高度動畫,**不提供** tree 的 role / aria / 鍵盤導覽(Radix 沒有 Tree primitive,見「定位」段)。對齊 [WAI-ARIA TreeView pattern](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/)。`aria-setsize` / `aria-posinset` **刻意省略**:APG 規定只有當節點未全數 render 進 DOM(lazy load)或 DOM 序 ≠ 閱讀序時才必需;本元件全可見節點皆在 DOM(`querySelectorAll('[role="treeitem"]:not([hidden])')`)且 DOM 序 = 閱讀序,故非必需。
395
395
 
396
+ **Accessible name(必填契約)**:`role="tree"` 的名稱無法從子節點內容推導,consumer **必須**提供 `aria-label`(直接字串)或 `aria-labelledby`(指向可見標題的 id)其中之一——有可見標題時優先 `aria-labelledby`,無則用 `aria-label`。兩者皆缺 → 螢幕閱讀器只讀出「tree」無法辨識用途(WAI-ARIA APG 要求 `role="tree"` 具 accessible name)。`tree-view.tsx` 於 `process.env.NODE_ENV !== 'production'` 時 `console.warn` 提示,避免上線後才發現無名稱樹(對齊 Button / Tag 的 dev-only 誤用警告 idiom)。
397
+
396
398
  **Keyboard 行為**(自建 handler,`tree-view.tsx` handleKeyDown):
397
399
 
398
400
  - Tab — 焦點進入整棵 tree(單一 tab stop)
@@ -137,10 +137,10 @@ export const LongLabel: Story = {
137
137
  <div className="flex flex-col gap-2">
138
138
  <span className="text-caption font-medium text-fg-muted">預設 truncate</span>
139
139
  <div className="w-[220px] border border-divider rounded-lg bg-surface overflow-hidden py-2">
140
- <TreeView aria-label="truncate test" defaultExpandedIds={['proj']}>
141
- <TreeItem id="proj" icon={Folder} label="這是一個很長的專案名稱會被截斷">
142
- <TreeItem id="f1" icon={FileText} label="這是一個很長的檔案名稱也會被截斷.tsx" />
143
- <TreeItem id="f2" icon={FileText} label="短檔名.ts" />
140
+ <TreeView aria-label="產品設計檔案樹(截斷)" defaultExpandedIds={['proj']}>
141
+ <TreeItem id="proj" icon={Folder} label="2026 產品品牌識別設計提案">
142
+ <TreeItem id="f1" icon={FileText} label="首頁主視覺設計稿-桌面版-v3.fig" />
143
+ <TreeItem id="f2" icon={FileText} label="logo.svg" />
144
144
  </TreeItem>
145
145
  </TreeView>
146
146
  </div>
@@ -148,10 +148,10 @@ export const LongLabel: Story = {
148
148
  <div className="flex flex-col gap-2">
149
149
  <span className="text-caption font-medium text-fg-muted">label 換行(移除 truncate)</span>
150
150
  <div className="w-[220px] border border-divider rounded-lg bg-surface overflow-hidden py-2">
151
- <TreeView aria-label="wrap test" defaultExpandedIds={['proj2']}>
152
- <TreeItem id="proj2" icon={Folder} label={<span className="break-words whitespace-normal">這是一個很長的專案名稱會自然換行到下一行</span>}>
153
- <TreeItem id="f3" icon={FileText} label={<span className="break-words whitespace-normal">這是一個很長的檔案名稱也會自然換行到下一行顯示完整內容.tsx</span>} />
154
- <TreeItem id="f4" icon={FileText} label="短檔名.ts" />
151
+ <TreeView aria-label="產品設計檔案樹(換行)" defaultExpandedIds={['proj2']}>
152
+ <TreeItem id="proj2" icon={Folder} label={<span className="break-words whitespace-normal">2026 產品品牌識別設計提案完整版</span>}>
153
+ <TreeItem id="f3" icon={FileText} label={<span className="break-words whitespace-normal">首頁主視覺設計稿-桌面版與行動版對照-v3-最終定稿.fig</span>} />
154
+ <TreeItem id="f4" icon={FileText} label="logo.svg" />
155
155
  </TreeItem>
156
156
  </TreeView>
157
157
  </div>
@@ -263,38 +263,4 @@ export const DragAndDrop: Story = {
263
263
  }
264
264
 
265
265
  // @story-trait-rationale: AllSizes retired per F migration(2026-05-15)— anatomy.stories.tsx SizeMatrix auto-compile owns size showcase。
266
- // ── Indent 結構對齊驗證 ─────────────────────────────────────────────────
267
-
268
- export const IndentAlignment: Story = {
269
- name: '縮排結構對齊驗證',
270
- render: () => (
271
- <div className="flex flex-col gap-4">
272
- <div className="flex flex-col gap-1">
273
- <h3 className="text-h6 font-semibold">indentStep = chevronSize + gap-2</h3>
274
- <p className="text-caption text-fg-muted max-w-xl">
275
- 子 chevron 應對齊父 icon 的起始位置,子 icon 應對齊父 label 的起始位置。
276
- 這裡混合有 / 無 children 的 node 驗證 chevron placeholder 佔位(leaf 無條件留等寬空白);
277
- icon 無自動佔位——無 icon 的 node(README.md / public)label 起點前移,屬「Icon 使用策略」
278
- 的混用反例,此處僅作 indent 結構驗證。
279
- </p>
280
- </div>
281
- <div className="w-[300px] border border-divider rounded-lg bg-surface overflow-hidden py-2">
282
- <TreeView aria-label="專案檔案樹" defaultExpandedIds={['src', 'components', 'public']}>
283
- <TreeItem id="src" icon={Folder} label="src">
284
- <TreeItem id="app" icon={FileCode} label="App.tsx" />
285
- <TreeItem id="readme" label="README.md" />
286
- </TreeItem>
287
- <TreeItem id="public" label="public">
288
- <TreeItem id="favicon" icon={Image} label="favicon.ico" />
289
- </TreeItem>
290
- <TreeItem id="components" icon={Folder} label="components">
291
- <TreeItem id="button" icon={Folder} label="Button">
292
- <TreeItem id="button-tsx" icon={FileCode} label="Button.tsx" />
293
- <TreeItem id="button-test" label="Button.test.tsx" />
294
- </TreeItem>
295
- </TreeItem>
296
- </TreeView>
297
- </div>
298
- </div>
299
- ),
300
- }
266
+ // @story-trait-rationale: IndentAlignment retired per Dim 24 dedup(2026-07-17)— indentStep=chevronSize+gap-2 幾何三重重複(showcase / anatomy IndentMatrix / principles IndentRule)。geometry 歸 anatomy.stories.tsx IndentMatrix、縮排原則 + icon 混用反例(唯一在此的內容)已遷入 principles.stories.tsx IndentRule;showcase 層不重複教 geometry(同 AllSizes / States 退役先例)。
@@ -293,6 +293,25 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
293
293
  },
294
294
  ref
295
295
  ) => {
296
+ // ── Accessible name dev-warn(2026-07-18:兌現 spec「Accessible name 必填契約」段承諾 —
297
+ // 原 spec 宣稱有此 console.warn 但 code 缺,spec-code drift;補上使兩者一致)──
298
+ // role="tree" 名稱無法從子節點推導,缺 aria-label / aria-labelledby → SR 只讀「tree」。
299
+ // 對齊 Button / Tag 的 dev-only 誤用警告 idiom;production 不觸發。
300
+ const ariaLabel = props['aria-label']
301
+ const ariaLabelledby = props['aria-labelledby']
302
+ React.useEffect(() => {
303
+ if (
304
+ process.env.NODE_ENV !== 'production' &&
305
+ ariaLabel == null &&
306
+ ariaLabelledby == null
307
+ ) {
308
+ console.warn(
309
+ '[DS] TreeView:role="tree" 缺 accessible name — 請傳 aria-label(直接字串)或 aria-labelledby(指向可見標題 id)其一。' +
310
+ '兩者皆缺時螢幕閱讀器只讀出「tree」無法辨識用途(WAI-ARIA APG 要求 role="tree" 具 accessible name)。',
311
+ )
312
+ }
313
+ }, [ariaLabel, ariaLabelledby])
314
+
296
315
  // ── Expand state(受控 / 非受控) ──
297
316
  const [internalExpanded, setInternalExpanded] = React.useState(
298
317
  () => new Set(defaultExpandedIds)
@@ -776,6 +795,23 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
776
795
  isKeyboardRef.current = true
777
796
  if (!treeRef.current) return
778
797
 
798
+ // ── 互動 descendant 自理鍵盤(先於導覽 / 重排分支)──
799
+ // 公開 inlineActions / inlineActionsSlot 經 <ItemInlineAction> 渲染的原生 <button>
800
+ // (及 consumer slot 內的 link / field / role=button)是各自獨立的 tab stop(spec
801
+ // A11y「單一 tab stop」段)。其 Enter/Space/方向鍵應由該控件自理——若冒泡到容器
802
+ // handleKeyDown,下方 Enter/Space 分支的 e.preventDefault()+select() 會吃掉 button
803
+ // 原生啟動並誤選 tree node。虛擬焦點模型下 tree 導覽/重排只在事件來自容器本身時處理
804
+ // (DOM focus 停在 tree 容器;treeitem row / chevron / checkbox 皆 tabIndex=-1)。
805
+ const keyTarget = e.target as HTMLElement | null
806
+ if (keyTarget && keyTarget !== e.currentTarget) {
807
+ const interactive = keyTarget.closest<HTMLElement>(
808
+ 'a[href], button, input, select, textarea, [contenteditable="true"], [tabindex]:not([tabindex="-1"])'
809
+ )
810
+ // interactive === e.currentTarget:唯一可聚焦祖先是 tree 容器(tabIndex=0 單一 tab
811
+ // stop 進出點)→ 屬非互動 descendant,照常走 tree 導覽。
812
+ if (interactive && interactive !== e.currentTarget) return
813
+ }
814
+
779
815
  // ── 鍵盤重排:Cmd/Ctrl+Shift+方向鍵(2026-07-14 v1,詳 spec「鍵盤重排」)──
780
816
  // 必排在下方「無焦點時方向鍵先聚焦第一項」分支之前(該分支不分 modifier 攔 Arrow*);
781
817
  // 非 modifier 導覽路徑(下方 switch)完全不變。
package/src/index.ts CHANGED
@@ -76,7 +76,6 @@ export {
76
76
  Button,
77
77
  ButtonDivider,
78
78
  ButtonGroup,
79
- ButtonGroupContext,
80
79
  buttonVariants,
81
80
  } from './components/Button/index'
82
81
  export type {
@@ -192,8 +191,8 @@ export {
192
191
  DescriptionList,
193
192
  } from './components/DescriptionList/index'
194
193
  export type {
195
- DescriptionDirection,
196
194
  DescriptionItemProps,
195
+ DescriptionListOrientation,
197
196
  DescriptionListProps,
198
197
  } from './components/DescriptionList/index'
199
198
  export {
@@ -228,7 +227,6 @@ export {
228
227
  DropdownMenuSubContent,
229
228
  DropdownMenuSubTrigger,
230
229
  DropdownMenuTrigger,
231
- floatingLayerClass,
232
230
  } from './components/DropdownMenu/index'
233
231
  export type {
234
232
  DropdownMenuItemProps,
@@ -246,7 +244,6 @@ export {
246
244
  FieldError,
247
245
  FieldGroup,
248
246
  FieldLabel,
249
- getMenuListMinHeight,
250
247
  useFormValidation,
251
248
  } from './components/Field/index'
252
249
  export type {
@@ -417,7 +414,6 @@ export {
417
414
  SheetPortal,
418
415
  SheetTitle,
419
416
  SheetTrigger,
420
- handleSheetOpenAutoFocus,
421
417
  sheetVariants,
422
418
  } from './components/Sheet/index'
423
419
  export {
@@ -548,13 +544,10 @@ export {
548
544
  ItemAvatar,
549
545
  ItemContent,
550
546
  ItemIcon,
551
- ItemInlineAction,
552
- ItemInlineActionButton,
553
547
  ItemLabel,
554
548
  ItemPrefix,
555
549
  ItemSuffix,
556
550
  ROW_PADDING_BY_SIZE,
557
- RowSizeProvider,
558
551
  getUniformPrefixSlotStyle,
559
552
  itemPrefixAlignVariants,
560
553
  useRowSize,
@@ -564,8 +557,6 @@ export type {
564
557
  ItemAvatarProps,
565
558
  ItemContentProps,
566
559
  ItemIconProps,
567
- ItemInlineActionButtonProps,
568
- ItemInlineActionProps,
569
560
  ItemSuffixHoverGroup,
570
561
  ItemSuffixProps,
571
562
  RowSize,
@@ -598,6 +589,18 @@ export type {
598
589
  // - components/SelectionControl
599
590
  // - patterns/horizontal-overflow
600
591
  // - patterns/overlay-surface
592
+ // 另有 public 元件內個別標 @internal jsDoc 的符號亦排除 front-door(2026-07-18 決策3;subpath 仍有):
593
+ // - components/Button:ButtonGroupContext
594
+ // - components/DropdownMenu:floatingLayerClass
595
+ // - components/Field:FieldVariantInternal
596
+ // - components/Field:WithFieldVariantInternal
597
+ // - components/Field:getMenuListMinHeight
598
+ // - components/Sheet:handleSheetOpenAutoFocus
599
+ // - patterns/element-anatomy:ItemInlineAction
600
+ // - patterns/element-anatomy:ItemInlineActionButton
601
+ // - patterns/element-anatomy:ItemInlineActionButtonProps
602
+ // - patterns/element-anatomy:ItemInlineActionProps
603
+ // - patterns/element-anatomy:RowSizeProvider
601
604
 
602
605
  // ─── Tokens(JS mirrors — token SSOT 程式面)──────────────────────────────
603
606
  export * from './tokens/categorical-color'
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * ## Pattern
8
8
  *
9
- * - **Source element**(被拖中的 row/column):`opacity-30`(半透,user 仍看得到原位)
9
+ * - **Source element**(被拖中的 row/column):`opacity-disabled`(= var(--opacity-disabled) 0.45 半透,user 仍看得到原位;2026-05-06 v14.5.2 由 opacity-30 改)
10
10
  * — 不是 `opacity:0` 全隱形,因 user 拖太遠看不到「源頭」會迷失方向。對齊 TreeView 最早 v1 + Notion / Atlassian / Linear。
11
11
  * - **Ghost preview**(浮動 follow cursor):透過 dnd-kit `<DragOverlay>` portal,clone source 的 outerHTML
12
12
  * strip transform/transition/opacity → render 在 portal 層,visually 跟 cursor 走。
@@ -26,7 +26,7 @@
26
26
  * - `bg-primary`:semantic state token(`--primary`)— 跟 focus state ring color 同 source(M23 一致)
27
27
  * - `bg-primary-subtle`:semantic primary subtle(`--primary-subtle`)— inside-drop dim background
28
28
  * - `h-0.5` / `w-0.5`:Tailwind size token = 2px(對齊 hairline divider thickness 概念)
29
- * - `opacity-30`:Tailwind 30% opacity(半透但仍清楚看到 outline)
29
+ * - `opacity-disabled`:DS token var(--opacity-disabled)= 0.45(半透但仍清楚看到 outline;見下方 v14.5.2 note)
30
30
  */
31
31
 
32
32
  import type * as React from 'react'
package/src/lib/utils.ts CHANGED
@@ -92,3 +92,22 @@ const twMerge = extendTailwindMerge({
92
92
  export function cn(...inputs: ClassValue[]) {
93
93
  return twMerge(clsx(inputs))
94
94
  }
95
+
96
+ // ── line-clamp 安全對照(2026-07-18 決策4)────────────────────────────────────
97
+ // 動態 `line-clamp-${n}` / `line-clamp-[${n}]` = Tailwind 掃不到具體值 → silent 失效(失敗記憶索引:
98
+ // 「Tailwind v4 動態 class」陷阱)。此 map 全靜態字串 → Tailwind 可掃。canonical 上限 1-6(spec maxLines/
99
+ // descriptionClamp ≤6);超界值 dev-warn + clamp 回 6(不 crash、不靜默失效)。null/0/undefined → 不 clamp。
100
+ const LINE_CLAMP: Record<number, string> = {
101
+ 1: 'line-clamp-1', 2: 'line-clamp-2', 3: 'line-clamp-3',
102
+ 4: 'line-clamp-4', 5: 'line-clamp-5', 6: 'line-clamp-6',
103
+ }
104
+ export function lineClampClass(n: number | undefined | null): string | undefined {
105
+ if (n == null || n <= 0) return undefined
106
+ const rounded = Math.round(n)
107
+ if (rounded !== n || n > 6) {
108
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
109
+ console.warn(`[DS] line-clamp 值 ${n} 超出 canonical 範圍 1-6(或非整數)→ clamp 回 6。請傳 1-6 整數。`)
110
+ }
111
+ }
112
+ return LINE_CLAMP[Math.min(6, Math.max(1, rounded))]
113
+ }
@@ -472,3 +472,4 @@ Icon 的目的是幫助辨識,不是視覺對稱。
472
472
  - `data-table.spec.md`
473
473
  - `file-viewer.spec.md`
474
474
  - `popover.spec.md`
475
+ - `separator.spec.md`
@@ -168,7 +168,7 @@ Q3. Row 多大?
168
168
  #### Content-role vs action-role 分層(附補充原理)
169
169
 
170
170
  Row 內元件分兩類,**size 規則不同**:
171
- - **Content-role**(display 資料):`<Input mode="display">` / Badge / Avatar / Tag → size 對應 row tier(sm row → sm)
171
+ - **Content-role**(display 資料):`<Input mode="view">` / Badge / Avatar / Tag → size 對應 row tier(sm row → sm)
172
172
  - **Action-role**(互動觸發):row action icon → **固定 ≤ 24**,不參與 content size-pair
173
173
 
174
174
  Row action 的 affordance 是「次要功能」,不是 primary CTA。Button chrome 過度強調;用 **Button xs 24 固定** 提供 command affordance 但不侵蝕 content hierarchy。
@@ -1028,15 +1028,18 @@ Selection control(Dropdown / Menu / List / SegmentedControl / Chip)的 item 視
1028
1028
  > 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
1029
1029
 
1030
1030
  - `alert.spec.md`
1031
+ - `checkbox.spec.md`
1031
1032
  - `color.spec.md`
1032
1033
  - `command.spec.md`
1033
1034
  - `dropdown-menu.spec.md`
1034
1035
  - `element-anatomy.spec.md`
1035
1036
  - `empty.spec.md`
1037
+ - `field-controls.spec.md`
1036
1038
  - `field.spec.md`
1037
1039
  - `file-item.spec.md`
1038
1040
  - `header-canonical.spec.md`
1039
1041
  - `inline-action.spec.md`
1042
+ - `layoutSpace.spec.md`
1040
1043
  - `menu-item.spec.md`
1041
1044
  - `notice.spec.md`
1042
1045
  - `select-menu.spec.md`
@@ -1047,5 +1050,6 @@ Selection control(Dropdown / Menu / List / SegmentedControl / Chip)的 item 視
1047
1050
  - `tabs.spec.md`
1048
1051
  - `time-picker.spec.md`
1049
1052
  - `toast.spec.md`
1053
+ - `token-system.spec.md`
1050
1054
  - `tree-view.spec.md`
1051
1055
  - `typography.spec.md`
@@ -3,7 +3,7 @@
3
3
  import * as React from "react"
4
4
  import type { LucideIcon } from "lucide-react"
5
5
  import { cva } from "class-variance-authority"
6
- import { cn } from "@/lib/utils"
6
+ import { cn, lineClampClass } from "@/lib/utils"
7
7
  import { Avatar, type AvatarProps } from "@/design-system/components/Avatar/avatar"
8
8
  import {
9
9
  Tooltip,
@@ -392,7 +392,7 @@ export const ItemContent = React.forwardRef<HTMLDivElement, ItemContentProps>(
392
392
  ? "text-[length:var(--font-body-size)]"
393
393
  : ""
394
394
 
395
- const clampClass = descriptionClamp ? `line-clamp-${descriptionClamp}` : ""
395
+ const clampClass = lineClampClass(descriptionClamp) ?? ""
396
396
 
397
397
  return (
398
398
  <div
@@ -558,6 +558,7 @@ ItemAvatar.displayName = "ItemAvatar"
558
558
  //
559
559
  // 接受任何 button props 並 spread(讓 Radix Slot 可以 merge onClick / data-state 等)
560
560
 
561
+ /** @internal — internal ItemInlineActionButton 的 props;隨其元件同走 subpath,不進 root barrel front-door。 */
561
562
  export interface ItemInlineActionButtonProps
562
563
  extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
563
564
  /** Lucide icon 元件 */
@@ -662,6 +663,7 @@ ItemInlineActionButton.displayName = "ItemInlineActionButton"
662
663
  // 需要接 Radix asChild(例如 collapsible trigger)時改用 `ItemInlineActionButton`,
663
664
  // 那層沒有 Tooltip 包裝,Root 就是 button,可直接塞進 Radix Slot。
664
665
 
666
+ /** @internal — internal ItemInlineAction 的 props;隨其元件同走 subpath,不進 root barrel front-door。 */
665
667
  export interface ItemInlineActionProps {
666
668
  action: InlineActionConfig
667
669
  /** Size 覆寫(見 `ItemInlineActionButtonProps.size`)——Field Controls / Tag 必傳 */
@@ -270,6 +270,15 @@ ChromeHeader / SurfaceHeader 新增 `tabsSlot?: ReactNode` prop。提供時自
270
270
 
271
271
  **不開**:`density?: 'md'|'lg'` 自由 prop(M21 違反 — 任意 density 等於 cva-on-pattern,該由 density context 決定)。`lockDensity="lg"` 原為 fullscreen viewer escape hatch,但 **2026-06-15 起無 consumer 使用** — FileViewer 改為全 surface 繼承 page density(原本只鎖兩個 header、沒鎖 body → 左緣不對齊,圖二 bug)。density lock 一律設在 **surface 根**而非 header primitive(見 `density.spec.md` anti-pattern)。其餘 consumer 走 `inherit`(跟 page density)。**Grep evidence**(historical 錨,migration 已完成):migration 前 `Sidebar` + `FileViewer Toolbar/InfoPanel` 各自手刻同一 signature `flex items-center gap-2 shrink-0 h-[var(--chrome-header-height)] border-b border-divider px-[var(--layout-space-loose)]`(production 重複 contract,非 premature abstraction)→ 過 M21 prop variant test 3 條(`<Sidebar>` 等近親 grep / 3 家世界級 page-header primitive / value 結構不同)→ 抽成本 primitive。**現況**:全部已消費 `<ChromeHeader>`(`sidebar.tsx:479` / `file-viewer.tsx:340` Toolbar + `:474` InfoPanel),grep 全 repo 已無此手刻 signature(除 `chrome-header.tsx` primitive 本身)。
272
272
 
273
+ ### 空 children / action state 契約(rendering contract)
274
+
275
+ **空 children**:`ChromeHeaderProps extends React.HTMLAttributes<HTMLElement>`,故 `children` 型別上 optional;但**實作恆渲染固定高度 row**(single-row `h-[var(--chrome-header-height)]` + `border-b`,`chrome-header.tsx:167-183`;`tabsSlot` 模式 row 1 亦恆渲染,`:113-122`)。因此:
276
+
277
+ - **沒有 title / actions 時不要渲染 ChromeHeader**——空 children 只會留一條固定高度 + border 的空白 chrome row(`tabsSlot` 模式更明顯:tabs 上方多整行空白標題列)。consumer 條件渲染,有內容才掛 header,而非傳空殼。
278
+ - **只有 tabs、無獨立標題列**的場景 → 用 standalone `<Tabs size="lg">`(tab 高 = chrome-header-height,見「選哪個 header」表 C 列),**不要**空 children + `tabsSlot` 硬撐。
279
+
280
+ **Action loading / disabled 歸屬**:ChromeHeader 只負責 slot 排版,**不擁有**注入 action 的非同步狀態。header 內 Button / 控件的 `loading` / `disabled` 由 consumer 注入的元件自帶(如 `<Button loading>` 自畫 spinner),ChromeHeader 層**無** loading / disabled prop。故本 primitive 的 **Loading 驗證 = N/A**(排版 primitive,無自身 async state)。
281
+
273
282
  ### Layer 4 — Hook mechanical
274
283
 
275
284
  `chrome_header_dispatcher.sh` 依序 dispatch **4 個 lib helper**(完整清單 SSOT = dispatcher 本身,避免 spec drift):
@@ -68,9 +68,13 @@ export const SingleRow = () => (
68
68
  <Separator orientation="vertical" className="h-6 mx-1" />
69
69
  <Button iconOnly dismiss size="sm" startIcon={X} aria-label="關閉" />
70
70
  </ChromeHeader>
71
- <div className="p-6 text-body text-fg-secondary">文件預覽內容區…</div>
71
+ <div className="flex flex-col gap-2 p-6 text-body text-fg-secondary">
72
+ <p className="text-caption text-fg-muted">陳雅婷 更新於 2026/04/18 · 12 頁 · 2.4 MB</p>
73
+ <p>本季合併營收 NT$4.82 億,較上季成長 8.2%;毛利率維持 41%。附錄含各區域損益與現金流量表。</p>
74
+ </div>
72
75
  </Panel>
73
76
  )
77
+ SingleRow.storyName = '單列(標題 + 動作列)'
74
78
 
75
79
  /** 2. withTabs — header 含分頁列(對標 FileViewer InfoPanel / 設定頁 header)。
76
80
  * 傳 tabsSlot 自動進 column 結構:row1 = 標題列、row2 = TabsList 全寬畫線。 */
@@ -92,17 +96,29 @@ export const WithTabs = () => (
92
96
  <Button iconOnly dismiss size="sm" startIcon={X} aria-label="關閉" />
93
97
  </ChromeHeader>
94
98
  <TabsContent value="overview">
95
- <div className="p-6 text-body text-fg-secondary">「概覽」分頁內容…</div>
99
+ <div className="flex flex-col gap-1 p-6 text-body text-fg-secondary">
100
+ <p>負責人:林思妤 · 期程:2026/03–2026/06 · 狀態:設計中</p>
101
+ <p>將既有品牌識別全面翻新——logo、配色與元件庫,預計 Q2 上線。</p>
102
+ </div>
96
103
  </TabsContent>
97
104
  <TabsContent value="activity">
98
- <div className="p-6 text-body text-fg-secondary">「活動」分頁內容…</div>
105
+ <div className="flex flex-col gap-1 p-6 text-body text-fg-secondary">
106
+ <p>陳柏宇 上傳了「新版 logo 提案 v3」· 2 小時前</p>
107
+ <p>林思妤 將狀態改為「設計中」· 昨天</p>
108
+ <p>Aisha Khan 留言:配色再往暖色調微調 · 3 天前</p>
109
+ </div>
99
110
  </TabsContent>
100
111
  <TabsContent value="files">
101
- <div className="p-6 text-body text-fg-secondary">「檔案」分頁內容…</div>
112
+ <div className="flex flex-col gap-1 p-6 text-body text-fg-secondary">
113
+ <p>品牌識別規範_v3.pdf · 8.1 MB</p>
114
+ <p>logo 原始檔.ai · 24 MB</p>
115
+ <p>配色對照表.xlsx · 320 KB</p>
116
+ </div>
102
117
  </TabsContent>
103
118
  </Tabs>
104
119
  </Panel>
105
120
  )
121
+ WithTabs.storyName = '含分頁列'
106
122
 
107
123
  /** Overview — header anatomy 兩型疊起來一次看完,每型上方標清「何時用 + 對標既有元件」。
108
124
  * 注:ChromeHeader 另有 `leadingRail` slot,但它專為「跟 sidebar 收合 icon 對齊」而生(寬 = sidebar 收合寬),
@@ -136,7 +152,16 @@ export const Overview = () => (
136
152
  <h2 className="flex-1 truncate text-body-lg font-medium text-foreground">編輯專案設定</h2>
137
153
  <Button iconOnly dismiss size="sm" startIcon={X} aria-label="關閉" />
138
154
  </SurfaceHeader>
139
- <div className="p-6 text-body text-fg-secondary">浮層內容區…(Dialog / Sheet / Popover body)</div>
155
+ <div className="flex flex-col gap-3 p-6 text-body text-fg-secondary">
156
+ <div className="flex flex-col gap-0.5">
157
+ <span className="text-caption text-fg-muted">專案名稱</span>
158
+ <span className="text-foreground">Acme 品牌改版專案</span>
159
+ </div>
160
+ <div className="flex flex-col gap-0.5">
161
+ <span className="text-caption text-fg-muted">可見範圍</span>
162
+ <span className="text-foreground">團隊成員(12 人)</span>
163
+ </div>
164
+ </div>
140
165
  </div>
141
166
  </section>
142
167
  <section className="flex flex-col gap-2">
@@ -148,3 +173,4 @@ export const Overview = () => (
148
173
  </section>
149
174
  </div>
150
175
  )
176
+ Overview.storyName = '總覽'
@@ -24,7 +24,7 @@
24
24
  * **機械防線**:`scripts/categorical-color-invariants.mjs` I1(release:preflight 已 chain)強制 map 的 key X 的值含 `--color-X-`(名實一致,禁 offset;原引 hook 名不存在,2026-07-04 修)。
25
25
  */
26
26
 
27
- /** 12 個 categorical 色相(順序對齊 primitives.css / Storybook color token )。 */
27
+ /** 12 個 categorical 色相。⚠️ 順序 = **categorical 指派序**(圖表系列鄰色最大區辨:blue→green→deep-orange→yellow→red…),**非** primitives.css 檔內宣告序 兩者本就不同,map 以名為 key 故功能無涉;**禁**為「對齊」重排(重排 = 圖表系列換色 = 視覺 breaking)。 */
28
28
  export const CATEGORICAL_HUES = [
29
29
  'blue',
30
30
  'green',
@@ -64,8 +64,8 @@ export const CAT_SUBTLE: Record<CategoricalHue, string> = {
64
64
  /**
65
65
  * solid 模式 class:step-6 底 + on-emphasis 配對文字。
66
66
  * 文字色依「step-6 底亮度」分桶(WCAG ≥3:1 大粗字門檻,2026-06-04 user「以最低為原則」):
67
- * - 夠深的色底 → `text-on-emphasis`(白):blue / deep-orange / red / turquoise / indigo / purple / magenta
68
- * - 太亮的色底 → `text-on-emphasis-dark`(深):yellow / amber / orange / lime(白字連 3:1 都不過)
67
+ * - 夠深的色底 → `text-on-emphasis`(白):blue / deep-orange / red / indigo / purple / magenta
68
+ * - 太亮的色底 → `text-on-emphasis-dark`(深):yellow / amber / orange / lime / turquoise(白字連 3:1 都不過;turquoise 2026-07-16 dark 色相修正後 L=0.68 白字 2.77 → 切深字,兩 mode 5.65/6.47 全過)
69
69
  * - ★ green 例外:白字 2.47 連 3:1 都不過,但 user 拍板維持白字(綠底白字慣見觀感)= documented exception。
70
70
  * 對比為 oklch→相對亮度實測(scripts/categorical-color-invariants.mjs 機械驗,green 列 exempt)。
71
71
  */
@@ -78,7 +78,7 @@ export const CAT_SOLID: Record<CategoricalHue, string> = {
78
78
  orange: 'bg-[var(--color-orange-6)] text-on-emphasis-dark',
79
79
  amber: 'bg-[var(--color-amber-6)] text-on-emphasis-dark',
80
80
  lime: 'bg-[var(--color-lime-6)] text-on-emphasis-dark',
81
- turquoise: 'bg-[var(--color-turquoise-6)] text-on-emphasis',
81
+ turquoise: 'bg-[var(--color-turquoise-6)] text-on-emphasis-dark',
82
82
  indigo: 'bg-[var(--color-indigo-6)] text-on-emphasis',
83
83
  purple: 'bg-[var(--color-purple-6)] text-on-emphasis',
84
84
  magenta: 'bg-[var(--color-magenta-6)] text-on-emphasis',
@@ -125,7 +125,7 @@ export const CAT_SOLID_TOKENS: Record<CategoricalHue, { bg: string; text: string
125
125
  orange: { bg: 'var(--color-orange-6)', text: 'var(--on-emphasis-dark)' },
126
126
  amber: { bg: 'var(--color-amber-6)', text: 'var(--on-emphasis-dark)' },
127
127
  lime: { bg: 'var(--color-lime-6)', text: 'var(--on-emphasis-dark)' },
128
- turquoise: { bg: 'var(--color-turquoise-6)', text: 'var(--on-emphasis)' },
128
+ turquoise: { bg: 'var(--color-turquoise-6)', text: 'var(--on-emphasis-dark)' },
129
129
  indigo: { bg: 'var(--color-indigo-6)', text: 'var(--on-emphasis)' },
130
130
  purple: { bg: 'var(--color-purple-6)', text: 'var(--on-emphasis)' },
131
131
  magenta: { bg: 'var(--color-magenta-6)', text: 'var(--on-emphasis)' },
@@ -272,8 +272,8 @@ Tag、Avatar 等需要多色區分的場景(專案標籤、團隊分類等)
272
272
 
273
273
  填色底上的文字色**依底色明暗成對**提供(世界級共識:Material `on-primary` family、Radix `--accent-contrast`、Polaris `text-{x}-on-bg-fill` —— 都是「成對 on-color token」,不是單一固定白):
274
274
 
275
- - `--on-emphasis = oklch(1 0 0)`(純白,dark mode 不反轉)—— 用於**夠深的飽和色底**(白字達 WCAG 門檻):primitive 6 階深色 / `--primary` / `--error` 等,及 categorical blue/deep-orange/red/turquoise/indigo/purple/magenta。
276
- - `--on-emphasis-dark = var(--black-a85)`(深字,dark mode 不反轉)—— 用於**太亮的色底**(白字對比不足):categorical **yellow / amber / orange / lime**。
275
+ - `--on-emphasis = oklch(1 0 0)`(純白,dark mode 不反轉)—— 用於**夠深的飽和色底**(白字達 WCAG 門檻):primitive 6 階深色 / `--primary` / `--error` 等,及 categorical blue/deep-orange/red/indigo/purple/magenta(turquoise 2026-07-16 移入深字組)
276
+ - `--on-emphasis-dark = var(--black-a85)`(深字,dark mode 不反轉)—— 用於**太亮的色底**(白字對比不足):categorical **yellow / amber / orange / lime / turquoise**(turquoise 2026-07-16 dark 色相修正後加入)。
277
277
 
278
278
  **合規門檻(2026-06-04 user 拍板「以最低為原則」)= WCAG large/bold 3:1**(solid chip / avatar 的字視為大粗字)。底色 step-6 白字達 3:1 → 用 `--on-emphasis`;不達 → 用 `--on-emphasis-dark`。
279
279
 
@@ -367,7 +367,7 @@ Badge 使用語義色的 text token(`--info-text`、`--error-text`),不直
367
367
 
368
368
  **step-7 vs semantic active 的區別**:兩者在 light mode 都指向 step-7,但 dark mode 行為不同。semantic active(如 `--primary-active`)在 dark mode 反轉為 step-5(壓暗模擬按壓感),而 primitive step-7 在 dark mode 仍維持高對比方向。簡言之:active 服務於互動回饋,step-7 服務於閱讀對比,兩者目的不同,dark mode 方向相反。
369
369
 
370
- **例外:step-6 滿版底色上的「亮色 hue」文字用 `--on-emphasis-dark`(`black-a85`,深色)**——yellow / amber / orange / lime 亮度極高,白字(`--on-emphasis`)連 WCAG 3:1 都不過,必須深色文字(見上「`--on-emphasis` 配對」段)。此例外只在底色是 step-6 時觸發,step-1 subtle 底色上仍用 step-7。(green-6 白字亦不過 3:1,但列 documented exception 維持白字。)
370
+ **例外:step-6 滿版底色上的「亮色 hue」文字用 `--on-emphasis-dark`(`black-a85`,深色)**——yellow / amber / orange / lime / turquoise 亮度極高,白字(`--on-emphasis`)連 WCAG 3:1 都不過,必須深色文字(見上「`--on-emphasis` 配對」段)。此例外只在底色是 step-6 時觸發,step-1 subtle 底色上仍用 step-7。(green-6 白字亦不過 3:1,但列 documented exception 維持白字。)
371
371
 
372
372
  ### Subtle 背景(淡色填充)
373
373
 
@@ -501,7 +501,7 @@ Dark mode 覆寫:hover/active 方向反轉(hover → step-7,active → ste
501
501
  在 `primitives.css` 定義 base-6 值(只需指定 L、C、H),相對公式自動推導 1-10 階。如已存在則跳過。
502
502
 
503
503
  ```css
504
- --color-turquoise-6: oklch(0.57 0.10 225);
504
+ --color-turquoise-6: oklch(0.64 0.09 196); /* light 真值(primitives.css:183);dark 另於 dark 區塊定義 */
505
505
  /* 1-5 / 7-10 自動由公式推導 */
506
506
  ```
507
507
 
@@ -285,7 +285,10 @@
285
285
  --color-neutral-9-opaque: color-mix(in srgb, white var(--_na9), black);
286
286
 
287
287
  /* Blue */
288
- --color-blue-6: oklch(0.60 0.22 258);
288
+ /* 2026-07-16 user 拍板:dark 主色對比升級 — 0.600.63(≈#2C84FF,對齊 Apple dark systemBlue)
289
+ card/table/浮層 surface 上互動文字 4.07-4.10:1(AA ✗)→ 4.63-4.67:1(AA ✓);canvas 5.53:1。
290
+ 其餘 1-10 階不手動指定,由本基準經下方既有相對公式自動推導(單一基準,設計語言不變)。 */
291
+ --color-blue-6: oklch(0.63 0.22 258);
289
292
  --color-blue-1: oklch(from var(--color-blue-6) l c h / calc(0.12 / l));
290
293
  --color-blue-2: oklch(from var(--color-blue-6) calc(l * 0.28) calc(c * 0.65) h);
291
294
  --color-blue-3: oklch(from var(--color-blue-6) calc(l * 0.44) calc(c * 0.82) h);
@@ -369,7 +372,11 @@
369
372
  --color-green-10: oklch(from var(--color-green-6) calc(l + (1 - l) * 0.75) calc(c * 0.14) h);
370
373
 
371
374
  /* Turquoise */
372
- --color-turquoise-6: oklch(0.57 0.10 225);
375
+ /* 2026-07-16 user 拍板修正:原 oklch(0.57 0.10 225)為複製 spec 假想範例的錯值 — 全 12 色相唯一
376
+ dark 改色相(196→225 = 偏藍)、唯一 L 降(其餘全升)、唯一 C 升,三重違反設計語言。
377
+ 修回 H=196(同 light,Apple systemBlue/Teal dark 同樣色相恆定);L 照 11 色相經驗律內插
378
+ (L≤0.58 → +0.05 / L≥0.70 → +0.03;0.64 居中 → +0.04)= 0.68;C 照 ×0.95 2dp 慣例 = 0.09。 */
379
+ --color-turquoise-6: oklch(0.68 0.09 196);
373
380
  --color-turquoise-1: oklch(from var(--color-turquoise-6) l c h / calc(0.12 / l));
374
381
  --color-turquoise-2: oklch(from var(--color-turquoise-6) calc(l * 0.28) calc(c * 0.65) h);
375
382
  --color-turquoise-3: oklch(from var(--color-turquoise-6) calc(l * 0.44) calc(c * 0.82) h);
@@ -97,13 +97,6 @@ Hover delay token 是「hover 觸發 → 延遲 N ms → overlay 顯示」的延
97
97
  - `../../components/Tooltip/tooltip.spec.md`(consumer)
98
98
  - `../../components/HoverCard/hover-card.spec.md`(consumer)
99
99
  - `../../components/Avatar/avatar.spec.md`(consumer)
100
-
101
- ## 被引用(auto-maintained,Dim 3 reciprocal audit)
102
-
103
- > 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
104
-
105
- - `hover-card.spec.md`
106
-
107
100
  ## 進出場動畫 token(2026-07-11 加,user 拍板)
108
101
 
109
102
  Overlay(Tooltip/Popover/HoverCard/DropdownMenu/Dialog/Sheet/FileViewer)的 fade/zoom/slide 進出場動畫,值統一 token 化(原各元件硬寫 zoom-95/slide-2/duration-300 = M17 假 SSOT)。由 tw-animate-css(Tailwind v4,= shadcn 官方機制)的 `--tw-duration`/`--tw-ease` 變數綁定;共用 SSOT = `overlay-motion.ts`(overlayMotion/surfaceMotion)。
@@ -120,3 +113,9 @@ Overlay(Tooltip/Popover/HoverCard/DropdownMenu/Dialog/Sheet/FileViewer)的 fade/
120
113
  **幾何原型分層(正當差異,不強行抹平)**:輕量 popup = fade+zoom+slide-side(8px);模態置中 = fade+zoom+slide-center(Dialog/FileViewer);邊緣抽屜 = slide-edge 100%、正當無 zoom(Sheet)。統一的是**時長/曲線/reduced-motion 守衛**(motion-reduce:animate-none 全 7 浮層),非幾何原型(對齊 Material standard-vs-emphasized / Carbon productive-vs-expressive tier 分層)。
121
114
 
122
115
  **a11y**:prefers-reduced-motion 下 `motion-reduce:animate-none` 全 7 浮層統一關進出場動畫(overlay-motion SSOT 保證,無漏)。
116
+
117
+ ## 被引用(auto-maintained,Dim 3 reciprocal audit)
118
+
119
+ > 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
120
+
121
+ - `hover-card.spec.md`
@@ -64,7 +64,7 @@ Tailwind 提供 `opacity-{5/10/20/30/40/50/60/70/80/90/95}` 百分比 ladder,但
64
64
 
65
65
  ## 消費者
66
66
 
67
- 直接套用 `opacity-disabled` 的元件(grep 驗證 2026-07-05;Textarea 已全面 token-swap 化移除):Avatar(Field disabled context self-dim)、Button(`aria-disabled` 分支)、MenuItem、Rating(disabled / loading 整塊)、Steps、Switch、DataTable(drag source 半透 + 整表 refresh loading reuse,經 `lib/drag-visual.ts`)。
67
+ 直接套用 `opacity-disabled` 的元件(grep 驗證 2026-07-16;Textarea 已全面 token-swap 化移除):Avatar(Field disabled context self-dim)、Button(`aria-disabled` 分支)、MenuItem、Rating(disabled / loading 整塊)、Steps、Switch、DataTable(drag source 半透 + 整表 refresh loading reuse,經 `lib/drag-visual.ts`)、PersonDisplay(disabled 整塊,`person-display.tsx:137`)。
68
68
 
69
69
  非消費者(曾誤列):Slider / Sidebar 走灰階 token swap;Chip 無 opacity 用例;Field wrapper blanket 已移除(Avatar self-dim 取代,見 field-wrapper.tsx 註解)。
70
70
 
@@ -44,7 +44,7 @@ benchmark:
44
44
 
45
45
  **Why**:Material 3「Key Colors & Tones」+ Atlassian「Full color spectrum」共識 — palette 是設計師調色盤 SSOT,缺中間階就斷層,新元件需消費中間色時不該 hot-add。**全色階是 capability,不是 cost**。
46
46
 
47
- **Regex**:`^--color-(amber|blue|brown|red|green|deep-orange|grey|indigo|lime|orange|pink|purple|teal|yellow|cyan)-\d+$`
47
+ **Regex**:`^--color-(amber|blue|red|green|deep-orange|indigo|lime|orange|purple|yellow)-\d+$`
48
48
 
49
49
  ### 2. Magenta / Turquoise 專用 palette(14 token,structural)
50
50
 
@@ -40,6 +40,8 @@ Typography 由三個獨立維度組成,分開疊加:
40
40
  | `text-caption` | 12px | 1.3 | 圖表附註、標籤文字、元件內次要說明 |
41
41
  | `text-footnote`| 10px | 1.3 | 法律文字、來源標注(極少用)|
42
42
 
43
+ > **10px a11y 用途限制**(2026-07-17):`text-footnote` 僅限**非必要的附帶資訊**(法律細字、來源標注、storybook 註解)——目前全庫零正式元件消費,只用於 stories `<p>` 註解。**使用者需理解才能操作的資訊禁止只靠 10px**;任何 ≤12px 用途上線前須驗 200% 放大(WCAG 1.4.4)不裁切。視覺標題 ≠ 語義標題(一律用 `h1–h6`,WCAG 1.3.1)。
44
+
43
45
  **h5(16px)vs body-lg(16px)/ h6(14px)vs body(14px)**:同尺寸配對的統一判別法——line-height 區分角色(標題 1.3 / 段落 1.5),使用端再疊 font-weight / color 強化標題層級,需有意識地選擇(詳「同尺寸但不同角色的選法」)。
44
46
 
45
47