@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
@@ -74,64 +74,57 @@ export const UsageGuidance: Story = {
74
74
  export const RuleMinuteStepForMeetings: Story = {
75
75
  name: '會議時段用 15 分鐘間隔',
76
76
  render: () => (
77
- <div className="flex gap-8">
78
- <div className="flex flex-col gap-1.5">
79
- <h3 className="text-caption font-medium text-foreground">✅ minuteStep=15</h3>
80
- <Field>
81
- <FieldLabel>專案週會時間</FieldLabel>
82
- <TimePicker value="09:15" onChange={() => {}} minuteStep={15} />
83
- </Field>
84
- </div>
85
- <div className="flex flex-col gap-1.5">
86
- <h3 className="text-caption font-medium text-fg-muted">❌ 預設 minuteStep=1(會議排程無意義)</h3>
87
- <Field>
88
- <FieldLabel>專案週會時間</FieldLabel>
89
- <TimePicker value="09:07" onChange={() => {}} />
90
- </Field>
77
+ <div className="flex flex-col gap-4">
78
+ <p className="text-caption text-fg-secondary max-w-prose">
79
+ 會議排程以 15 分鐘為粒度是世界級慣例(Google Calendar / Outlook / Notion Calendar
80
+ 的時間選單預設都是 15)。用預設 minuteStep=1 會讓使用者困在挑「9:07 還是 9:08」,
81
+ 失去會議排程的本質——這類情境一律設 minuteStep=15。
82
+ </p>
83
+ <div className="flex gap-8">
84
+ <div className="flex flex-col gap-1.5">
85
+ <h3 className="text-caption font-medium text-foreground">✅ minuteStep=15</h3>
86
+ <Field>
87
+ <FieldLabel>專案週會時間</FieldLabel>
88
+ <TimePicker value="09:15" onChange={() => {}} minuteStep={15} />
89
+ </Field>
90
+ </div>
91
+ <div className="flex flex-col gap-1.5">
92
+ <h3 className="text-caption font-medium text-fg-muted">❌ 預設 minuteStep=1(會議排程無意義)</h3>
93
+ <Field>
94
+ <FieldLabel>專案週會時間</FieldLabel>
95
+ <TimePicker value="09:07" onChange={() => {}} />
96
+ </Field>
97
+ </div>
91
98
  </div>
92
99
  </div>
93
100
  ),
94
101
  }
95
102
 
96
- /**
97
- * Rule:Range 語意用兩個 TimePicker 組合
98
- * TimePicker MVP 不內建 Range(見 spec「為何無 Range」)——對齊 Ant composition 思路,
99
- * consumer 用兩個 TimePicker + arrow 達成營業時段 / 會議時段等 range 場景。
100
- */
101
- export const RuleRangeComposition: Story = {
102
- name: '時間範圍用兩個 TimePicker 組合,不內建',
103
- render: () => {
104
- const [open, setOpen] = React.useState('10:00')
105
- const [close, setClose] = React.useState('22:00')
106
- return (
107
- <Field>
108
- <FieldLabel>營業時段(兩個 TimePicker + →)</FieldLabel>
109
- <div className="flex items-center gap-2">
110
- <TimePicker value={open} onChange={setOpen} />
111
- <span className="text-fg-muted">→</span>
112
- <TimePicker value={close} onChange={setClose} />
113
- </div>
114
- </Field>
115
- )
116
- },
117
- }
103
+ // Dim 24 retire(2026-07-16):原 RuleRangeComposition(營業時段雙 TimePicker + →)與「展示」層
104
+ // 的「店家營業時間」(ShopBusinessHours)near-identical(同值 10:00/22:00、同結構),且其規則
105
+ // (TimePicker 不內建 Range,用兩個並列組合)已由本頁 UsageGuidance「何時不用」條目文字說明。
106
+ // 重複範例退役;範圍組合的 live demo 見「展示 店家營業時間」(UsageGuidance LinkTo 指向)。
118
107
 
119
108
  /**
120
109
  * 規則:清除用 X 行內動作,不要用文字按鈕
121
- * 設定 clearable 後,TimePicker 會自動在欄位尾端渲染一個 X 行內動作來清空值,
122
- * 點擊即清空。不要自己擺一顆「清除」文字按鈕——統一用 X 圖示符合全站慣例。
123
110
  */
124
111
  export const RuleClearNoLabelButton: Story = {
125
112
  name: '清除用 X 行內動作,不用文字按鈕',
126
113
  render: () => {
127
114
  const [t, setT] = React.useState<string>('14:30')
128
115
  return (
129
- <div className="flex flex-col gap-1.5">
130
- <h3 className="text-caption font-medium text-foreground">✅ clearable=true(自動渲染 X 行內動作)</h3>
131
- <Field>
132
- <FieldLabel>提醒時間</FieldLabel>
133
- <TimePicker value={t} onChange={setT} clearable />
134
- </Field>
116
+ <div className="flex flex-col gap-4">
117
+ <p className="text-caption text-fg-secondary max-w-prose">
118
+ 設定 clearable 後,TimePicker 會自動在欄位尾端渲染一個 X 行內動作來清空值,點擊即清空。
119
+ 不要自己擺一顆「清除」文字按鈕——全站統一用 X 圖示表達「移除已填內容」,與其他欄位的清除慣例一致。
120
+ </p>
121
+ <div className="flex flex-col gap-1.5">
122
+ <h3 className="text-caption font-medium text-foreground">✅ clearable=true(自動渲染 X 行內動作)</h3>
123
+ <Field>
124
+ <FieldLabel>提醒時間</FieldLabel>
125
+ <TimePicker value={t} onChange={setT} clearable />
126
+ </Field>
127
+ </div>
135
128
  </div>
136
129
  )
137
130
  },
@@ -80,7 +80,7 @@ open 軸同樣只開最小 API — **uncontrolled-only**:`defaultOpen`(初始開
80
80
  value={time} // ISO "HH:mm" or "HH:mm:ss" | null
81
81
  onChange={setTime}
82
82
  size="sm" | "md" | "lg" // 對齊 field-height family,default md
83
- mode="edit" | "display" | "readonly" | "disabled" // display = 純展示(DataTable cell 用)
83
+ mode="edit" | "view" | "readonly" | "disabled" // view = 純展示值(DataTable cell 用)
84
84
  disabled={boolean}
85
85
  error={boolean}
86
86
  clearable={boolean}
@@ -105,7 +105,7 @@ open 軸同樣只開最小 API — **uncontrolled-only**:`defaultOpen`(初始開
105
105
 
106
106
  ### 顯示格式化
107
107
 
108
- **Display 走 `Intl.DateTimeFormat`**(跨 locale 統一、12h/24h 支援)。`formatOptions` 透傳進去。
108
+ **View 走 `Intl.DateTimeFormat`**(跨 locale 統一、12h/24h 支援)。`formatOptions` 透傳進去。
109
109
 
110
110
  ---
111
111
 
@@ -199,7 +199,7 @@ Panel 展開後的 column picker 結構:
199
199
  詳見 `../Field/field-controls.spec.md`(共用規則)。
200
200
 
201
201
  ### Empty 值
202
- `value={null}` / `value=""` / `value=undefined` 都視為空,trigger 顯示 `placeholder`。Display 模式空值顯示半形 `-`(對齊 `EMPTY_DISPLAY`,text-foreground)。
202
+ `value={null}` / `value=""` / `value=undefined` 都視為空,trigger 顯示 `placeholder`。View 模式空值顯示半形 `-`(對齊 `EMPTY_DISPLAY`,text-foreground)。
203
203
 
204
204
  ### 驗證時機
205
205
  - **每次欄位選取(時 / 分 / 秒任一改變)當下即 commit value 給 `onChange`**(eager commit,非 OK 才送)。OK 鈕(「確定」)只負責關閉 Panel,不另行 commit。`此刻` 按鈕則一次 commit 當前時間並關閉 Panel
@@ -214,6 +214,7 @@ N/A — TimePicker 是純同步輸入,無 async 狀態。
214
214
  ## A11y 預設
215
215
 
216
216
  - trigger `role="combobox"`,在 `<Field>` 內時以 `aria-labelledby` 指向 field label(`fieldCtx.labelId`)
217
+ - **Standalone(無 `<Field>` 包裹)accessible name**:trigger 無預設 `aria-label`,`<Field>` 外使用時 consumer **必須**自帶 `aria-label` 或 `aria-labelledby`,否則 `role="combobox"` 成為無名稱控件(對齊 Select / Combobox / DatePicker 同家族 standalone 約定)。DS 不為 trigger 塞泛用 fallback name(如「時間」),避免遮蔽 consumer 的具體語意(如「會議開始時間」);Field 內則自動接線無需 consumer 介入。Panel 本體(Popover dialog)另有本 DS 提供的 `aria-label="選擇時間"`,與 trigger name 職責分離
217
218
  - Panel 開啟時 trigger `aria-expanded="true"` + `aria-haspopup="dialog"`
218
219
  - 每欄 `role="listbox"`(`aria-label` 為 `hours` / `minutes` / `seconds`),item `role="option" aria-selected`
219
220
  - Screen reader 經 trigger 的 label + 內層可見值文字朗讀目前選取時間(目前值以可見 `<span>` 呈現,無額外 `aria-valuetext`)
@@ -272,9 +273,9 @@ trigger 的互動狀態(focus / invalid / disabled / readonly)完全繼承 `../F
272
273
 
273
274
  ## 邊界案例
274
275
 
275
- - **Disabled**:Field SSOT own;trigger 自動 disabled(`text-fg-disabled` + 不開 picker),Display mode + disabled 維持時間格式但 token disabled。
276
+ - **Disabled**:Field SSOT own;trigger 自動 disabled(`text-fg-disabled` + 不開 picker)。顯式 `mode` 優先於 `disabled` prop(`useResolvedFieldMode` prop > disabled),view 分支無 disabled token 邏輯——要 disabled 視覺走 `mode="disabled"`(readonly/disabled 分支 `text-fg-disabled`,時間格式維持)
276
277
  - **Loading**:TimePicker 為 sync UI(time math 在 client),無 loading state。極端 case(後端 disabled-times list)應由 consumer 先 disable trigger 直到 fetch 完成。
277
- - **Empty(no value)**:`value=null` 為合法 initial state,trigger 顯 placeholder;**未傳 `placeholder` 時 default = 格式遮罩**(`HH:MM`,showSeconds=true 時 `HH:MM:SS`),非固定文案。`null` + Display mode 顯半形 `-`(hyphen + `text-foreground`,disabled → fg-disabled)對齊 Input display empty 慣例。
278
+ - **Empty(no value)**:`value=null` 為合法 initial state,trigger 顯 placeholder;**未傳 `placeholder` 時 default = 格式遮罩**(`HH:MM`,showSeconds=true 時 `HH:MM:SS`),非固定文案。`null` + view mode 顯半形 `-`(hyphen + `text-foreground`;`mode="disabled"` → fg-disabled)對齊 Input view empty 慣例。
278
279
  - **Empty(disabled all times)**:極端場景(`disabledHours` / `disabledMinutes` 覆蓋全範圍),panel column 全 disabled,鍵盤焦點停留無導覽目標。
279
280
  - **Panel 開啟中外部 value 變更**:controlled 單向資料流——欄位選中即時反映新 value,並 scroll-into-view(`behavior:'smooth'`,mount 後的變更;見「Spacing + 結構」的 Scroll-into-view 條)。
280
281
  - **Dark mode / density**:走 Field + Popover SSOT 自動 adapt;panel column item 由 MenuItem SSOT 控 density。
@@ -27,7 +27,7 @@ export default meta
27
27
 
28
28
  type Story = StoryObj<typeof TimePicker>
29
29
 
30
- // @story-trait-rationale: pre-existing trait gaps tracked separately; this PR scope = add Modes story with display card.
30
+ // @story-trait-rationale: pre-existing trait gaps tracked separately; this PR scope = add Modes story with view card.
31
31
  /* ── 四模式 ── */
32
32
  export const Modes: Story = {
33
33
  name: '四模式',
@@ -38,8 +38,8 @@ export const Modes: Story = {
38
38
  <TimePicker value="14:30" onChange={() => {}} aria-label="會議時段(edit mode demo)" />
39
39
  </div>
40
40
  <div>
41
- <h3 className="text-body font-bold text-foreground mb-2">display</h3>
42
- <TimePicker mode="display" value="14:30" aria-label="會議時段(display mode demo)" />
41
+ <h3 className="text-body font-bold text-foreground mb-2">view</h3>
42
+ <TimePicker mode="view" value="14:30" aria-label="會議時段(view 模式示範)" />
43
43
  </div>
44
44
  <div>
45
45
  <h3 className="text-body font-bold text-foreground mb-2">readonly</h3>
@@ -63,7 +63,7 @@ export interface TimeFormatOptions {
63
63
  }
64
64
 
65
65
  // 2026-07-06 D3 perf:Intl.DateTimeFormat 是標準庫最貴 constructor 之一(locale data
66
- // resolution)。formatTime 無 cheap path,display/readonly/edit 每 render 必建;DataTable time 欄
66
+ // resolution)。formatTime 無 cheap path,view/readonly/edit 每 render 必建;DataTable time 欄
67
67
  // 轉發 config 時每 cell × 每次表格 re-render 重建。module-level cache 重用實例(DateTimeFormat
68
68
  // 無狀態可安全重用),key = locale|JSON.stringify(options)。行為 Δ=0。
69
69
  const dtfCache = new Map<string, Intl.DateTimeFormat>()
@@ -109,7 +109,10 @@ export interface TimePickerProps
109
109
  extends TimeFormatOptions,
110
110
  Omit<
111
111
  React.HTMLAttributes<HTMLDivElement>,
112
- 'onChange' | 'placeholder'
112
+ // 'defaultValue' 明確排除:本元件 controlled-only(time-picker.spec.md「Controlled-only
113
+ // rationale」),不支援 uncontrolled 初始值 — 若不 Omit,HTMLAttributes 會讓 defaultValue
114
+ // 通過型別卻靜默無效(不初始化 draft),與 spec contract 矛盾。
115
+ 'onChange' | 'placeholder' | 'defaultValue'
113
116
  > {
114
117
  mode?: FieldMode
115
118
  /** Field chrome variant. Default = context.variant ?? 'default'. Per-prop override. */
@@ -141,9 +144,9 @@ export interface TimePickerProps
141
144
  */
142
145
  endIcon?: LucideIcon | null
143
146
  /**
144
- * Display 是否渲 endIcon + Field naked wrapper(D-path opt-in,2026-05-08)
145
- * — DataTable cell display↔edit 像素級對齊用。預設 false(裸 span,backward compat)。
146
- * 設 true 時 display 也走 fieldWrapperStyles(naked variant)+ ItemSuffix Clock,
147
+ * View 態是否渲 endIcon + Field naked wrapper(D-path opt-in,2026-05-08)
148
+ * — DataTable cell view↔edit 像素級對齊用。預設 false(裸 span,backward compat)。
149
+ * 設 true 時 view 也走 fieldWrapperStyles(naked variant)+ ItemSuffix Clock,
147
150
  * 與 edit 同 DOM 結構,消除 Layer-B padding mismatch。
148
151
  */
149
152
  showDisplayEndIcon?: boolean
@@ -188,7 +191,7 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
188
191
  const size = useResolvedFieldSize(sizeProp) // B 組 cascade fix
189
192
  const error = useResolvedFieldInvalid(errorProp)
190
193
  const disabled = useResolvedFieldDisabled(disabledProp)
191
- // 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode="display"> 漏 cascade
194
+ // 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode="view"> 漏 cascade
192
195
  const resolvedMode = useResolvedFieldMode({ mode, disabled })
193
196
  const emptyDisplay = useFieldEmptyDisplay()
194
197
  const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
@@ -252,21 +255,25 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
252
255
  setOpen(false)
253
256
  }
254
257
 
255
- // mode='display'(Phase B2 2026-05-05):純內容輸出 — 對齊原 TimePickerDisplay sub-component(retired)。
258
+ // mode='view'(Phase B2 2026-05-05):純內容輸出 — 對齊原 TimePickerDisplay sub-component(retired)。
256
259
  // Default(showDisplayEndIcon=false):無 Field wrapper / 無 Clock icon — backward compat 裸 span。
257
260
  // Opt-in(showDisplayEndIcon=true,2026-05-08 D-path):Field naked wrapper + ItemSuffix Clock,
258
- // 與 edit 同結構消除 cell display↔edit 像素偏移(Layer-B padding mismatch)。
259
- if (resolvedMode === 'display') {
261
+ // 與 edit 同結構消除 cell view↔edit 像素偏移(Layer-B padding mismatch)。
262
+ if (resolvedMode === 'view') {
263
+ // 2026-07-16 DA3 fix:view 分支補 {...props} spread(aria-label 等 DOM attr 原本靜默丟失;
264
+ // Switch 同型 2026-07-04 已修,本檔漏 — M10 同型)。
260
265
  if (!showDisplayEndIcon) {
261
- // 2026-05-14 I2 fix(spec contract (e) display typography canonical):bare span 套
266
+ // 2026-05-14 I2 fix(spec contract (e) view typography canonical):bare span 套
262
267
  // `fieldDisplayTextClass(size)`(sm/md→text-body,lg→text-body-lg)— 對齊 Field family 統一。
263
- if (!value) return <span className={cn(fieldDisplayTextClass(size), fieldEmptyColorClass(resolvedMode), className)}>{emptyDisplay}</span>
264
- return <span className={cn(fieldDisplayTextClass(size), 'truncate', className)}>{formatTime(value, { formatOptions, locale })}</span>
268
+ if (!value) return <span {...props} ref={ref as React.Ref<HTMLSpanElement>} className={cn(fieldDisplayTextClass(size), fieldEmptyColorClass(resolvedMode), className)}>{emptyDisplay}</span>
269
+ return <span {...props} ref={ref as React.Ref<HTMLSpanElement>} className={cn(fieldDisplayTextClass(size), 'truncate', className)}>{formatTime(value, { formatOptions, locale })}</span>
265
270
  }
266
271
  return (
267
272
  <div
268
- className={cn(fieldWrapperStyles({ mode: 'display', variant, size }), className)}
269
- data-field-mode="display"
273
+ ref={ref}
274
+ {...props}
275
+ className={cn(fieldWrapperStyles({ mode: 'view', variant, size }), className)}
276
+ data-field-mode="view"
270
277
  >
271
278
  <span className={cn(bareInputStyles, 'flex-1 min-w-0 truncate', !value && fieldEmptyColorClass(resolvedMode))}>
272
279
  {value ? formatTime(value, { formatOptions, locale }) : emptyDisplay}
@@ -284,6 +291,9 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
284
291
  if (!isEditable) {
285
292
  return (
286
293
  <div
294
+ // 2026-07-16 DA3 fix:非-edit 分支補 ref 轉發(forwardRef<HTMLDivElement> 原本只有 edit
295
+ // 與 bare-span view 接 ref,readonly/disabled 及 endIcon-view div 靜默丟失 — 同 view props 漏接)。
296
+ ref={ref}
287
297
  className={cn(fieldWrapperStyles({ mode: resolvedMode, variant: variant, size }), className)}
288
298
  data-field-mode={resolvedMode}
289
299
  aria-disabled={resolvedMode === 'disabled' ? true : undefined}
@@ -45,7 +45,7 @@ export const UsageGuidance: Story = {
45
45
  <LinkTo kind="Design System/Components/Toast/展示" name="有標題與描述"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">有 Title + Description</span></LinkTo>
46
46
  </li>
47
47
  <li>
48
- <LinkTo kind="Design System/Components/Toast/展示" name="互動測試"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">互動測試</span></LinkTo>
48
+ <LinkTo kind="Design System/Components/Toast/展示" name="點擊觸發通知"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">點擊觸發通知</span></LinkTo>
49
49
  </li>
50
50
  </ul>
51
51
  <p className="text-fg-muted mt-3">判斷不確定時:先看下方「何時用 / 何時不用」的對照;若訊息重要到使用者必須看到、或需要使用者確認,就不該用 Toast,改用 Alert(持久顯示)或 Dialog(阻斷確認)。</p>
@@ -116,7 +116,7 @@ export const WithDescription = {
116
116
  }
117
117
 
118
118
  export const Interactive = {
119
- name: '互動測試',
119
+ name: '點擊觸發通知',
120
120
  render: () => (
121
121
  <div className="flex flex-col gap-4">
122
122
  <span className="text-caption text-fg-muted">點按鈕觸發 Toast</span>
@@ -245,9 +245,9 @@ const InspectorInner = () => {
245
245
  <div className="px-16 py-16 rounded-lg bg-canvas border border-divider flex items-center justify-center">
246
246
  <Tooltip defaultOpen>
247
247
  <TooltipTrigger asChild>
248
- <Button variant="secondary" startIcon={Settings} size="sm">觸發器</Button>
248
+ <Button variant="secondary" startIcon={Settings} size="sm">欄位設定</Button>
249
249
  </TooltipTrigger>
250
- <TooltipContent side={side}>提示文字</TooltipContent>
250
+ <TooltipContent side={side}>調整資料表的欄位顯示與排序</TooltipContent>
251
251
  </Tooltip>
252
252
  </div>
253
253
 
@@ -351,7 +351,15 @@ export const Inspector = {
351
351
 
352
352
  export const PlacementReference = {
353
353
  name: '方向與間距',
354
- render: () => (
354
+ render: () => {
355
+ // 四方向以工具列常見操作示範,方位由外部 mono 標籤標示,Tooltip 提供對應說明。
356
+ const placementDemo: Record<SideKey, { label: string; tip: string }> = {
357
+ top: { label: '複製連結', tip: '複製此頁面的公開分享連結' },
358
+ right: { label: '封存專案', tip: '封存後移至封存區,隨時可還原' },
359
+ bottom: { label: '重新整理', tip: '重新載入最新資料' },
360
+ left: { label: '更多操作', tip: '檢視全部可用的操作' },
361
+ }
362
+ return (
355
363
  <div className="flex flex-col gap-8">
356
364
  <div className="flex flex-col gap-1">
357
365
  <H3>四方向 Placement</H3>
@@ -365,9 +373,9 @@ export const PlacementReference = {
365
373
  <div className="px-8 py-8 rounded-lg bg-canvas border border-divider flex items-center justify-center w-full">
366
374
  <Tooltip defaultOpen>
367
375
  <TooltipTrigger asChild>
368
- <Button variant="tertiary" size="sm">{s}</Button>
376
+ <Button variant="tertiary" size="sm">{placementDemo[s].label}</Button>
369
377
  </TooltipTrigger>
370
- <TooltipContent side={s}>提示文字</TooltipContent>
378
+ <TooltipContent side={s}>{placementDemo[s].tip}</TooltipContent>
371
379
  </Tooltip>
372
380
  </div>
373
381
  </div>
@@ -397,7 +405,8 @@ export const PlacementReference = {
397
405
  <Desc>transform-origin 使用 --radix-tooltip-content-transform-origin,zoom 效果從觸發器方向展開。</Desc>
398
406
  </div>
399
407
  </div>
400
- ),
408
+ )
409
+ },
401
410
  }
402
411
 
403
412
  // ── Accessibility ─────────────────────────────────────────────────────────
@@ -52,7 +52,7 @@ export const UsageGuidance: Story = {
52
52
  <LinkTo kind="Design System/Components/Tooltip/展示" name="非 Button 元素"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">非 Button 元素</span></LinkTo>
53
53
  </li>
54
54
  <li>
55
- <LinkTo kind="Design System/Components/Tooltip/展示" name="方向"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">方向</span></LinkTo>
55
+ <LinkTo kind="Design System/Components/Tooltip/展示" name="基本用法"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">基本用法</span></LinkTo>
56
56
  </li>
57
57
  <li>
58
58
  <LinkTo kind="Design System/Components/Tooltip/展示" name="長文字"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">長文字</span></LinkTo>
@@ -189,13 +189,13 @@ export const NeedsTooltipRule: Story = {
189
189
  title="✅ 截斷文字用 tooltip 顯示完整內容(僅當實際被截斷時)"
190
190
  note="Tooltip 是資訊補救 — 文字被 truncate 時才顯示完整內容。沒被截斷就不該顯示 tooltip(避免過度互動)"
191
191
  >
192
- <Tooltip>
192
+ <Tooltip defaultOpen>
193
193
  <TooltipTrigger asChild>
194
194
  <div className="w-32 truncate px-3 py-1 border border-border rounded-md cursor-default">
195
- 非常長的專案名稱會被截斷
195
+ 2026 亞洲市場品牌改版與官網重構
196
196
  </div>
197
197
  </TooltipTrigger>
198
- <TooltipContent>非常長的專案名稱會被截斷顯示完整版</TooltipContent>
198
+ <TooltipContent>2026 亞洲市場品牌改版與官網重構</TooltipContent>
199
199
  </Tooltip>
200
200
  <Label>↑ 模式示意(此靜態範例恆顯示;實務上應偵測文字真被截斷 `scrollWidth &gt; clientWidth` 時才掛 tooltip)</Label>
201
201
  </Rule>
@@ -55,7 +55,11 @@ Tooltip 的判斷標準是:**畫面上的資訊是否已經足夠讓使用者
55
55
 
56
56
  ## 與 Button icon-only 的關係
57
57
 
58
- Button 的 `iconOnly` 模式已內建自動 tooltip(以 `aria-label` 驅動),開發者不需要額外包 `<Tooltip>`。只有在非 Button 元素需要 tooltip 時,才手動使用 Tooltip 元件。
58
+ Button 的 `iconOnly` 模式已內建自動 tooltip(以 `aria-label` 驅動),開發者不需要額外包 `<Tooltip>`。
59
+
60
+ **例外——`iconOnly` + `asChild`**:當 `iconOnly` 同時搭配 `asChild` 時,內建 tooltip wrapper **不啟動**(Radix Slot 只接受單一 child,Button 主動補 tooltip 會破壞單 child 規則)。此時 consumer 必須**自行傳入 child 的 `aria-label`** 給螢幕閱讀器,並視需要自行包 `<Tooltip>`(見 `../Button/button.spec.md` 與 `button.tsx` 的 asChild 警告)。
61
+
62
+ 只有在非 Button 元素需要 tooltip 時,才手動使用 Tooltip 元件。
59
63
 
60
64
  ## 內容完整性
61
65
 
@@ -90,6 +94,7 @@ Tooltip 最大寬度 **280px**(見 `.tsx` 的 `max-w-[280px]`)——超過
90
94
  - **觸控裝置**:無 hover,tooltip 無法觸發——必要資訊不可只靠 tooltip 承載(見「何時不用」「禁止事項」)
91
95
  - **長文字**:超過 max-width 280px 自動換行、完整呈現不截斷(見「最大寬度」「內容完整性」)
92
96
  - **貼近 viewport 邊**:Radix 自動翻邊 + `collisionPadding` 8px 呼吸距離(見「Edge collision」)
97
+ - **空內容**:`TooltipContent` 的 `children` 為 `null` / `undefined` / `false` / 空字串時**不掛浮層**——不渲染帶 padding 的空 `role="tooltip"` 殼,trigger 原樣保留。Tooltip 是資訊補救機制(見「何時用」),沒有補充內容就不該出現。實作於 `tooltip.tsx` 的 `TooltipContent` 以空值 guard 提早 return null 落地
93
98
 
94
99
  ## 禁止事項
95
100
 
@@ -98,6 +103,7 @@ Tooltip 最大寬度 **280px**(見 `.tsx` 的 `max-w-[280px]`)——超過
98
103
  - ❌ 不要把 tooltip 當作必要資訊的唯一載體——tooltip 需要 hover 才能看到,觸控裝置無法觸發
99
104
  - ❌ 不要為個別 tooltip 設定不同的延遲時間——全產品統一一組參數
100
105
  - ❌ 不要在 tooltip 內截斷資訊——tooltip 是資訊終點,內容必須完整
106
+ - ❌ 不要對 `TooltipContent` 傳空內容——空 tooltip 是無意義的視覺雜訊(空值時元件不掛浮層,見「邊界狀態」)
101
107
 
102
108
  ---
103
109
 
@@ -105,11 +111,11 @@ Tooltip 最大寬度 **280px**(見 `.tsx` 的 `max-w-[280px]`)——超過
105
111
 
106
112
  Tooltip 是**單一職責 hover 提示 primitive**(一句話補充),刻意無變體:
107
113
 
108
- - **無 ColorMatrix**:Tooltip 固定一種視覺(dark surface + white text,跟頁面主題反轉),無 variant / severity / hue。若需要有色 hint,那是 Popover / HoverCard 的職責。Dark mode 自動由 inverse theme 處理。故無 color matrix。
114
+ - **無 ColorMatrix**:Tooltip 固定一種視覺(`--tooltip` bg + white text,內部 wrapper 強制 `data-theme="dark"`——不隨頁面主題反轉,dark mode 下 `--tooltip` 有自己的深色值),無 variant / severity / hue。若需要有色 hint,那是 Popover / HoverCard 的職責。故無 color matrix。
109
115
  - **無 SizeMatrix**:Tooltip 無 size prop,尺寸由內容 + max-width 常數決定(本 spec「最大寬度」段)。不提供 sm/md/lg tier——短文字 hint 不需要尺寸變體。
110
116
  - **無 StateBehavior**:Tooltip 是 passive 出現 / 消失(hover / focus 觸發,見「出現時機」段),無 hover / selected / active / disabled 這類互動元件 state。開 / 關行為由 Radix primitive 處理,寫在 `Overview` 的 usage 說明已足夠。
111
117
 
112
- 對應 anatomy story:保留 `Overview` + `Inspector`(delay / placement 互動試玩) + 元件特有 `PlacementReference`(4 種 side 方向對照 + side-aware 動畫表;Tooltip 走 `align="center"` 輕量浮層例外,見「Align 對齊」段,無 align 軸矩陣)。
118
+ 對應 anatomy story:保留 `Overview` + `Inspector`(side 切換 + token 對照) + 元件特有 `PlacementReference`(4 種 side 方向對照 + side-aware 動畫表;Tooltip 走 `align="center"` 輕量浮層例外,見「Align 對齊」段,無 align 軸矩陣)。
113
119
 
114
120
  ---
115
121
 
@@ -70,7 +70,7 @@ export const LongText: Story = {
70
70
  <Tooltip defaultOpen>
71
71
  <TooltipTrigger asChild>
72
72
  <Button variant="secondary" startIcon={Info}>
73
- 長文字提示
73
+ API 金鑰安全說明
74
74
  </Button>
75
75
  </TooltipTrigger>
76
76
  <TooltipContent>
@@ -23,7 +23,16 @@ const TooltipTrigger = TooltipPrimitive.Trigger
23
23
  const TooltipContent = React.forwardRef<
24
24
  React.ElementRef<typeof TooltipPrimitive.Content>,
25
25
  React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
26
- >(({ className, sideOffset = OVERLAY_SIDE_OFFSET, collisionPadding = OVERLAY_COLLISION_PADDING, style, children, ...props }, ref) => (
26
+ >(({ className, sideOffset = OVERLAY_SIDE_OFFSET, collisionPadding = OVERLAY_COLLISION_PADDING, style, children, ...props }, ref) => {
27
+ // 空內容不掛浮層:children 為 null / undefined / false / 空字串時不渲染帶 padding 的空
28
+ // role="tooltip" 殼(見 spec「邊界狀態」)。Tooltip 是資訊補救機制,無補充內容即不出現,
29
+ // trigger 由 TooltipTrigger 原樣保留。
30
+ const isEmptyContent =
31
+ children == null ||
32
+ children === false ||
33
+ (typeof children === 'string' && children.trim() === '')
34
+ if (isEmptyContent) return null
35
+ return (
27
36
  // collisionPadding default 8px:避免 tooltip 貼 viewport 邊(Radix avoidCollisions 預設 true 但 padding 0 會貼邊)
28
37
  // 消費 OVERLAY_COLLISION_PADDING overlay 家族 canonical(與 Popover 一致;HoverCard 特例 12 補 rounding)避免 viewport edge clipping
29
38
  <TooltipPrimitive.Portal>
@@ -48,7 +57,8 @@ const TooltipContent = React.forwardRef<
48
57
  <div data-theme="dark" className="contents">{children}</div>
49
58
  </TooltipPrimitive.Content>
50
59
  </TooltipPrimitive.Portal>
51
- ))
60
+ )
61
+ })
52
62
  TooltipContent.displayName = TooltipPrimitive.Content.displayName
53
63
 
54
64
  // Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)
@@ -283,11 +283,11 @@ export const ColorMatrix: Story = {
283
283
  <H3>Hover + Selected 並存的實際渲染</H3>
284
284
  <Desc>以下 `Documents/Photos/beach.jpg` 為 selected row,可 hover 任一其他 row 觀察 neutral-hover。</Desc>
285
285
  <div className="border border-border rounded-lg p-4 max-w-md">
286
- <TreeView selectedIds={selected} onSelectedChange={setSelected} defaultExpandedIds={['docs', 'photos']}>
287
- <TreeItem id="docs" label="Documents(hover 試試)" icon={Folder}>
286
+ <TreeView aria-label="色彩狀態範例檔案樹" selectedIds={selected} onSelectedChange={setSelected} defaultExpandedIds={['docs', 'photos']}>
287
+ <TreeItem id="docs" label="Documents" icon={Folder}>
288
288
  <TreeItem id="resume" label="Resume.pdf" icon={FileText} />
289
289
  <TreeItem id="photos" label="Photos" icon={Folder}>
290
- <TreeItem id="beach" label="beach.jpg(selected)" icon={Image} />
290
+ <TreeItem id="beach" label="beach.jpg" icon={Image} />
291
291
  <TreeItem id="trip" label="trip.jpg" icon={Image} />
292
292
  </TreeItem>
293
293
  </TreeItem>
@@ -307,11 +307,11 @@ export const IndentMatrix: Story = {
307
307
  <H3>indentStep = chevronSize + gap-2</H3>
308
308
  <Desc>每層 indent 剛好是 chevron(16 / 20px)+ gap-2(8px)的距離——跟 item-layout 的 prefix-content gap 一致。讓 tree indent 視覺跟 item-layout 融為一體,不是獨立數字系統。</Desc>
309
309
  <div className="border border-border rounded-lg p-4 max-w-md">
310
- <TreeView defaultExpandedIds={['l1', 'l2', 'l3']}>
311
- <TreeItem id="l1" label="L1 (depth 0)" icon={Folder}>
312
- <TreeItem id="l2" label="L2 (depth 1)" icon={Folder}>
313
- <TreeItem id="l3" label="L3 (depth 2)" icon={Folder}>
314
- <TreeItem id="l4" label="L4 (depth 3)" icon={FileText} />
310
+ <TreeView aria-label="品牌資產資料夾樹" defaultExpandedIds={['brand', 'website', 'images']}>
311
+ <TreeItem id="brand" label="品牌資產" icon={Folder}>
312
+ <TreeItem id="website" label="網站" icon={Folder}>
313
+ <TreeItem id="images" label="圖片" icon={Folder}>
314
+ <TreeItem id="hero" label="首頁橫幅.png" icon={Image} />
315
315
  </TreeItem>
316
316
  </TreeItem>
317
317
  </TreeItem>
@@ -323,15 +323,17 @@ export const IndentMatrix: Story = {
323
323
  <H3>葉節點 chevron placeholder</H3>
324
324
  <Desc>同層有展開 icon、有的沒有 → label 不會對齊。TreeView 自動給葉節點留透明 chevron placeholder,label 永遠對齊 column。</Desc>
325
325
  <div className="border border-border rounded-lg p-4 max-w-md">
326
- <TreeView defaultExpandedIds={['folder']}>
327
- <TreeItem id="folder" label="Folder(可展開)" icon={Folder}>
328
- <TreeItem id="leaf-1" label="Leaf 1(無 children)" icon={FileText} />
329
- <TreeItem id="folder-2" label="Folder 2(可展開)" icon={Folder} />
330
- <TreeItem id="leaf-3" label="Leaf 3(無 children)" icon={FileText} />
326
+ <TreeView aria-label="設計交付資料夾樹" defaultExpandedIds={['delivery']}>
327
+ <TreeItem id="delivery" label="設計交付" icon={Folder}>
328
+ <TreeItem id="spec" label="元件規格.pdf" icon={FileText} />
329
+ <TreeItem id="assets" label="切版素材" icon={Folder}>
330
+ <TreeItem id="icons" label="圖示集.svg" icon={Image} />
331
+ </TreeItem>
332
+ <TreeItem id="checklist" label="交付清單.md" icon={FileText} />
331
333
  </TreeItem>
332
334
  </TreeView>
333
335
  </div>
334
- <p className="text-footnote text-fg-muted mt-3">↑ / 資料夾 label 左側對齊 — 不會因有無 chevron 位移</p>
336
+ <p className="text-footnote text-fg-muted mt-3">↑ 葉節點(元件規格.pdf / 交付清單.md)與可展開資料夾(切版素材)label 左側對齊 — 不因有無 chevron 位移</p>
335
337
  </div>
336
338
  </div>
337
339
  ),
@@ -78,9 +78,24 @@ export const UsageGuidance: Story = {
78
78
  <Section title="何時不用 + 替代方案">
79
79
  <Rule
80
80
  title='❌ 互斥展開(同時只開一個):用 Accordion type="single"'
81
- note="TreeView 預設允許任意多個節點同時展開。若需要「選一個展開其他自動收」的互斥語意,用 Accordion(非 TreeView)"
81
+ note='TreeView 預設允許任意多個節點同時展開。平面設定分段若需要「展開一個、其他自動收合」的互斥語意,用 Accordion type="single"(非 TreeView)'
82
82
  >
83
- <Label warn>(範例省略)TreeView 是多展開,Accordion 是單展開,語意不同</Label>
83
+ <div className="border border-border rounded-lg p-3 w-80">
84
+ <TreeView aria-label="帳號設定分段" selectionMode="none" defaultExpandedIds={['account', 'notify', 'privacy']}>
85
+ <TreeItem id="account" label="帳號設定">
86
+ <TreeItem id="account-email" label="電子郵件" />
87
+ <TreeItem id="account-password" label="密碼" />
88
+ </TreeItem>
89
+ <TreeItem id="notify" label="通知偏好">
90
+ <TreeItem id="notify-email" label="信件通知" />
91
+ <TreeItem id="notify-push" label="推播通知" />
92
+ </TreeItem>
93
+ <TreeItem id="privacy" label="隱私與安全">
94
+ <TreeItem id="privacy-2fa" label="兩步驟驗證" />
95
+ </TreeItem>
96
+ </TreeView>
97
+ </div>
98
+ <Label warn>↑ 三個設定分段在 TreeView 裡可同時展開;要「展開一個自動收其他」的互斥手風琴,用 Accordion type="single"</Label>
84
99
  </Rule>
85
100
  </Section>
86
101
 
@@ -101,10 +116,10 @@ export const UsageGuidance: Story = {
101
116
  </Rule>
102
117
 
103
118
  <Rule
104
- title="❌ Sidebar 的簡單 2 層 nav:用 Sidebar 內建結構"
105
- note="Sidebar SidebarMenuSub 已處理「主項目 + 子項目」2 層結構。只有深度 3 層或 user data 樹才用 TreeView"
119
+ title="❌ 純視覺分段(設計時可列舉的固定選單):用 SidebarGroup 不用 TreeView"
120
+ note="Sidebar 主選單只接受 1 層可列舉項目——設計時就固定死、不會 runtime 新增的分段,用 SidebarGroup 純視覺分段即可。真階層 user data(專案 / 子專案、部門樹,即使只有 2 層、未來會長)才用 TreeView(見 sidebar.spec.md「判斷規則」)"
106
121
  >
107
- <Label warn>2 nav Sidebar Sub Menu 足夠,不需要 TreeView 的遞迴 overhead</Label>
122
+ <Label warn>固定分段用 SidebarGroup 就夠,不需要 TreeView 的遞迴 overhead;user data 樹則一律 TreeView</Label>
108
123
  </Rule>
109
124
  </Section>
110
125
  </div>
@@ -174,16 +189,34 @@ export const IndentRule: Story = {
174
189
  note="同層 siblings 有展開 icon、有的沒有 → label 不對齊。TreeView 自動給葉節點留 chevron 位置(透明 placeholder),consumer 不需介入"
175
190
  >
176
191
  <div className="border border-border rounded-lg p-3 w-80">
177
- <TreeView defaultExpandedIds={['fa', 'sub']}>
178
- <TreeItem id="fa" label="資料夾 A" icon={Folder}>
179
- <TreeItem id="file-a" label="檔案 a.txt" icon={FileText} />
180
- <TreeItem id="sub" label="子資料夾" icon={Folder}>
181
- <TreeItem id="file-b" label="檔案 b.txt" icon={FileText} />
192
+ <TreeView aria-label="行銷素材資料夾樹" defaultExpandedIds={['assets', 'social']}>
193
+ <TreeItem id="assets" label="行銷素材" icon={Folder}>
194
+ <TreeItem id="hero" label="活動主視覺.png" icon={Image} />
195
+ <TreeItem id="social" label="社群貼文" icon={Folder}>
196
+ <TreeItem id="story" label="限動範本.psd" icon={FileText} />
182
197
  </TreeItem>
183
198
  </TreeItem>
184
199
  </TreeView>
185
200
  </div>
186
- <Label>↑ 「檔案 a.txt」是葉,「子資料夾」可展開——兩者 label 依然垂直對齊</Label>
201
+ <Label>↑ 「活動主視覺.png」是葉,「社群貼文」可展開——兩者 label 依然垂直對齊</Label>
202
+ </Rule>
203
+
204
+ <Rule
205
+ title="❌ 混用有 icon / 無 icon 的節點"
206
+ note="Chevron 有自動佔位(leaf 也留等寬空白),但 icon 無自動佔位——同層有些節點傳 icon、有些沒傳,label 起點會直接錯開。要嘛全傳 icon,要嘛全不傳(見 spec「Icon 一致性原則」)"
207
+ >
208
+ <div className="border border-border rounded-lg p-3 w-80">
209
+ <TreeView aria-label="icon 混用反例檔案樹" defaultExpandedIds={['src', 'public']}>
210
+ <TreeItem id="src" label="src" icon={Folder}>
211
+ <TreeItem id="app" label="App.tsx" icon={FileText} />
212
+ <TreeItem id="readme" label="README.md" />
213
+ </TreeItem>
214
+ <TreeItem id="public" label="public">
215
+ <TreeItem id="favicon" label="favicon.ico" icon={Image} />
216
+ </TreeItem>
217
+ </TreeView>
218
+ </div>
219
+ <Label warn>↑ 「README.md」「public」沒傳 icon → label 起點比有 icon 的節點前移,視覺節奏錯開</Label>
187
220
  </Rule>
188
221
  </div>
189
222
  ),