@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
@@ -1,4 +1,4 @@
1
- // @benchmark-cited: Polaris / Material / Ant 共識 principles canonical(UsageGuidance integrated)
1
+ // @benchmark-unverified-blanket: file-level retraction per M22 (d) — 產品架構參照(Linear / Notion / Figma / GitHub / Slack / Gmail 等)為 usage 觀察,非逐句 URL-cited;視為 unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.
2
2
  import type { Meta, StoryObj } from '@storybook/react'
3
3
  import LinkTo from '@storybook/addon-links/react'
4
4
 
@@ -62,11 +62,11 @@ export const UsageGuidance: Story = {
62
62
  <section>
63
63
  <h2 className="text-h4 mb-2">Consumer 紀律</h2>
64
64
  <ul className="text-body space-y-1">
65
- <li>❌ 禁:`&lt;AppShell&gt;` 內塞另一個 `&lt;AppShell&gt;`(整頁框架單例)</li>
66
- <li>❌ 禁:`sidebar={'{'}&lt;div&gt;...&lt;/div&gt;{'}'}`應傳 `&lt;Sidebar&gt;` primitive(型別收 ReactNode 不機械強制,2026-06-10 拍板)</li>
67
- <li>❌ 禁:`header={'{'}&lt;header&gt;raw&lt;/header&gt;{'}'}` → 應傳 `&lt;ChromeHeader&gt;` 或 header-canonical 派生(同上,不機械強制)</li>
68
- <li>❌ 禁:`&lt;AppShell.Main&gt;` 強制 padding(違 layoutSpace 規則 1B)</li>
69
- <li>✅ 必:Main 內容遵循既有 `layoutSpace.spec.md` 6 條規則(Page header / Card / DataTable / naked list 各按規則)</li>
65
+ <li>❌ 禁:`&lt;AppShell&gt;` 內塞另一個 `&lt;AppShell&gt;`(整頁框架只能有一個)</li>
66
+ <li>❌ 禁:`sidebar` 傳裸 `&lt;div&gt;`必傳 `&lt;Sidebar&gt;`,才能維持一致的展開/收合、行動裝置抽屜、鍵盤操作與視覺;傳其他容器雖然型別過得了,卻會失去這些導覽行為</li>
67
+ <li>❌ 禁:`header` 傳裸 `&lt;header&gt;` → 應傳 `&lt;ChromeHeader&gt;`(或沿用同一套標準標頭結構自訂),才能得到一致的標頭高度、內距與對齊</li>
68
+ <li>❌ 禁:硬性撐開 Main 區域的內距 Main 本身不加留白,由內容自己決定(自帶邊框的卡片、資料表各自帶內距)</li>
69
+ <li>✅ 必:Main 內容的間距沿用既有版面間距原則——頁面標頭、卡片、資料表、純列表各按對應規則處理</li>
70
70
  </ul>
71
71
  </section>
72
72
  </div>
@@ -81,37 +81,37 @@ export const LayoutModeRule: Story = {
81
81
  render: () => (
82
82
  <div className="prose max-w-2xl space-y-6 px-[var(--layout-space-loose)] py-[var(--layout-space-tight)]">
83
83
  <section>
84
- <h2 className="text-h4 mb-2">唯一判準:Header scope</h2>
84
+ <h2 className="text-h4 mb-2">唯一判準:頂部標頭服務的範圍</h2>
85
85
  <p className="text-body mb-2">
86
- 唯一 distinguisher = <strong>Header scope 是 local 還是 global</strong>,
87
- <em>不是</em> workspace 多寡(Notion / Gmail 都支援 multi-workspace,卻分屬不同派)。
88
- 問自己:頂部那條 bar 服務的是「當前這一頁」還是「整個 app」?
86
+ 唯一的區分標準 = <strong>頂部標頭服務的是「目前這一頁」還是「整個產品」</strong>,
87
+ <em>不是</em>工作區(workspace)的數量多寡(Notion / Gmail 都支援多工作區,卻分屬不同派)。
88
+ 問自己:頂部那條標頭列服務的是當前頁面,還是整個產品?
89
89
  </p>
90
90
  <ul className="text-body space-y-1">
91
91
  <li>
92
- <strong>primary-sidebar</strong>:頂部是 <strong>local toolbar</strong> —
93
- 服務當前頁(breadcrumb / page-level actions / filter)。Sidebar 頂天立地,WorkspaceBrand sidebar 頂。
92
+ <strong>primary-sidebar</strong>:頂部是<strong>服務當前頁的工具列</strong> —
93
+ 放麵包屑、頁面層級操作、篩選。Sidebar 頂天立地,工作區品牌(`WorkspaceBrand`)放在 sidebar 頂。
94
94
  參考 Linear / Notion / Figma。
95
95
  </li>
96
96
  <li>
97
- <strong>primary-header</strong>:多一條 <strong>global bar</strong> 橫跨頂部
98
- 服務整個 app(account / workspace switcher / 跨頁 search / notifications);local toolbar
99
- <em>仍在</em> main col 頂,只是上面多了 global header。WorkspaceBrand 改放 globalHeader 左側。
97
+ <strong>primary-header</strong>:多一條<strong>橫跨頂部、服務整個產品的全域標頭</strong> —
98
+ 放帳號、工作區切換、跨頁搜尋、通知;服務當前頁的工具列
99
+ <em>仍在</em>主欄頂部,只是上面多了這條全域標頭。工作區品牌改放全域標頭(`globalHeader`)左側。
100
100
  參考 GitHub / Slack / Gmail。完整組合見{' '}
101
101
  <LinkTo kind="Design System/Components/AppShell/展示" name="主標頭佈局 — 全域+本地兩層(GitHub/Gmail/Slack 派)"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">展示 → 主標頭佈局 — 全域+本地兩層</span></LinkTo>
102
102
  </li>
103
103
  </ul>
104
104
  <p className="text-caption text-fg-secondary mt-2">
105
- 兩 mode 是 product 角色表態——啟動時固定,不該在 runtime 切換。視覺對照圖見{' '}
105
+ 兩種模式是產品的角色定位——啟動時就固定,不該在執行期切換。視覺對照圖見{' '}
106
106
  <LinkTo kind="Design System/Components/AppShell/設計規格" name="兩種布局模式對照圖"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">設計規格 → 兩種布局模式對照圖</span></LinkTo>
107
107
  </p>
108
108
  </section>
109
109
 
110
110
  <section>
111
- <h2 className="text-h4 mb-2">常見誤解:global header 取代 local toolbar</h2>
111
+ <h2 className="text-h4 mb-2">常見誤解:全域標頭會取代當前頁工具列</h2>
112
112
  <p className="text-body">
113
- 錯。primary-header = primary-sidebar 的所有東西 + <strong>額外一條</strong> global header 在頂;
114
- `header` slot(local toolbar)<strong>仍然存在</strong>。GitHub / Slack / Gmail 全部同時保有兩層,
113
+ 錯。primary-header = primary-sidebar 的所有東西 + <strong>額外一條</strong>全域標頭在頂;
114
+ 當前頁工具列(`header` slot)<strong>仍然存在</strong>。GitHub / Slack / Gmail 全部同時保有兩層,
115
115
  不是二選一。
116
116
  </p>
117
117
  </section>
@@ -129,9 +129,9 @@ export const LayoutModeRule: Story = {
129
129
  <h2 className="text-h4 mb-2">帳號入口(個人設定)跟著 mode 走(只能出現一次)</h2>
130
130
  <ul className="text-body space-y-1">
131
131
  <li>• <strong>primary-sidebar</strong>:帳號 / 個人設定放 Sidebar 底部(`&lt;SidebarFooter&gt;`)— Linear / Notion / Figma</li>
132
- <li>• <strong>primary-header</strong>:帳號入口改放 globalHeader 右側 avatar(品牌左、帳號右,左右對稱),sidebar 不放 user footer — GitHub / Gmail / Slack 帳號一律在 global bar 右上</li>
132
+ <li>• <strong>primary-header</strong>:帳號入口改放 globalHeader 右側 avatar(品牌左、帳號右,左右對稱),sidebar 不放帳號頁尾 — GitHub / Gmail / Slack 帳號一律在全域標頭右上</li>
133
133
  <li>• 開「個人資料 / 設定 / 登出」帳號選單(`&lt;DropdownMenu&gt;`),<strong>不用 ProfileCard</strong>(ProfileCard 是看別人的人員卡,預設動作 Chat/通話用在自己身上不對)</li>
134
- <li>• 帳號 avatar = 24px,跟左側品牌 avatar 同尺寸(header-canonical 4.5 chrome header avatar);右側邊距與品牌距分割線對稱</li>
134
+ <li>• 帳號 avatar = 24px,跟左側品牌 avatar 同尺寸(沿用標準標頭的頭像尺寸規範);右側邊距與品牌距分割線對稱</li>
135
135
  <li>❌ 禁:`primary-header` mode 同時在 globalHeader + sidebar footer 各放一份 = 入口混淆(帳號入口視覺 SSOT,只能出現一次)</li>
136
136
  </ul>
137
137
  </section>
@@ -67,6 +67,10 @@ benchmark:
67
67
 
68
68
  Sub-component:`<AppShellAside title={...} width={400}>`(width consumer 自決 — `number` 或 `{ md, xl }` breakpoint-keyed,clamp `min-width: 240` / `max-width: 640`;**title prop required**,modal mode 走 Sheet → `aria-labelledby` 強制,per `sheet.spec.md`「禁止事項」(無 title → aria-labelledby 強制))。
69
69
 
70
+ **Aside body padding(內容自帶,刻意 Pattern B)**:`<AppShellAside>` 的 body(ScrollArea 內)**不自帶 px/py**,由 consumer 塞入的 children 自行負責留白(對齊 `layoutSpace.spec.md` 規則 1B「父層/內容管 padding」的裸結構路線,同 Table / DataTable)。**理由**:aside 內容異質(清單需 hover 貼邊滿版、表格需 flush、表單才需 padding),框架統一加 padding 反而破壞清單/表格。與 Dialog / Sheet body 自帶 `px-loose py-tight` 的差異是**刻意**的(那兩者內容較單一)。consumer 放需留白的內容時,自帶 `px-[var(--layout-space-loose)] py-[var(--layout-space-tight)]`(範例 `app-shell.stories.tsx` 即如此)。
71
+
72
+ **Aside 開關控制(刻意單向,2026-07-17 user 拍板)**:`asideOpen` / `onAsideOpenChange` 提供完整 **controlled** 路徑;uncontrolled fallback 初值**刻意固定關閉**(`useState(false)`),**不提供 `defaultAsideOpen`**。理由:側邊詳情面板實務上幾乎一定由 app 的 route / 選取狀態驅動(modal mode 更明文要求 controlled),初值固定關閉是合理預設,額外加 `defaultAsideOpen` 偏 YAGNI。對齊 Dim 26 dual-mode canonical 的「刻意單一模式 → 補 rationale」例外機制。未來真有 uncontrolled 初值需求再加(additive,不破壞)。
73
+
70
74
  ### Hook export:`useAppShell()`(2026-05-21 D2 codify per Phase B codex catch)
71
75
 
72
76
  Public compound API hook,讓 consumer 自拼 custom aside / 自管 modal layout(對齊 Radix `useDialogContext` / MUI `useFormControl` / Mantine `useAppShellContext` 慣例)。
@@ -68,7 +68,7 @@ const ISSUE_COLUMNS = [
68
68
  *
69
69
  * Per codex Layer B D3(2026-05-20):row-driven Aside trigger 才符合 Linear/Notion/Jira/Airtable
70
70
  * production 真實情境。row 右側 dedicated action button(`Button variant="text" size="xs" iconOnly`
71
- * per data-table.spec L193 canonical)→ click 開 Aside + 被選 issue active visual(pressed prop)。
71
+ * per data-table.spec L153 rowActions canonical)→ click 開 Aside + 被選 issue active visual(pressed prop)。
72
72
  * AppShell header toggle 降級為 secondary show/hide 不是主入口。
73
73
  */
74
74
  function IssuesView({ selectedId, asideOpen, onSelectIssue }: { selectedId?: string; asideOpen: boolean; onSelectIssue: (issue: Issue) => void }) {
@@ -118,7 +118,6 @@ function IssuesView({ selectedId, asideOpen, onSelectIssue }: { selectedId?: str
118
118
  </PopoverTrigger>
119
119
  <PopoverContent align="end" className="w-auto p-0">
120
120
  <DataTableFilterPanel
121
- mode="flat"
122
121
  columns={ISSUE_COLUMNS as any}
123
122
  value={filterTree}
124
123
  onChange={setFilterTree}
@@ -150,7 +149,7 @@ function IssuesView({ selectedId, asideOpen, onSelectIssue }: { selectedId?: str
150
149
  </div>
151
150
  </div>
152
151
  {/* DataTable:naked structure,layoutSpace 規則 1B 父層 mx 對齊 chrome 內容左右邊界。
153
- rowActions:per data-table.spec L193 canonical(Button text xs iconOnly,固定 24px)+
152
+ rowActions:per data-table.spec L153 rowActions canonical(Button text xs iconOnly,固定 24px)+
154
153
  pressed={row.id === selectedId} 顯示當前選中(per codex D3 active visual)。 */}
155
154
  <div className="flex-1 min-h-0 mx-[var(--layout-space-loose)] mb-[var(--layout-space-loose)]">
156
155
  <DataTable
@@ -226,7 +225,7 @@ function IssueDetail({ issue }: { issue: Issue | null }) {
226
225
  <Tag size="sm" color={PRIORITY_COLOR[issue.priority]}>{issue.priority}</Tag>
227
226
  </div>
228
227
  </div>
229
- <DescriptionList direction="horizontal" divided>
228
+ <DescriptionList orientation="horizontal" divided>
230
229
  <DescriptionItem label="Assignee">{issue.assignee}</DescriptionItem>
231
230
  <DescriptionItem label="Due">{issue.due}</DescriptionItem>
232
231
  </DescriptionList>
@@ -374,7 +373,7 @@ export const PrimarySidebarWithTabs: Story = {
374
373
  /**
375
374
  * primary-header mode(GitHub / Gmail / Slack 派)— global header 橫跨頂部 + local header 仍在 main col 頂。
376
375
  *
377
- * @usage-ref: app-shell.spec.md L74(primary-header 2-layer model)
376
+ * @usage-ref: app-shell.spec.md「primary-header = primary-sidebar + 一條 global header」段(2-layer model)
378
377
  * @usage-consumes: ChromeHeader + Sidebar viewportInsetTop + GlobalHeader + PageHeader + IssuesView
379
378
  *
380
379
  * 2026-05-21 v2 per user clarification「primary-header = primary-sidebar + 一條 global header」:
@@ -375,7 +375,7 @@ export const avatarMeta = {
375
375
  // — item-anatomy AVATAR_SIZE 查表 / ProfileCard 40 等),無 discrete tier → 兩欄留空為 intentional 非漏填
376
376
  variants: {},
377
377
  sizes: {},
378
- states: ['default', 'focus-visible', 'disabled'], // 2026-06-11 R2:本身無自有 hover/active(spec L279;hoverCard 互動屬 HoverCard),
378
+ states: ['default', 'focus-visible', 'disabled'], // 2026-06-11 R2:本身無自有 hover/active(spec「StateBehavior 範疇」段;hoverCard 互動屬 HoverCard),
379
379
  tokens: {
380
380
  bg: ['bg-surface-raised'],
381
381
  fg: ['--foreground', '--on-emphasis'],
@@ -379,14 +379,14 @@ Badge 是**計數 / 狀態視覺指示器**,本身**非互動元件**:
379
379
  - Badge 通常依附在互動元件上(Button / Nav Item),那些元件有自己的 state——Badge 隨父層視覺連動,不擁有獨立 state。
380
380
  - 計數變化(`count` 更新)屬資料更新不是 UI state 切換。
381
381
 
382
- 對應 anatomy story:保留 `Overview` + `Inspector` + `ColorMatrix` + `SizeMatrix`。
382
+ 對應 anatomy story:保留 `Overview` + `Inspector` + `ColorMatrix` + `SizeMatrix` + `Accessibility`。
383
383
 
384
384
  ---
385
385
 
386
386
  ## 相關
387
387
 
388
388
  - `../Tag/tag.spec.md` — 分類標籤、狀態標記(Badge vs Tag 的詳細對照在本 spec 定位段落)
389
- - `../Avatar/avatar.spec.md` — `badgeCount` 內部消費 `<Badge variant="critical">`(遵循本 spec);`status` 是 Avatar 自有 presence SVG(非 Badge 系統),兩者可並存,詳該 spec
389
+ - `../Avatar/avatar.spec.md` — `badgeCount` 內部消費 `<Badge variant="critical">`(遵循本 spec);`status` 是 Avatar 自有 presence dot(內部 `span` + inline backgroundColor,非 Badge 系統),兩者可並存,詳該 spec
390
390
  - `../Button/button.spec.md` — iconOnly Button + Badge overlay 通知 icon 的組合模式
391
391
  - `../Chip/chip.spec.md` — 可互動 filter(不是 Badge 的用途)
392
392
  - `../CircularProgress/circular-progress.spec.md` — Loading 狀態指示(取代 Spinner)
@@ -342,19 +342,23 @@ export const StateBehavior: Story = {
342
342
  <Breadcrumb>
343
343
  <BreadcrumbList>
344
344
  <BreadcrumbItem>
345
- <BreadcrumbLink href="#">Link(預設 fg-secondary)</BreadcrumbLink>
345
+ <BreadcrumbLink href="#">團隊空間</BreadcrumbLink>
346
346
  </BreadcrumbItem>
347
347
  <BreadcrumbSeparator />
348
348
  <BreadcrumbItem>
349
- <BreadcrumbLink href="#">Hover 我(→ primary-hover)</BreadcrumbLink>
349
+ <BreadcrumbLink href="#">設計系統</BreadcrumbLink>
350
350
  </BreadcrumbItem>
351
351
  <BreadcrumbSeparator />
352
352
  <BreadcrumbItem>
353
- <BreadcrumbPage>Current Page(foreground, 不可點)</BreadcrumbPage>
353
+ <BreadcrumbPage>元件規格</BreadcrumbPage>
354
354
  </BreadcrumbItem>
355
355
  </BreadcrumbList>
356
356
  </Breadcrumb>
357
357
  </div>
358
+ <p className="text-footnote text-fg-muted mt-3">
359
+ 「團隊空間」「設計系統」為可點連結(預設 fg-secondary,移上去 hover 轉 primary-hover);
360
+ 末項「元件規格」為當前頁(foreground,不可點)。
361
+ </p>
358
362
  </div>
359
363
  </div>
360
364
  ),
@@ -18,7 +18,7 @@ import {
18
18
  DropdownMenuContent,
19
19
  DropdownMenuItem,
20
20
  } from '@/design-system/components/DropdownMenu/dropdown-menu'
21
- import { Tabs, TabsList, TabsTrigger } from '@/design-system/components/Tabs/tabs'
21
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/design-system/components/Tabs/tabs'
22
22
  import { Button } from '@/design-system/components/Button/button'
23
23
 
24
24
  const meta: Meta = {
@@ -159,6 +159,9 @@ export const UsageGuidance: Story = {
159
159
  <TabsTrigger value="customers">顧客</TabsTrigger>
160
160
  <TabsTrigger value="products">產品</TabsTrigger>
161
161
  </TabsList>
162
+ <TabsContent value="orders" className="text-body text-fg-muted mt-2">訂單列表、篩選與批次出貨</TabsContent>
163
+ <TabsContent value="customers" className="text-body text-fg-muted mt-2">顧客名單、分群與生命週期價值</TabsContent>
164
+ <TabsContent value="products" className="text-body text-fg-muted mt-2">商品目錄、庫存與上下架狀態</TabsContent>
162
165
  </Tabs>
163
166
  </div>
164
167
  </Rule>
@@ -190,7 +190,7 @@ export const DeclarativeAutoCollapse: Story = {
190
190
  <Breadcrumb>
191
191
  <BreadcrumbList
192
192
  items={[
193
- { label: 'Long Organization Name', href: '/org' },
193
+ { label: 'Global Platform Infrastructure Group', href: '/org' },
194
194
  { label: 'Product Engineering Team', href: '/team' },
195
195
  { label: 'Design System Component Refactor Sprint 23' },
196
196
  ]}
@@ -46,7 +46,7 @@ export const Overview: Story = {
46
46
  <Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
47
47
  </>
48
48
  }
49
- onClear={() => {}}
49
+ onClearSelection={() => {}}
50
50
  />
51
51
  </div>
52
52
  </section>
@@ -87,7 +87,7 @@ export const Overview: Story = {
87
87
  <BulkActionBar
88
88
  selection={Array.from({ length: 50 }, (_, i) => `f-${i}`)}
89
89
  actions={<Button variant="tertiary" size="md" startIcon={Download}>下載</Button>}
90
- onClear={() => {}}
90
+ onClearSelection={() => {}}
91
91
  />
92
92
  </div>
93
93
  </div>
@@ -115,7 +115,7 @@ function SelectionStateDemo() {
115
115
  <Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
116
116
  </>
117
117
  }
118
- onClear={() => setCount(0)}
118
+ onClearSelection={() => setCount(0)}
119
119
  />
120
120
  {count === 0 && <span className="text-caption text-fg-muted italic px-3 py-2 inline-block">↑ count=0 → BulkActionBar 不渲染</span>}
121
121
  </div>
@@ -162,7 +162,7 @@ function InspectorInner() {
162
162
  <Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
163
163
  </>
164
164
  }
165
- onClear={() => setSelectionCount(0)}
165
+ onClearSelection={() => setSelectionCount(0)}
166
166
  />
167
167
  {selectionCount === 0 && <span className="text-caption text-fg-muted italic px-3 py-2 inline-block">selection=0 → 不渲染</span>}
168
168
  </div>
@@ -196,7 +196,7 @@ export const Accessibility: Story = {
196
196
  <section>
197
197
  <H3>鍵盤(預期 consumer 在 page 層級監聽)</H3>
198
198
  <ul className="list-disc list-inside text-caption text-fg-secondary space-y-1">
199
- <li><kbd>Esc</kbd> → 觸發 <code>onClear()</code>(consumer 在 page-level keydown 監聽)</li>
199
+ <li><kbd>Esc</kbd> → 觸發 <code>onClearSelection()</code>(consumer 在 page-level keydown 監聽)</li>
200
200
  <li>Tab 序:close → actions(count 是純文字 / aria 朗讀對象,非 tab stop)</li>
201
201
  <li>Hint CTA 是 <code>{`<button>`}</code>,鍵盤可達</li>
202
202
  </ul>
@@ -216,7 +216,7 @@ export const Accessibility: Story = {
216
216
  <Button variant="tertiary" size="md" startIcon={Trash2} danger disabled>刪除(無權限)</Button>
217
217
  </>
218
218
  }
219
- onClear={() => {}}
219
+ onClearSelection={() => {}}
220
220
  />
221
221
  </div>
222
222
  </section>
@@ -83,7 +83,7 @@ export const UsageGuidance: Story = {
83
83
  <Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
84
84
  </>
85
85
  }
86
- onClear={() => {}}
86
+ onClearSelection={() => {}}
87
87
  />
88
88
  <Label>↑ selection {`>`} 0 浮現,= 0 完全藏</Label>
89
89
  </Rule>
@@ -160,7 +160,7 @@ export const ActionVariantRule: Story = {
160
160
  <Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
161
161
  </>
162
162
  }
163
- onClear={() => {}}
163
+ onClearSelection={() => {}}
164
164
  />
165
165
  <Label>↑ tertiary 主 / tertiary danger 破壞性</Label>
166
166
  </Rule>
@@ -177,7 +177,7 @@ export const ActionVariantRule: Story = {
177
177
  <Button variant="primary" size="md" startIcon={Trash2} danger>刪除</Button>
178
178
  </>
179
179
  }
180
- onClear={() => {}}
180
+ onClearSelection={() => {}}
181
181
  />
182
182
  <Label warn>↑ 兩顆都 primary → 視覺重量過高,跟 page Submit 競爭</Label>
183
183
  </Rule>
@@ -211,7 +211,7 @@ export const HintBannerRule: Story = {
211
211
  <BulkActionBar
212
212
  selection={Array.from({ length: 50 }, (_, i) => `f-${i}`)}
213
213
  actions={<Button variant="tertiary" size="md">下載</Button>}
214
- onClear={() => {}}
214
+ onClearSelection={() => {}}
215
215
  />
216
216
  </div>
217
217
  </div>
@@ -222,7 +222,19 @@ export const HintBannerRule: Story = {
222
222
  title="❌ 自刻 hint banner / 用 contrast 底色 div"
223
223
  note="banner 是 Alert 的職責(自帶 a11y role / aria-live / 視覺一致性)。自刻會破壞跟其他 Alert 的視覺一致性,也漏 a11y"
224
224
  >
225
- <Label warn>(範例省略)有 Alert 元件不用是浪費</Label>
225
+ <div className="border border-divider rounded-md overflow-hidden">
226
+ <div className="bg-info-subtle text-info-text px-[var(--layout-space-loose)] py-[var(--layout-space-tight)] text-footnote">
227
+ 已選取本頁全部 50 個。點此選取全部 5370 個項目
228
+ </div>
229
+ <div className="border-t border-divider">
230
+ <BulkActionBar
231
+ selection={Array.from({ length: 50 }, (_, i) => `f-${i}`)}
232
+ actions={<Button variant="tertiary" size="md">下載</Button>}
233
+ onClearSelection={() => {}}
234
+ />
235
+ </div>
236
+ </div>
237
+ <Label warn>↑ 手刻 div 當 banner:無 role=&quot;status&quot; / aria-live,SR 不會朗讀狀態變化;底色跟其他 Alert 不一致;「點此選取」是純文字非可操作連結</Label>
226
238
  </Rule>
227
239
 
228
240
  <Rule
@@ -233,7 +245,7 @@ export const HintBannerRule: Story = {
233
245
  selection={['a', 'b', 'c']}
234
246
  hiddenByFilter={2}
235
247
  actions={<Button variant="tertiary" size="md" startIcon={Archive}>封存</Button>}
236
- onClear={() => {}}
248
+ onClearSelection={() => {}}
237
249
  />
238
250
  <Label>↑ count 文字 inline:「已選 3 項 · 2 個被 filter 隱藏」</Label>
239
251
  </Rule>
@@ -81,9 +81,9 @@ benchmark:
81
81
 
82
82
  ### Slot
83
83
 
84
- - **`actions`**:consumer 提供 **md** Buttons(size rationale 見「Size canonical」);`variant=tertiary`(主)/ `tertiary danger`(destructive)— **不用 primary**( dialog 確認最終 action)
84
+ - **`actions`**:consumer 提供 **md** Buttons(size rationale 見「Size canonical」);**一律 `variant=tertiary`**( destructive — `Trash` 等 icon + label 已辨識危險)— **不用 primary、destructive 不上 danger 紅填色**(2026-07-17 user 拍板:批次刪除鈕點下去是**開確認 dialog**非直接刪,紅色強調留給 dialog 內確認最終不可逆 action;對齊 UX Movement / GitLab Pajamas「紅留給不可逆最終確認」+ button.spec.md「Inline destructive 不用高強調」)
85
85
  - **count 區**:`已選 {N} 項`(內建)+ inline filter hidden status `· {M} 個被 filter 隱藏`(`hiddenByFilter` prop 傳入時)
86
- - **clear**:`<Button iconOnly size=md variant=text dismiss />`(內建,觸發 `onClear`)
86
+ - **clear**:`<Button iconOnly size=md variant=text dismiss />`(內建,觸發 `onClearSelection`)
87
87
 
88
88
  #### Size canonical(2026-05-04 升 SSOT)
89
89
 
@@ -143,7 +143,7 @@ interface BulkActionBarProps extends React.HTMLAttributes<HTMLDivElement> {
143
143
  /** 已選 ID;length === 0 且無 totalSelected 時自動隱藏(回 null,完整判準見「Extend dataset pattern」) */
144
144
  selection: readonly string[]
145
145
  /** Clear 觸發,user 點 X icon(consumer 在 page-level 監聽 Esc 觸發) */
146
- onClear?: () => void
146
+ onClearSelection?: () => void
147
147
  /** 批次 actions(consumer 提供 md Button,variant=tertiary 或 tertiary+danger,不用 primary) */
148
148
  actions?: React.ReactNode
149
149
  /** Filter 模式:hidden 數量,顯示在 count 區 inline 「已選 {N} 項 · {M} 個被 filter 隱藏」 */
@@ -169,6 +169,16 @@ interface BulkActionBarLabels {
169
169
 
170
170
  **Hint banner(擴 dataset 提示)不在本 API**:由 consumer 用 `<Alert variant="info" placement="fixed">` 配 ReactNode title 帶 inline link 自組,黏在 BulkActionBar 上方。Alert / Notice 的 `title` + `description` 已支援 ReactNode(2026-04-28)。
171
171
 
172
+ ### 批次操作進行中(loading / 防重複提交 / 選取保留)
173
+
174
+ `actions` 由 consumer 注入 ReactNode,**批次操作的非同步生命週期由 consumer 擁有**(BulkActionBar 是 stateless 呈現層,不介入 action 執行):
175
+
176
+ - **Loading**:非同步 action(批次刪除 / 匯出)進行中,對應 action Button 傳 `loading`(既有 API,spinner + 自動 disabled)——不在 bar 內另加全域 spinner
177
+ - **防重複提交**:同一批次操作進行中,consumer 對該 action Button 設 `loading` / `disabled` 阻止連點觸發多次(語義同 `../Field/form-validation.spec.md` double-submit 防護,但 owner 在 consumer 而非 bar)
178
+ - **選取保留**:操作進行中 `selection` 維持不變、bar 持續可見;**成功**後才由 consumer 清空 `selection`(觸發 bar 自動隱藏),**失敗**則保留選取讓 user 重試
179
+
180
+ 對齊 Polaris BulkActions(action `loading` 由 consumer 提供)/ Material DataGrid batch action 慣例。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
181
+
172
182
  ### Extend dataset pattern(totalSelected)
173
183
 
174
184
  「本頁全選 → hint 點擊 → 擴選整個 dataset」2-step 後,consumer 把 `totalSelected` 設為 dataset 真總數,count 區改顯示該值(否則 fallback `selection.length`)——避免 Alert 顯「已選 5370」但 bar 仍顯「已選 50」的不同步(2026-05-13 ship)。對齊 Gmail / Linear / Notion 全選 dataset hint pattern。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
@@ -183,7 +193,7 @@ interface BulkActionBarLabels {
183
193
  - count 文字用 `aria-live="polite"` + `aria-atomic="true"`(selection 變更時 SR 整句重讀「已選 3 項」)
184
194
  - Clear button:`aria-label="清除選取"`
185
195
  - Hint banner 用 `role="status"` + `aria-live="polite"`(state 切換時通知)
186
- - 鍵盤:Esc → `onClear()`(consumer 應監聽 page-level keydown 觸發);Tab 序按 DOM 順序 = clear(X)→ actions(count 是純文字 span,非互動元素 → 不參與 Tab)
196
+ - 鍵盤:Esc → `onClearSelection()`(consumer 應監聽 page-level keydown 觸發);Tab 序按 DOM 順序 = clear(X)→ actions(count 是純文字 span,非互動元素 → 不參與 Tab)
187
197
  - Disabled action(無權限等)用 Button `disabled` + tooltip 解釋,**不藏 action**(避免 user 困惑)
188
198
 
189
199
  ---
@@ -194,7 +204,7 @@ interface BulkActionBarLabels {
194
204
  - **底色**:**無底色 contrast**,跟 page 同色(`bg-canvas` / `bg-surface` 視 placement 繼承)。對齊 Notion / Linear minimalist — 用文字內容切換呈現「mode」,**不**用底色 highlight。**不像 Polaris 那種顯著底色變化** <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
195
205
  - **邊界**:**無外框邊界**(融入 page chrome)— 恆 **`border-top` border-divider 切割 layout**(bar 是 page 結構,不是 floating overlay,不用 box-shadow 製造「浮層」誤導)。top-toolbar 變體為未來項(見「Size canonical」)
196
206
  - **與 table 的關係**:inline composition — bar 接在 DataTable 下方,toolbar 永遠保留(見「Placement」)
197
- - **Action variant**:`tertiary`(主)+ `tertiary danger`(destructive)— **不用 primary**(留給 dialog 確認最終 action)
207
+ - **Action variant**:**一律 `tertiary`**(含 destructive,icon+label 辨識危險)— **不用 primary、不用 danger 紅填色**(紅色強調留給 dialog 內確認最終 action,見「Slot」rationale)
198
208
 
199
209
  ---
200
210
 
@@ -21,13 +21,13 @@ export const Default: Story = {
21
21
  return (
22
22
  <BulkActionBar
23
23
  selection={selection}
24
- onClear={() => setSelection([])}
24
+ onClearSelection={() => setSelection([])}
25
25
  actions={
26
26
  <>
27
27
  <Button variant="tertiary" size="md" startIcon={Archive}>封存</Button>
28
28
  <Button variant="tertiary" size="md" startIcon={TagIcon}>加標籤</Button>
29
29
  <Button variant="tertiary" size="md" startIcon={MoveRight}>移動</Button>
30
- <Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
30
+ <Button variant="tertiary" size="md" startIcon={Trash2}>刪除</Button>
31
31
  </>
32
32
  }
33
33
  />
@@ -35,26 +35,10 @@ export const Default: Story = {
35
35
  },
36
36
  }
37
37
 
38
- // Filter 模式:hidden 數量顯示在 count inline
39
- export const WithFilterHidden: Story = {
40
- name: '部分項目被篩選隱藏',
41
- render: () => {
42
- const [selection, setSelection] = useState<string[]>(['issue-1', 'issue-2', 'issue-3'])
43
- return (
44
- <BulkActionBar
45
- selection={selection}
46
- onClear={() => setSelection([])}
47
- hiddenByFilter={2}
48
- actions={
49
- <>
50
- <Button variant="tertiary" size="md" startIcon={Archive}>封存</Button>
51
- <Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
52
- </>
53
- }
54
- />
55
- )
56
- },
57
- }
38
+ // @story-trait-rationale: WithFilterHidden(selection=3 + hiddenByFilter=2)retired 2026-07-17 per audit Dim 24 —
39
+ // principles.stories.tsx HintBannerRule 第三 Rule 已用同情境(selection 3 + hiddenByFilter=2)且多教「filter hidden
40
+ // inline 進 count 區、不開 hint banner」規則;展示版無新增約束(earn-existence 2-test 雙 NO)→ retire。
41
+ // hiddenByFilter inline 呈現 canonical home = principles HintBannerRule。
58
42
 
59
43
  // Hint banner via Alert primitive(擴 dataset 提示)— 對齊 ref 圖
60
44
  // hint banner 唯一 trigger condition:本頁全選 + 還有 dataset 沒選到
@@ -108,11 +92,11 @@ export const WithExtendDatasetHint: Story = {
108
92
  <BulkActionBar
109
93
  selection={selection}
110
94
  totalSelected={allSelected ? TOTAL : undefined}
111
- onClear={() => { setSelection([]); setAllSelected(false) }}
95
+ onClearSelection={() => { setSelection([]); setAllSelected(false) }}
112
96
  actions={
113
97
  <>
114
98
  <Button variant="tertiary" size="md" startIcon={Download}>下載</Button>
115
- <Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
99
+ <Button variant="tertiary" size="md" startIcon={Trash2}>刪除</Button>
116
100
  </>
117
101
  }
118
102
  />
@@ -41,7 +41,7 @@ export interface BulkActionBarProps extends React.HTMLAttributes<HTMLDivElement>
41
41
  /** 已選 ID,length === 0 時自動隱藏(回傳 null) */
42
42
  selection: readonly string[]
43
43
  /** Clear 觸發,user 點 X icon 或 Esc(consumer 在 page-level 監聽) */
44
- onClear?: () => void
44
+ onClearSelection?: () => void
45
45
  /** 批次 actions(consumer 提供 md Button,variant=tertiary 或 tertiary+danger;不用 primary) */
46
46
  actions?: React.ReactNode
47
47
  /** Filter 模式:hidden 數量,顯示在 count 區 inline「已選 {N} 項 · {M} 個被 filter 隱藏」 */
@@ -77,7 +77,7 @@ export interface BulkActionBarProps extends React.HTMLAttributes<HTMLDivElement>
77
77
 
78
78
  const BulkActionBar = React.forwardRef<HTMLDivElement, BulkActionBarProps>(
79
79
  function BulkActionBar(
80
- { selection, onClear, actions, hiddenByFilter, totalSelected, labels: labelsOverride, className, ...props },
80
+ { selection, onClearSelection, actions, hiddenByFilter, totalSelected, labels: labelsOverride, className, ...props },
81
81
  ref
82
82
  ) {
83
83
  const labels: BulkActionBarLabels = React.useMemo(
@@ -109,7 +109,7 @@ const BulkActionBar = React.forwardRef<HTMLDivElement, BulkActionBarProps>(
109
109
  {/* X close — md dismiss(2026-05-04 spec update:default placement = footer variant,
110
110
  visual weight 對齊 Dialog footer commitment buttons md;same-row consistency 維持)
111
111
  未來若有 top-toolbar variant(覆蓋 sm-density toolbar)→ 該 variant override sm */}
112
- {onClear && (
112
+ {onClearSelection && (
113
113
  <Button
114
114
  variant="text"
115
115
  size="md"
@@ -117,7 +117,7 @@ const BulkActionBar = React.forwardRef<HTMLDivElement, BulkActionBarProps>(
117
117
  dismiss
118
118
  startIcon={X}
119
119
  aria-label={labels.clear}
120
- onClick={onClear}
120
+ onClick={onClearSelection}
121
121
  />
122
122
  )}
123
123
 
@@ -23,14 +23,14 @@ import { ButtonGroupContext } from './button'
23
23
  * </ButtonGroup>
24
24
  *
25
25
  * // 垂直排列(fullWidth 自動套用)
26
- * <ButtonGroup direction="vertical">
26
+ * <ButtonGroup orientation="vertical">
27
27
  * <Button variant="primary">確認</Button>
28
28
  * <Button variant="tertiary">取消</Button>
29
29
  * </ButtonGroup>
30
30
  */
31
31
  interface ButtonGroupProps extends React.HTMLAttributes<HTMLDivElement> {
32
32
  /** 排列方向,預設 horizontal */
33
- direction?: 'horizontal' | 'vertical'
33
+ orientation?: 'horizontal' | 'vertical'
34
34
  /** 水平對齊,預設 start */
35
35
  align?: 'start' | 'center' | 'end'
36
36
  }
@@ -40,8 +40,8 @@ const BUTTON_GROUP_CTX_VERTICAL = { fullWidth: true } as const
40
40
  const BUTTON_GROUP_CTX_HORIZONTAL = { fullWidth: false } as const
41
41
 
42
42
  const ButtonGroup = React.forwardRef<HTMLDivElement, ButtonGroupProps>(
43
- ({ direction = 'horizontal', align = 'start', className, children, ...props }, ref) => {
44
- const isVertical = direction === 'vertical'
43
+ ({ orientation = 'horizontal', align = 'start', className, children, ...props }, ref) => {
44
+ const isVertical = orientation === 'vertical'
45
45
 
46
46
  return (
47
47
  <ButtonGroupContext.Provider value={isVertical ? BUTTON_GROUP_CTX_VERTICAL : BUTTON_GROUP_CTX_HORIZONTAL}>
@@ -102,6 +102,23 @@ const VARIANT_DESC: Record<VariantKey, string> = {
102
102
  link: '外觀像連結的按鈕,不用於操作列',
103
103
  }
104
104
 
105
+ // variant 名稱只放在左側 mono 標籤欄;按鈕本體用真實動作文案(禁 variant 名當 label)
106
+ const VARIANT_LABEL: Record<VariantKey, string> = {
107
+ primary: '新增任務',
108
+ secondary: '儲存變更',
109
+ tertiary: '取消',
110
+ text: '略過此步',
111
+ link: '查看說明',
112
+ }
113
+
114
+ const DANGER_LABEL: Record<VariantKey, string> = {
115
+ primary: '刪除專案',
116
+ secondary: '移除成員',
117
+ tertiary: '清除紀錄',
118
+ text: '撤銷授權',
119
+ link: '刪除',
120
+ }
121
+
105
122
  /* ═══════════════════════════════════════════════════════════════════════════
106
123
  Shared UI Components
107
124
 
@@ -224,7 +241,8 @@ export const Overview = {
224
241
  <div className="flex flex-col gap-2">
225
242
  {VARIANTS.map((v) => (
226
243
  <div key={v} className="flex items-center gap-4">
227
- <div className="w-28 shrink-0"><Button variant={v} size="sm" startIcon={Plus}>{v}</Button></div>
244
+ <span className="w-16 shrink-0 text-[11px] font-mono text-fg-muted">{v}</span>
245
+ <div className="w-28 shrink-0"><Button variant={v} size="sm" startIcon={Plus}>{VARIANT_LABEL[v]}</Button></div>
228
246
  <span className="text-caption text-fg-secondary">{VARIANT_DESC[v]}</span>
229
247
  </div>
230
248
  ))}
@@ -232,7 +250,8 @@ export const Overview = {
232
250
  <span className="text-[11px] text-fg-muted font-medium">+ danger prop</span>
233
251
  {DANGER_VARIANTS.map((v) => (
234
252
  <div key={v} className="flex items-center gap-4">
235
- <div className="w-28 shrink-0"><Button variant={v} danger size="sm" startIcon={Trash2}>{v}</Button></div>
253
+ <span className="w-16 shrink-0 text-[11px] font-mono text-fg-muted">{v}</span>
254
+ <div className="w-28 shrink-0"><Button variant={v} danger size="sm" startIcon={Trash2}>{DANGER_LABEL[v]}</Button></div>
236
255
  <span className="text-caption text-fg-secondary">
237
256
  {v === 'primary' ? '立即不可逆——點下去就執行' : v === 'secondary' ? '有警示意圖,後面還有確認' : '低強調的危險操作'}
238
257
  </span>
@@ -503,7 +522,7 @@ export const ColorMatrix = {
503
522
  <td className="p-3 border-b border-divider font-mono text-caption font-medium align-top">{v}</td>
504
523
  {STATES.map((st) => (
505
524
  <td key={st} className="p-3 border-b border-divider align-top min-w-[160px]">
506
- <Button variant={v} size="sm" startIcon={Plus} disabled={st === 'disabled'}>{v}</Button>
525
+ <Button variant={v} size="sm" startIcon={Plus} disabled={st === 'disabled'}>{VARIANT_LABEL[v]}</Button>
507
526
  <TokenAnnotation colors={TOKEN_MAP[v][st]} />
508
527
  </td>
509
528
  ))}
@@ -523,7 +542,7 @@ export const ColorMatrix = {
523
542
  <td className="p-3 border-b border-divider font-mono text-caption font-medium align-top">{v} + danger</td>
524
543
  {STATES.map((st) => (
525
544
  <td key={st} className="p-3 border-b border-divider align-top min-w-[160px]">
526
- <Button variant={v} danger size="sm" startIcon={Trash2} disabled={st === 'disabled'}>{v}</Button>
545
+ <Button variant={v} danger size="sm" startIcon={Trash2} disabled={st === 'disabled'}>{DANGER_LABEL[v]}</Button>
527
546
  <TokenAnnotation colors={DANGER_MAP[v]![st]} />
528
547
  </td>
529
548
  ))}
@@ -612,10 +631,10 @@ export const SizeMatrix = {
612
631
  <tr key={v}>
613
632
  <Td mono>{v}</Td>
614
633
  {SIZES.map((sz) => (
615
- <Td key={sz}><Button variant={v} size={sz} startIcon={Plus}>{v}</Button></Td>
634
+ <Td key={sz}><Button variant={v} size={sz} startIcon={Plus}>{VARIANT_LABEL[v]}</Button></Td>
616
635
  ))}
617
636
  {SIZES.map((sz) => (
618
- <Td key={`io-${sz}`}><Button variant={v} size={sz} iconOnly startIcon={Plus} aria-label={v} /></Td>
637
+ <Td key={`io-${sz}`}><Button variant={v} size={sz} iconOnly startIcon={Plus} aria-label={VARIANT_LABEL[v]} /></Td>
619
638
  ))}
620
639
  </tr>
621
640
  ))}
@@ -661,7 +680,7 @@ export const StateBehavior = {
661
680
  <div className="flex flex-wrap gap-3">
662
681
  {VARIANTS.map((v) => (
663
682
  <div key={v} className="flex flex-col items-center gap-1">
664
- <Button variant={v} disabled startIcon={Plus}>{v}</Button>
683
+ <Button variant={v} disabled startIcon={Plus}>{VARIANT_LABEL[v]}</Button>
665
684
  <span className="text-[10px] text-fg-muted">{v}</span>
666
685
  </div>
667
686
  ))}