@qijenchen/design-system 0.1.0-beta.84 → 0.1.0-beta.86

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 (492) 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-group.d.ts +47 -0
  50. package/dist/components/DataTable/data-table-filter-group.d.ts.map +1 -0
  51. package/dist/components/DataTable/data-table-filter-group.js +154 -0
  52. package/dist/components/DataTable/data-table-filter-group.js.map +1 -0
  53. package/dist/components/DataTable/data-table-filter-panel.d.ts +0 -2
  54. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  55. package/dist/components/DataTable/data-table-filter-panel.js +5 -328
  56. package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
  57. package/dist/components/DataTable/data-table-filter-value-picker.d.ts +29 -0
  58. package/dist/components/DataTable/data-table-filter-value-picker.d.ts.map +1 -0
  59. package/dist/components/DataTable/data-table-filter-value-picker.js +187 -0
  60. package/dist/components/DataTable/data-table-filter-value-picker.js.map +1 -0
  61. package/dist/components/DataTable/data-table-interaction-layer.d.ts +2 -2
  62. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  63. package/dist/components/DataTable/data-table-sort-manager.js +2 -2
  64. package/dist/components/DataTable/data-table-sort-manager.js.map +1 -1
  65. package/dist/components/DataTable/data-table.d.ts +1 -1
  66. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  67. package/dist/components/DataTable/data-table.js +44 -32
  68. package/dist/components/DataTable/data-table.js.map +1 -1
  69. package/dist/components/DatePicker/date-picker.d.ts +3 -3
  70. package/dist/components/DatePicker/date-picker.js +4 -4
  71. package/dist/components/DatePicker/date-picker.js.map +1 -1
  72. package/dist/components/DescriptionList/description-list.d.ts +3 -3
  73. package/dist/components/DescriptionList/description-list.d.ts.map +1 -1
  74. package/dist/components/DescriptionList/description-list.js +6 -6
  75. package/dist/components/DescriptionList/description-list.js.map +1 -1
  76. package/dist/components/Dialog/dialog.d.ts +2 -2
  77. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  78. package/dist/components/Dialog/dialog.js.map +1 -1
  79. package/dist/components/DropdownMenu/dropdown-menu.d.ts +8 -3
  80. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  81. package/dist/components/DropdownMenu/dropdown-menu.js +7 -7
  82. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  83. package/dist/components/Field/field-context.d.ts +16 -10
  84. package/dist/components/Field/field-context.d.ts.map +1 -1
  85. package/dist/components/Field/field-context.js.map +1 -1
  86. package/dist/components/Field/field-types.d.ts +2 -2
  87. package/dist/components/Field/field-types.d.ts.map +1 -1
  88. package/dist/components/Field/field-types.js.map +1 -1
  89. package/dist/components/Field/field-wrapper.d.ts +20 -4
  90. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  91. package/dist/components/Field/field-wrapper.js +30 -31
  92. package/dist/components/Field/field-wrapper.js.map +1 -1
  93. package/dist/components/Field/field.d.ts +1 -1
  94. package/dist/components/Field/field.d.ts.map +1 -1
  95. package/dist/components/Field/field.js +25 -3
  96. package/dist/components/Field/field.js.map +1 -1
  97. package/dist/components/Field/index.d.ts +1 -0
  98. package/dist/components/Field/index.d.ts.map +1 -1
  99. package/dist/components/Field/index.js +2 -0
  100. package/dist/components/Field/index.js.map +1 -1
  101. package/dist/components/Field/use-form-validation.d.ts +1 -1
  102. package/dist/components/Field/use-form-validation.js.map +1 -1
  103. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  104. package/dist/components/FileItem/file-item.js +2 -5
  105. package/dist/components/FileItem/file-item.js.map +1 -1
  106. package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
  107. package/dist/components/FileUpload/file-upload.js +5 -1
  108. package/dist/components/FileUpload/file-upload.js.map +1 -1
  109. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  110. package/dist/components/FileViewer/file-viewer.js +2 -1
  111. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  112. package/dist/components/HoverCard/hover-card.d.ts +2 -2
  113. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  114. package/dist/components/HoverCard/hover-card.js +4 -2
  115. package/dist/components/HoverCard/hover-card.js.map +1 -1
  116. package/dist/components/InlineEdit/inline-edit.d.ts +23 -16
  117. package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -1
  118. package/dist/components/InlineEdit/inline-edit.js +104 -62
  119. package/dist/components/InlineEdit/inline-edit.js.map +1 -1
  120. package/dist/components/Input/input.d.ts +1 -1
  121. package/dist/components/Input/input.d.ts.map +1 -1
  122. package/dist/components/Input/input.js +6 -6
  123. package/dist/components/Input/input.js.map +1 -1
  124. package/dist/components/LinkInput/link-input.d.ts +5 -5
  125. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  126. package/dist/components/LinkInput/link-input.js +3 -3
  127. package/dist/components/LinkInput/link-input.js.map +1 -1
  128. package/dist/components/Notice/notice.d.ts +17 -1
  129. package/dist/components/Notice/notice.d.ts.map +1 -1
  130. package/dist/components/Notice/notice.js +7 -1
  131. package/dist/components/Notice/notice.js.map +1 -1
  132. package/dist/components/NumberInput/number-input.d.ts +1 -1
  133. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  134. package/dist/components/NumberInput/number-input.js +12 -1
  135. package/dist/components/NumberInput/number-input.js.map +1 -1
  136. package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts +1 -1
  137. package/dist/components/PeoplePicker/avatar-stack-overflow.js.map +1 -1
  138. package/dist/components/PeoplePicker/people-picker.d.ts +5 -5
  139. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  140. package/dist/components/PeoplePicker/people-picker.js +7 -5
  141. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  142. package/dist/components/PeoplePicker/person-display.d.ts +1 -1
  143. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  144. package/dist/components/ProfileCard/profile-card.d.ts +1 -1
  145. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  146. package/dist/components/RadioGroup/radio-group.d.ts +3 -3
  147. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  148. package/dist/components/RadioGroup/radio-group.js +1 -1
  149. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  150. package/dist/components/Rating/rating.d.ts.map +1 -1
  151. package/dist/components/Rating/rating.js +4 -0
  152. package/dist/components/Rating/rating.js.map +1 -1
  153. package/dist/components/ScrollArea/scroll-area.d.ts +3 -2
  154. package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
  155. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  156. package/dist/components/SegmentedControl/segmented-control.d.ts +4 -3
  157. package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
  158. package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
  159. package/dist/components/Select/select.d.ts +16 -6
  160. package/dist/components/Select/select.d.ts.map +1 -1
  161. package/dist/components/Select/select.js +17 -5
  162. package/dist/components/Select/select.js.map +1 -1
  163. package/dist/components/SelectMenu/select-menu.d.ts +17 -1
  164. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  165. package/dist/components/SelectMenu/select-menu.js +17 -3
  166. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  167. package/dist/components/SelectionControl/boolean-value.d.ts +1 -1
  168. package/dist/components/SelectionControl/boolean-value.js.map +1 -1
  169. package/dist/components/Sheet/sheet.d.ts +2 -2
  170. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  171. package/dist/components/Sheet/sheet.js.map +1 -1
  172. package/dist/components/Sidebar/sidebar.d.ts +1 -1
  173. package/dist/components/Sidebar/sidebar.js +2 -2
  174. package/dist/components/Sidebar/sidebar.js.map +1 -1
  175. package/dist/components/Slider/slider.d.ts.map +1 -1
  176. package/dist/components/Slider/slider.js +6 -3
  177. package/dist/components/Slider/slider.js.map +1 -1
  178. package/dist/components/Steps/steps.d.ts +6 -4
  179. package/dist/components/Steps/steps.d.ts.map +1 -1
  180. package/dist/components/Steps/steps.js +19 -5
  181. package/dist/components/Steps/steps.js.map +1 -1
  182. package/dist/components/Switch/switch.d.ts +4 -4
  183. package/dist/components/Switch/switch.d.ts.map +1 -1
  184. package/dist/components/Switch/switch.js +3 -1
  185. package/dist/components/Switch/switch.js.map +1 -1
  186. package/dist/components/Tabs/tabs.d.ts +1 -1
  187. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  188. package/dist/components/Tabs/tabs.js +73 -23
  189. package/dist/components/Tabs/tabs.js.map +1 -1
  190. package/dist/components/Tag/tag.d.ts +3 -1
  191. package/dist/components/Tag/tag.d.ts.map +1 -1
  192. package/dist/components/Tag/tag.js +2 -2
  193. package/dist/components/Tag/tag.js.map +1 -1
  194. package/dist/components/Textarea/textarea.d.ts +4 -4
  195. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  196. package/dist/components/Textarea/textarea.js +15 -15
  197. package/dist/components/Textarea/textarea.js.map +1 -1
  198. package/dist/components/TimePicker/time-picker.d.ts +4 -4
  199. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  200. package/dist/components/TimePicker/time-picker.js +8 -5
  201. package/dist/components/TimePicker/time-picker.js.map +1 -1
  202. package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
  203. package/dist/components/Tooltip/tooltip.js +27 -23
  204. package/dist/components/Tooltip/tooltip.js.map +1 -1
  205. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  206. package/dist/components/TreeView/tree-view.js +16 -0
  207. package/dist/components/TreeView/tree-view.js.map +1 -1
  208. package/dist/index.d.ts +7 -7
  209. package/dist/index.d.ts.map +1 -1
  210. package/dist/index.js +6 -11
  211. package/dist/lib/drag-visual.d.ts +2 -2
  212. package/dist/lib/drag-visual.js.map +1 -1
  213. package/dist/lib/utils.d.ts +1 -0
  214. package/dist/lib/utils.d.ts.map +1 -1
  215. package/dist/lib/utils.js +21 -1
  216. package/dist/lib/utils.js.map +1 -1
  217. package/dist/patterns/element-anatomy/item-anatomy.d.ts +2 -0
  218. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  219. package/dist/patterns/element-anatomy/item-anatomy.js +2 -2
  220. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  221. package/dist/tokens/categorical-color.d.ts +3 -3
  222. package/dist/tokens/categorical-color.d.ts.map +1 -1
  223. package/dist/tokens/categorical-color.js +2 -2
  224. package/dist/tokens/categorical-color.js.map +1 -1
  225. package/ds-canonical/fork/AGENTS.md +213 -0
  226. package/ds-canonical/fork/codex/agents/skills/independent-review/SKILL.md +33 -0
  227. package/ds-canonical/fork/codex/hooks.json +16 -0
  228. package/ds-canonical/fork/governance.lock +22 -7
  229. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +22 -1
  230. package/ds-canonical/fork/hooks/inject_deploy_url_after_push.sh +14 -2
  231. package/ds-canonical/fork/manifest.json +10 -1
  232. package/ds-canonical/fork/preamble.md +3 -2
  233. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +7 -7
  234. package/ds-canonical/fork/skills/ux-audit/SKILL.md +1 -1
  235. package/ds-canonical/fork/skills/visual-audit/SKILL.md +0 -1
  236. package/ds-canonical/hooks/check_codex_brief_invariants.sh +9 -3
  237. package/ds-canonical/hooks/check_datatable_invariants.sh +6 -3
  238. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +22 -1
  239. package/ds-canonical/hooks/check_overlay_open_focus_escape_probe.sh +11 -2
  240. package/ds-canonical/hooks/check_plugin_fork_health.sh +17 -1
  241. package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +21 -4
  242. package/ds-canonical/hooks/inject_deploy_url_after_push.sh +14 -2
  243. package/ds-canonical/hooks/session_start_governance_check.sh +10 -2
  244. package/ds-canonical/references/certified-surfaces.md +20 -0
  245. package/ds-canonical/references/preflight-gate-baseline.json +5 -0
  246. package/ds-canonical/references/principle-dim-map.json +20 -2
  247. package/ds-canonical/references/props-naming.md +2 -1
  248. package/ds-canonical/references/ssot-index.md +2 -2
  249. package/ds-canonical/references/story-baseline-registry.json +2 -2
  250. package/ds-canonical/rules/story-rules.md +1 -1
  251. package/ds-canonical/rules/ui-development.md +2 -1
  252. package/ds-canonical/skills/codex-collab/SKILL.md +7 -7
  253. package/ds-canonical/skills/codex-collab/references/brief-template.md +5 -1
  254. package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
  255. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +5 -5
  256. package/ds-canonical/skills/design-system-audit/SKILL.md +5 -5
  257. package/ds-canonical/skills/product-ui-audit/SKILL.md +7 -7
  258. package/ds-canonical/skills/ux-audit/SKILL.md +1 -1
  259. package/ds-canonical/skills/visual-audit/SKILL.md +0 -1
  260. package/ds-story-manifest.json +21 -21
  261. package/llms-full.txt +3 -3
  262. package/llms.txt +1 -1
  263. package/package.json +2 -1
  264. package/src/components/Accordion/accordion.anatomy.stories.tsx +11 -11
  265. package/src/components/Accordion/accordion.principles.stories.tsx +21 -6
  266. package/src/components/Accordion/accordion.spec.md +10 -1
  267. package/src/components/Accordion/accordion.stories.tsx +10 -5
  268. package/src/components/Accordion/accordion.tsx +7 -2
  269. package/src/components/Alert/alert.principles.stories.tsx +2 -8
  270. package/src/components/AppShell/app-shell.anatomy.stories.tsx +19 -13
  271. package/src/components/AppShell/app-shell.principles.stories.tsx +21 -21
  272. package/src/components/AppShell/app-shell.spec.md +4 -0
  273. package/src/components/AppShell/app-shell.stories.tsx +4 -5
  274. package/src/components/Avatar/avatar.tsx +1 -1
  275. package/src/components/Badge/badge.spec.md +2 -2
  276. package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +7 -3
  277. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +4 -1
  278. package/src/components/Breadcrumb/breadcrumb.stories.tsx +1 -1
  279. package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +6 -6
  280. package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +18 -6
  281. package/src/components/BulkActionBar/bulk-action-bar.spec.md +15 -5
  282. package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +8 -24
  283. package/src/components/BulkActionBar/bulk-action-bar.tsx +4 -4
  284. package/src/components/Button/button-group.tsx +4 -4
  285. package/src/components/Button/button.anatomy.stories.tsx +26 -7
  286. package/src/components/Button/button.principles.stories.tsx +1 -1
  287. package/src/components/Button/button.spec.md +2 -2
  288. package/src/components/Button/button.stories.tsx +22 -28
  289. package/src/components/Button/button.tsx +2 -1
  290. package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
  291. package/src/components/Calendar/calendar.spec.md +1 -1
  292. package/src/components/Carousel/carousel.spec.md +17 -1
  293. package/src/components/Carousel/carousel.tsx +93 -42
  294. package/src/components/Checkbox/checkbox-group.tsx +26 -15
  295. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +3 -3
  296. package/src/components/Checkbox/checkbox.spec.md +14 -3
  297. package/src/components/Checkbox/checkbox.stories.tsx +4 -4
  298. package/src/components/Checkbox/checkbox.tsx +79 -19
  299. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +5 -5
  300. package/src/components/Coachmark/coachmark.principles.stories.tsx +1 -1
  301. package/src/components/Combobox/combobox.anatomy.stories.tsx +11 -4
  302. package/src/components/Combobox/combobox.principles.stories.tsx +10 -11
  303. package/src/components/Combobox/combobox.spec.md +6 -2
  304. package/src/components/Combobox/combobox.stories.tsx +9 -36
  305. package/src/components/Combobox/combobox.tsx +42 -27
  306. package/src/components/Command/command.spec.md +3 -3
  307. package/src/components/Command/command.tsx +9 -2
  308. package/src/components/DataTable/cell-registry.tsx +75 -76
  309. package/src/components/DataTable/column-types.ts +4 -17
  310. package/src/components/DataTable/data-table-column-visibility-panel.tsx +20 -7
  311. package/src/components/DataTable/data-table-filter-group.tsx +231 -0
  312. package/src/components/DataTable/data-table-filter-panel.tsx +19 -436
  313. package/src/components/DataTable/data-table-filter-value-picker.tsx +237 -0
  314. package/src/components/DataTable/data-table-interaction-layer.tsx +3 -3
  315. package/src/components/DataTable/data-table-sort-manager.tsx +2 -2
  316. package/src/components/DataTable/data-table.anatomy.stories.tsx +25 -24
  317. package/src/components/DataTable/data-table.css +2 -3
  318. package/src/components/DataTable/data-table.principles.stories.tsx +17 -11
  319. package/src/components/DataTable/data-table.spec.md +8 -8
  320. package/src/components/DataTable/data-table.stories.tsx +39 -29
  321. package/src/components/DataTable/data-table.tsx +94 -68
  322. package/src/components/DataTable/filter-operators.spec.md +5 -3
  323. package/src/components/DateGrid/date-grid.spec.md +1 -1
  324. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +9 -9
  325. package/src/components/DatePicker/date-picker.principles.stories.tsx +4 -4
  326. package/src/components/DatePicker/date-picker.spec.md +9 -9
  327. package/src/components/DatePicker/date-picker.stories.tsx +9 -9
  328. package/src/components/DatePicker/date-picker.tsx +13 -13
  329. package/src/components/DescriptionList/description-list.anatomy.stories.tsx +5 -5
  330. package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
  331. package/src/components/DescriptionList/description-list.spec.md +12 -12
  332. package/src/components/DescriptionList/description-list.stories.tsx +3 -3
  333. package/src/components/DescriptionList/description-list.tsx +10 -10
  334. package/src/components/Dialog/dialog.tsx +4 -1
  335. package/src/components/DropdownMenu/dropdown-menu.tsx +27 -14
  336. package/src/components/Field/field-context.ts +17 -11
  337. package/src/components/Field/field-controls.spec.md +44 -37
  338. package/src/components/Field/field-types.ts +14 -12
  339. package/src/components/Field/field-wrapper.tsx +59 -43
  340. package/src/components/Field/field.anatomy.stories.tsx +6 -6
  341. package/src/components/Field/field.principles.stories.tsx +5 -28
  342. package/src/components/Field/field.spec.md +4 -4
  343. package/src/components/Field/field.stories.tsx +103 -72
  344. package/src/components/Field/field.tsx +36 -4
  345. package/src/components/Field/form-validation.spec.md +2 -0
  346. package/src/components/Field/index.ts +1 -0
  347. package/src/components/Field/use-form-validation.ts +1 -1
  348. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
  349. package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
  350. package/src/components/FileItem/file-item.tsx +10 -8
  351. package/src/components/FileUpload/file-upload.principles.stories.tsx +7 -7
  352. package/src/components/FileUpload/file-upload.spec.md +3 -2
  353. package/src/components/FileUpload/file-upload.tsx +5 -1
  354. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
  355. package/src/components/FileViewer/file-viewer.principles.stories.tsx +1 -1
  356. package/src/components/FileViewer/file-viewer.spec.md +1 -1
  357. package/src/components/FileViewer/file-viewer.stories.tsx +1 -1
  358. package/src/components/FileViewer/file-viewer.tsx +3 -2
  359. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -4
  360. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -1
  361. package/src/components/HoverCard/hover-card.spec.md +2 -2
  362. package/src/components/HoverCard/hover-card.tsx +4 -2
  363. package/src/components/InlineEdit/inline-edit.anatomy.stories.tsx +184 -0
  364. package/src/components/InlineEdit/inline-edit.principles.stories.tsx +142 -0
  365. package/src/components/InlineEdit/inline-edit.spec.md +71 -46
  366. package/src/components/InlineEdit/inline-edit.stories.tsx +20 -19
  367. package/src/components/InlineEdit/inline-edit.tsx +211 -125
  368. package/src/components/Input/input.anatomy.stories.tsx +15 -15
  369. package/src/components/Input/input.principles.stories.tsx +3 -3
  370. package/src/components/Input/input.spec.md +4 -4
  371. package/src/components/Input/input.stories.tsx +3 -3
  372. package/src/components/Input/input.tsx +13 -13
  373. package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -2
  374. package/src/components/LinkInput/link-input.principles.stories.tsx +1 -1
  375. package/src/components/LinkInput/link-input.spec.md +6 -2
  376. package/src/components/LinkInput/link-input.stories.tsx +8 -8
  377. package/src/components/LinkInput/link-input.tsx +21 -19
  378. package/src/components/Notice/notice.principles.stories.tsx +1 -1
  379. package/src/components/Notice/notice.tsx +8 -2
  380. package/src/components/NumberInput/number-input.anatomy.stories.tsx +6 -6
  381. package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
  382. package/src/components/NumberInput/number-input.spec.md +6 -6
  383. package/src/components/NumberInput/number-input.stories.tsx +4 -4
  384. package/src/components/NumberInput/number-input.tsx +17 -5
  385. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +8 -4
  386. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +6 -7
  387. package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
  388. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -30
  389. package/src/components/PeoplePicker/avatar-stack-overflow.ts +1 -1
  390. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +13 -10
  391. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +5 -1
  392. package/src/components/PeoplePicker/people-picker.stories.tsx +5 -5
  393. package/src/components/PeoplePicker/people-picker.tsx +39 -20
  394. package/src/components/PeoplePicker/person-display.tsx +3 -3
  395. package/src/components/Popover/popover.anatomy.stories.tsx +10 -7
  396. package/src/components/Popover/popover.principles.stories.tsx +49 -28
  397. package/src/components/Popover/popover.spec.md +4 -2
  398. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
  399. package/src/components/ProfileCard/profile-card.tsx +1 -1
  400. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +14 -13
  401. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +8 -23
  402. package/src/components/ProgressBar/progress-bar.stories.tsx +8 -2
  403. package/src/components/README.md +1 -0
  404. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +8 -3
  405. package/src/components/RadioGroup/radio-group.stories.tsx +3 -3
  406. package/src/components/RadioGroup/radio-group.tsx +19 -14
  407. package/src/components/Rating/rating.anatomy.stories.tsx +1 -1
  408. package/src/components/Rating/rating.principles.stories.tsx +3 -3
  409. package/src/components/Rating/rating.spec.md +2 -2
  410. package/src/components/Rating/rating.stories.tsx +4 -18
  411. package/src/components/Rating/rating.tsx +5 -1
  412. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
  413. package/src/components/ScrollArea/scroll-area.spec.md +1 -1
  414. package/src/components/ScrollArea/scroll-area.tsx +5 -2
  415. package/src/components/SegmentedControl/segmented-control.principles.stories.tsx +4 -1
  416. package/src/components/SegmentedControl/segmented-control.spec.md +5 -10
  417. package/src/components/SegmentedControl/segmented-control.tsx +6 -3
  418. package/src/components/Select/select.anatomy.stories.tsx +13 -7
  419. package/src/components/Select/select.principles.stories.tsx +3 -3
  420. package/src/components/Select/select.spec.md +11 -1
  421. package/src/components/Select/select.stories.tsx +41 -27
  422. package/src/components/Select/select.tsx +44 -26
  423. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +44 -26
  424. package/src/components/SelectMenu/select-menu.principles.stories.tsx +0 -1
  425. package/src/components/SelectMenu/select-menu.stories.tsx +7 -19
  426. package/src/components/SelectMenu/select-menu.tsx +38 -5
  427. package/src/components/SelectionControl/boolean-value.tsx +2 -2
  428. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
  429. package/src/components/SelectionControl/selection-item.principles.stories.tsx +4 -4
  430. package/src/components/Separator/separator.principles.stories.tsx +1 -1
  431. package/src/components/Sheet/sheet.principles.stories.tsx +5 -3
  432. package/src/components/Sheet/sheet.tsx +4 -1
  433. package/src/components/Sidebar/sidebar.tsx +1 -1
  434. package/src/components/Slider/slider.anatomy.stories.tsx +5 -4
  435. package/src/components/Slider/slider.principles.stories.tsx +6 -6
  436. package/src/components/Slider/slider.spec.md +13 -1
  437. package/src/components/Slider/slider.stories.tsx +44 -32
  438. package/src/components/Slider/slider.tsx +13 -4
  439. package/src/components/Steps/steps.anatomy.stories.tsx +7 -7
  440. package/src/components/Steps/steps.principles.stories.tsx +1 -1
  441. package/src/components/Steps/steps.spec.md +4 -2
  442. package/src/components/Steps/steps.stories.tsx +4 -56
  443. package/src/components/Steps/steps.tsx +31 -7
  444. package/src/components/Switch/switch.anatomy.stories.tsx +3 -3
  445. package/src/components/Switch/switch.principles.stories.tsx +1 -1
  446. package/src/components/Switch/switch.spec.md +4 -4
  447. package/src/components/Switch/switch.stories.tsx +3 -3
  448. package/src/components/Switch/switch.tsx +16 -10
  449. package/src/components/Tabs/tabs.anatomy.stories.tsx +6 -0
  450. package/src/components/Tabs/tabs.principles.stories.tsx +10 -2
  451. package/src/components/Tabs/tabs.spec.md +5 -2
  452. package/src/components/Tabs/tabs.stories.tsx +36 -44
  453. package/src/components/Tabs/tabs.tsx +101 -36
  454. package/src/components/Tag/tag.anatomy.stories.tsx +2 -2
  455. package/src/components/Tag/tag.principles.stories.tsx +4 -4
  456. package/src/components/Tag/tag.spec.md +3 -1
  457. package/src/components/Tag/tag.tsx +5 -3
  458. package/src/components/Textarea/textarea.anatomy.stories.tsx +6 -6
  459. package/src/components/Textarea/textarea.spec.md +5 -5
  460. package/src/components/Textarea/textarea.tsx +24 -24
  461. package/src/components/TimePicker/time-picker.anatomy.stories.tsx +12 -11
  462. package/src/components/TimePicker/time-picker.principles.stories.tsx +37 -44
  463. package/src/components/TimePicker/time-picker.spec.md +6 -5
  464. package/src/components/TimePicker/time-picker.stories.tsx +3 -3
  465. package/src/components/TimePicker/time-picker.tsx +24 -14
  466. package/src/components/Toast/toast.principles.stories.tsx +1 -1
  467. package/src/components/Toast/toast.stories.tsx +1 -1
  468. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +15 -6
  469. package/src/components/Tooltip/tooltip.principles.stories.tsx +4 -4
  470. package/src/components/Tooltip/tooltip.spec.md +9 -3
  471. package/src/components/Tooltip/tooltip.stories.tsx +1 -1
  472. package/src/components/Tooltip/tooltip.tsx +12 -2
  473. package/src/components/TreeView/tree-view.anatomy.stories.tsx +16 -14
  474. package/src/components/TreeView/tree-view.principles.stories.tsx +44 -11
  475. package/src/components/TreeView/tree-view.spec.md +8 -6
  476. package/src/components/TreeView/tree-view.stories.tsx +9 -43
  477. package/src/components/TreeView/tree-view.tsx +36 -0
  478. package/src/index.ts +16 -9
  479. package/src/lib/drag-visual.ts +2 -2
  480. package/src/lib/utils.ts +19 -0
  481. package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
  482. package/src/patterns/element-anatomy/item-anatomy.tsx +4 -2
  483. package/src/patterns/header-canonical/header-canonical.spec.md +9 -0
  484. package/src/patterns/header-canonical/header-canonical.stories.tsx +31 -5
  485. package/src/tokens/categorical-color.ts +5 -5
  486. package/src/tokens/color/color.spec.md +5 -5
  487. package/src/tokens/color/primitives.css +9 -2
  488. package/src/tokens/opacity/opacity.spec.md +1 -1
  489. package/src/tokens/orphan-tokens.spec.md +1 -1
  490. package/src/tokens/typography/typography.spec.md +2 -0
  491. package/src/tokens/uiSize/uiSize.css +6 -1
  492. package/src/tokens/uiSize/uiSize.spec.md +1 -1
@@ -48,6 +48,9 @@ type ShapeKey = 'circle' | 'tag'
48
48
  const SIZES: SizeKey[] = ['sm', 'md', 'lg']
49
49
  const SHAPES: ShapeKey[] = ['circle', 'tag']
50
50
 
51
+ // 真實隱藏項目池(reviewer / assignee 溢出情境;count 可調,超出池長度時循環)— 取代編號 placeholder
52
+ const HIDDEN_REVIEWERS = ['林思妤', '陳柏宇', 'Aisha Khan', '黃冠霖', 'Diego Santos', '王品澄', 'Nadia Petrova', '張哲瑋']
53
+
51
54
  // font-size 依 shape 分流(re-verified 2026-07-04):circle shape 走元件內建 triggerText
52
55
  // map(overflow-indicator.tsx:28-32),tag shape 走 tagVariants({ size })(tag.tsx:40-44)。
53
56
  // circleText / tagText 兩欄分別對應,避免把 circle-only 字級當成兩 shape 通用。
@@ -209,9 +212,9 @@ function OverflowInspector() {
209
212
  <OverflowIndicator count={count} shape={shape} size={size}>
210
213
  <div className="flex flex-col gap-1 text-caption min-w-[140px]">
211
214
  {Array.from({ length: Math.min(count, 8) }).map((_, i) => (
212
- <span key={i}>隱藏項目 {i + 1}</span>
215
+ <span key={i}>{HIDDEN_REVIEWERS[i % HIDDEN_REVIEWERS.length]}</span>
213
216
  ))}
214
- {count > 8 && <span className="text-fg-muted">…及其他 {count - 8} 項</span>}
217
+ {count > 8 && <span className="text-fg-muted">…及其他 {count - 8} 位</span>}
215
218
  </div>
216
219
  </OverflowIndicator>
217
220
  </div>
@@ -291,6 +294,7 @@ function OverflowInspector() {
291
294
  value={count}
292
295
  onChange={(e) => setCount(parseInt(e.target.value, 10))}
293
296
  className="w-full"
297
+ aria-label="調整隱藏項目數量"
294
298
  />
295
299
  <div className="text-caption font-mono text-fg-secondary">count = {count}</div>
296
300
  </div>
@@ -381,7 +385,7 @@ export const ShapeMatrix: Story = {
381
385
  </OverflowIndicator>
382
386
  </Td>
383
387
  <Td mono>tagVariants neutral · rounded-md · 同 Tag 視覺</Td>
384
- <Td>Combobox multi-select tag 溢出 / Breadcrumb 收合</Td>
388
+ <Td>Combobox multi-select tag 溢出(Breadcrumb 收合走 BreadcrumbEllipsis canonical,非本元件)</Td>
385
389
  </tr>
386
390
  </tbody>
387
391
  </table>
@@ -545,7 +549,7 @@ export const Accessibility = {
545
549
  render: () => (
546
550
  <div className="max-w-3xl text-body text-fg-secondary">
547
551
  <h3 className="text-h5 text-foreground mb-2">無障礙設計</h3>
548
- <p className="whitespace-pre-line">{"摘要:\n\n ARIA / Pattern :對齊 [W3C ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/patterns/) 對應 pattern。\n\n 互動行為 :\n\n +N trigger 是 keyboard-focusable 計數 span(tabIndex=0;不掛 role/aria-haspopup——無 activation 行為的宣告是對 AT 空承諾,2026-07-14 對齊 Avatar hoverCard canonical 拆除;2026-06-01 #13 由純 passive 改 focusable,修 WCAG 2.1.1 違反)。HoverCard 在 hover 或 trigger 取得 focus 時自動展開,沒有「按 Enter 開選單」的鍵盤指令,也沒有 click 切換。\n\n Focus :trigger 是 tab stop(focus-visible ring,鍵盤使用者可 focus 開啟 HoverCard 看溢出內容);HoverCard 內展開的可互動內容(人員 tag / ProfileCard)由各自內容元件負責 focus 管理。\n\n 驗證 :Storybook a11y addon panel 應 0 critical violation;HoverCard 內容透過 hover 或 focus 自動顯示(非鍵盤指令觸發)。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。"}</p>
552
+ <p className="whitespace-pre-line">{"摘要:\n\n ARIA / Pattern :對齊 [W3C ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/patterns/) 對應 pattern。\n\n 互動行為 :\n\n +N trigger 是 keyboard-focusable 計數 span(tabIndex=0;不掛 role/aria-haspopup——無 activation 行為的宣告是對 AT 空承諾,2026-07-14 對齊 Avatar hoverCard canonical 拆除;2026-06-01 #13 由純 passive 改 focusable,修 WCAG 2.1.1 違反)。HoverCard 在 hover 或 trigger 取得 focus 時自動展開,沒有「按 Enter 開選單」的鍵盤指令,也沒有 click 切換。\n\n Focus :trigger 是 tab stop(focus-visible ring,鍵盤使用者可 focus 開啟 HoverCard 看溢出內容);HoverCard 內展開的內容不接受鍵盤 focus——Radix 強制 content tabbable node 為 tabindex=-1,鍵盤等效操作由主列可見項承擔,需鍵盤可操作的互動浮層改用 Popover(click 觸發)。\n\n 驗證 :Storybook a11y addon panel 應 0 critical violation;HoverCard 內容透過 hover 或 focus 自動顯示(非鍵盤指令觸發)。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。"}</p>
549
553
  </div>
550
554
  ),
551
555
  }
@@ -29,9 +29,6 @@ export const UsageGuidance: Story = {
29
29
  <li>
30
30
  <LinkTo kind="Design System/Internal/OverflowIndicator/展示" name="人員頭像 疊合 +N"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">PR reviewer 頭像疊合 +N(只顯前 3 位)</span></LinkTo>
31
31
  </li>
32
- <li>
33
- <LinkTo kind="Design System/Internal/OverflowIndicator/展示" name="Breadcrumb 中段收合"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Breadcrumb 中段收合</span></LinkTo>
34
- </li>
35
32
  <li>
36
33
  <LinkTo kind="Design System/Internal/OverflowIndicator/展示" name="DataTable 人員欄位"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">DataTable 人員欄位</span></LinkTo>
37
34
  </li>
@@ -43,10 +40,10 @@ export const UsageGuidance: Story = {
43
40
  <div className="prose prose-sm max-w-prose">
44
41
  <p>處理超出空間的 2 種策略:</p>
45
42
  <ul>
46
- <li><strong>OverflowIndicator(本元件)</strong>—項目數量已知且不多時,顯示前幾個、其餘折成 <code>+N</code>,點開看完整清單</li>
43
+ <li><strong>OverflowIndicator(本元件)</strong>—項目數量已知且不多時,顯示前幾個、其餘折成 <code>+N</code>,hover 展開看完整清單</li>
47
44
  <li><strong>ScrollArea</strong>—項目數量未知或極多時,讓使用者捲動瀏覽全部</li>
48
45
  </ul>
49
- <p className="text-fg-muted">判斷:項目數可數且少(≤ 100)→ OverflowIndicator;太多或未知數量 → ScrollArea。</p>
46
+ <p className="text-fg-muted">判斷:項目少量、已知、適合一次預覽 → OverflowIndicator;數量大、未知或需連續瀏覽 → ScrollArea。</p>
50
47
  </div>
51
48
  </div>
52
49
  ),
@@ -67,8 +64,10 @@ export const CompositionRules: Story = {
67
64
  <h4>Pattern 2 — 人員頭像疊合 +N</h4>
68
65
  <p>一群人只顯示前幾位、其餘折成 `+M`。目前由列表元件自行把頭像疊合再放上 OverflowIndicator(形狀用圓形對齊頭像);未來規劃中的 Avatar 群組元件會把這段組合收進去。對齊 Slack 工作區成員預覽 / Linear 團隊成員的做法。</p>
69
66
 
70
- <h4>Pattern 3 — Tabs / Breadcrumb 水平溢出</h4>
71
- <p>水平容器(Tabs / Breadcrumb)寬度不夠 → 搭配水平溢出處理 + OverflowIndicator,把中段或末端收合成 `…` 或 `+N`。對齊 Material 可捲動 Tabs + 自動溢出選單 / GitHub Breadcrumb 收合的做法。</p>
67
+ <h4>Pattern 3 — PeoplePicker 人員選擇欄位 +N</h4>
68
+ <p>人員選擇欄位選了多位、單行放不下 PeoplePicker(<code>PersonDisplay</code>)內部使用 OverflowIndicator(圓形對齊頭像),hover `+N` 展開完整名單。對齊 Linear 多指派人 / Jira watchers 的做法。</p>
69
+
70
+ <p className="text-fg-muted">水平容器(Tabs / 單行 chip 列)寬度不夠時<strong className="font-medium">不用本元件</strong>——DS canonical 是 <code>horizontal-overflow</code> 的 <code>OverflowMenuTriggerButton</code> + DropdownMenu(click 開選單,見 horizontal-overflow.spec.md);Breadcrumb 中段收合走 BreadcrumbEllipsis + DropdownMenu(見 breadcrumb.spec.md)。兩者皆 click 開選單,與本元件 hover 展開語義不同。</p>
72
71
 
73
72
  <p className="text-fg-muted">禁止:在頁面程式碼裡自己手刻 `+N` <code>&lt;span&gt;</code>(會失去 hover 展開浮層與一致的形狀樣式)— 一律使用 OverflowIndicator。</p>
74
73
  </div>
@@ -124,6 +124,6 @@ OverflowIndicator 是 **composite**(HoverCard trigger + tag-styled `+N` span + H
124
124
 
125
125
  **互動行為**:trigger 為 `+N` 計數 span,**keyboard-focusable**(`tabIndex=0`;**不掛 `role="button"` / `aria-haspopup`**——2026-07-14 對齊 Avatar hoverCard canonical 2026-07-06 拆除:trigger 無 activation 行為,宣告 button/haspopup 卻按 Enter 無反應 = 對 AT 空承諾),HoverCard 在 hover 或 trigger 取得 focus 時自動展開——無「按 Enter 開選單」的鍵盤指令,也沒有 click 切換。(2026-06-01 #13:原設計純 passive 不可聚焦 → 鍵盤使用者無法開啟 HoverCard 看溢出內容,違 WCAG 2.1.1;改 focusable)。**鍵盤限制**:HoverCard 內容(tag dismiss / nested ProfileCard)僅滑鼠可互動——Radix 把 content 內 tabbable node 設 `tabindex="-1"`(hover-card.spec.md「A11y 預設」),鍵盤等效操作由主列可見項承擔。
126
126
 
127
- **Focus**:trigger 是 tab stop(keyboard 可達 + focus-visible ring,讓鍵盤使用者也能 focus 開啟 HoverCard 看溢出內容);HoverCard 內展開的可互動內容(如人員 tag / ProfileCard)由各自的內容元件負責 focus 管理。
127
+ **Focus**:trigger 是 tab stop(keyboard 可達 + focus-visible ring,讓鍵盤使用者也能 focus 開啟 HoverCard 看溢出內容);HoverCard 內展開的內容**不接受鍵盤 focus**——Radix 強制 content 內 tabbable node 為 `tabindex="-1"`(見上方「互動行為」鍵盤限制 + `hover-card.spec.md`「A11y 預設」),鍵盤等效操作由主列可見項承擔;需鍵盤可操作的互動浮層改用 Popover(click 觸發)。
128
128
 
129
129
  **驗證**:Storybook a11y addon panel 應 0 critical violation;HoverCard 內容透過 hover 或 focus 自動顯示(非鍵盤指令觸發)。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。
@@ -129,38 +129,14 @@ export const AvatarStackOverflow: Story = {
129
129
  // (overflow-indicator.anatomy.stories.tsx)完整覆蓋,展示層只留真實業務場景;
130
130
  // principles UsageGuidance 對應 LinkTo 已同步移除。
131
131
 
132
- /* ═══════════════════════════════════════════════════════════════════════════
133
- Story 3:Breadcrumb 中段收合(+N 顯示隱藏路徑)
134
- ═══════════════════════════════════════════════════════════════════════════ */
135
-
136
- export const BreadcrumbCollapse: Story = {
137
- name: 'Breadcrumb 中段收合',
138
- render: () => (
139
- <div className="flex flex-col gap-3 max-w-lg">
140
- <p className="text-caption text-fg-muted">
141
- 深層路徑過長時,中段收合為 +N,hover 看完整路徑。
142
- </p>
143
- <nav className="flex items-center gap-1 text-caption">
144
- <span className="text-fg-muted hover:text-foreground cursor-pointer">Projects</span>
145
- <span className="text-fg-muted">/</span>
146
- <OverflowIndicator count={3} shape="tag" size="sm">
147
- <div className="flex flex-col gap-1 text-caption">
148
- <span>Platform</span>
149
- <span>Infrastructure</span>
150
- <span>Monitoring</span>
151
- </div>
152
- </OverflowIndicator>
153
- <span className="text-fg-muted">/</span>
154
- <span className="text-fg-muted hover:text-foreground cursor-pointer">Dashboards</span>
155
- <span className="text-fg-muted">/</span>
156
- <span className="text-foreground font-medium">API latency</span>
157
- </nav>
158
- </div>
159
- ),
160
- }
132
+ // @story-trait-rationale: BreadcrumbCollapse(Breadcrumb 中段收合)retired 2026-07-16 per
133
+ // deep-audit A.1b — DS Breadcrumb 收合 canonical = BreadcrumbEllipsis + DropdownMenu(click 開選單,
134
+ // breadcrumb.spec.md「BreadcrumbEllipsis」段),Breadcrumb 全庫零 OverflowIndicator 消費;
135
+ // 本 story 示範與 canonical 相左的 hover +N 收合 = 誤導 consumer,故 retire;
136
+ // principles UsageGuidance 對應 LinkTo 已同步移除。
161
137
 
162
138
  /* ═══════════════════════════════════════════════════════════════════════════
163
- Story 4:DataTable row 的指派人員溢出
139
+ Story 3:DataTable row 的指派人員溢出
164
140
  ═══════════════════════════════════════════════════════════════════════════ */
165
141
 
166
142
  const rows = [
@@ -10,7 +10,7 @@
10
10
  * 4. **架構違反**:`MultiPersonDisplay` display path 用 canvas-based 算 / Combobox edit path 用 DOM offsetWidth
11
11
  * → 同 cell width 兩 path 不同結果 → user verbatim「同寬 cell overflow 時間不一樣」SSOT 根本被違反
12
12
  *
13
- * **Fix**(per codex Q3 consensus):抽 pure formula + React hook,display + edit 兩 path **共用**。
13
+ * **Fix**(per codex Q3 consensus):抽 pure formula + React hook,view + edit 兩 path **共用**。
14
14
  *
15
15
  * **Deterministic formula**:
16
16
  * ```
@@ -31,6 +31,7 @@ export const Overview: Story = {
31
31
  people={SAMPLE_PEOPLE}
32
32
  value={SAMPLE_PEOPLE[0]}
33
33
  onChange={() => {}}
34
+ aria-label="負責人"
34
35
  />
35
36
  </div>
36
37
  </div>
@@ -45,6 +46,7 @@ export const Overview: Story = {
45
46
  people={SAMPLE_PEOPLE}
46
47
  value={SAMPLE_PEOPLE[0]}
47
48
  onChange={() => {}}
49
+ aria-label="指派對象(單選 assignee 範例)"
48
50
  />
49
51
  </div>
50
52
  <div>
@@ -53,6 +55,7 @@ export const Overview: Story = {
53
55
  people={SAMPLE_PEOPLE}
54
56
  value={[SAMPLE_PEOPLE[0], SAMPLE_PEOPLE[1]]}
55
57
  onChange={() => {}}
58
+ aria-label="專案協作者(多選 reviewers 範例)"
56
59
  />
57
60
  </div>
58
61
  </div>
@@ -65,7 +68,7 @@ export const Overview: Story = {
65
68
  <thead><tr><Th>Prop</Th><Th>Type</Th><Th>Default</Th><Th>說明</Th></tr></thead>
66
69
  <tbody>
67
70
  {[
68
- ['mode', "'edit' | 'display' | 'readonly' | 'disabled'", "'edit'", 'Field mode'],
71
+ ['mode', "'edit' | 'view' | 'readonly' | 'disabled'", "'edit'", 'Field mode'],
69
72
  ['variant', "'default'", "'default'", 'Field chrome variant(對齊 Select / Combobox;naked = @internal cell-as-input,2026-07-14 型別收窄至 FieldVariantInternal)'],
70
73
  ['size', "'sm' | 'md' | 'lg'", "'md'", '對齊 field-height tier'],
71
74
  ['value', 'PersonValue | PersonValue[] | null', '—', '單選 / 多選(類型決定模式)'],
@@ -78,7 +81,7 @@ export const Overview: Story = {
78
81
  ['pillShowAvatar', 'boolean', 'true', "multiDisplay='pill' 時是否顯示 avatar prefix"],
79
82
  ['pillWrap', 'boolean', 'true', 'pill 模式是否允許換行'],
80
83
  ['searchIn', "'menu' | 'trigger'", "'menu'", '搜尋型態(menu 浮層內 / trigger inline;multi 才有意義)'],
81
- ['showDisplayEndIcon', 'boolean', 'false', 'display 模式渲 ChevronDown + naked wrapper(opt-in 逃生門;2026-07-08 A 案後 DataTable 預設不傳)'],
84
+ ['showDisplayEndIcon', 'boolean', 'false', 'view 模式渲 ChevronDown + naked wrapper(opt-in 逃生門;2026-07-08 A 案後 DataTable 預設不傳)'],
82
85
  ['defaultOpen', 'boolean', 'false', 'uncontrolled 初始開啟狀態'],
83
86
  ['onOpenChange', '(open: boolean) => void', '—', 'open 狀態變更 callback'],
84
87
  ['disabled', 'boolean', 'false', '停用'],
@@ -97,7 +100,7 @@ export const Overview: Story = {
97
100
  export const Inspector: Story = {
98
101
  name: '元件檢閱器',
99
102
  parameters: {
100
- docs: { description: { story: '右側 Controls 切 props 即時 render,取代 Figma inspect。切 `mode` 看 edit / display / readonly / disabled 視覺差異,切 `size` 對照 field-height tier,切 `variant` 看 default / naked chrome 差異。' } },
103
+ docs: { description: { story: '右側 Controls 切 props 即時 render,取代 Figma inspect。切 `mode` 看 edit / view / readonly / disabled 視覺差異,切 `size` 對照 field-height tier,切 `variant` 看 default / naked chrome 差異。' } },
101
104
  },
102
105
  args: {
103
106
  mode: 'edit',
@@ -111,7 +114,7 @@ export const Inspector: Story = {
111
114
  emptyText: '沒有符合的人員',
112
115
  },
113
116
  argTypes: {
114
- mode: { control: 'radio', options: ['edit', 'display', 'readonly', 'disabled'] },
117
+ mode: { control: 'radio', options: ['edit', 'view', 'readonly', 'disabled'] },
115
118
  variant: { control: 'radio', options: ['default', 'naked'] },
116
119
  size: { control: 'radio', options: ['sm', 'md', 'lg'] },
117
120
  disabled: { control: 'boolean' },
@@ -121,7 +124,7 @@ export const Inspector: Story = {
121
124
  },
122
125
  render: (args) => (
123
126
  <div className="max-w-md">
124
- <PeoplePicker {...args} onChange={() => {}} />
127
+ <PeoplePicker {...args} onChange={() => {}} aria-label="負責人(Inspector 範例)" />
125
128
  </div>
126
129
  ),
127
130
  }
@@ -133,12 +136,12 @@ export const ModeMatrix: Story = {
133
136
  <div>
134
137
  <H3>edit(預設)</H3>
135
138
  <Desc>Field 樣式 + 展開觸發 Popover + Command 搜尋。</Desc>
136
- <PeoplePicker people={SAMPLE_PEOPLE} value={SAMPLE_PEOPLE[0]} onChange={() => {}} />
139
+ <PeoplePicker people={SAMPLE_PEOPLE} value={SAMPLE_PEOPLE[0]} onChange={() => {}} aria-label="負責人(edit 模式範例)" />
137
140
  </div>
138
141
  <div>
139
- <H3>display</H3>
142
+ <H3>view</H3>
140
143
  <Desc>裸 PersonDisplay(Avatar + Name),無 field chrome——DataTable cell / detail panel 純顯示。</Desc>
141
- <PeoplePicker mode="display" people={SAMPLE_PEOPLE} value={SAMPLE_PEOPLE[0]} />
144
+ <PeoplePicker mode="view" people={SAMPLE_PEOPLE} value={SAMPLE_PEOPLE[0]} />
142
145
  </div>
143
146
  <div>
144
147
  <H3>readonly</H3>
@@ -341,8 +344,8 @@ export const StateBehavior: Story = {
341
344
  people={SAMPLE_PEOPLE}
342
345
  value={null}
343
346
  onChange={() => {}}
344
- searchPlaceholder="試試搜尋 xyz"
345
- emptyText="找不到符合的組員 — 試試輸入 email 邀請外部"
347
+ searchPlaceholder="搜尋團隊成員…"
348
+ emptyText="找不到符合的組員"
346
349
  />
347
350
  </div>
348
351
 
@@ -79,6 +79,7 @@ export const UsageGuidance: Story = {
79
79
  value={assignee}
80
80
  onChange={(v) => setAssignee(Array.isArray(v) ? v[0] : v)}
81
81
  people={team}
82
+ aria-label="task 負責人"
82
83
  />
83
84
  </div>
84
85
  </Rule>
@@ -115,6 +116,7 @@ export const UsageGuidance: Story = {
115
116
  ]}
116
117
  value={tags}
117
118
  onChange={setTags}
119
+ aria-label="技術 stack(多選 Combobox)"
118
120
  />
119
121
  <Label>↑ 技術 stack 是分類多選,不是人員,用 Combobox</Label>
120
122
  </Rule>
@@ -140,6 +142,7 @@ export const SingleVsMultiRule: Story = {
140
142
  value={assignee}
141
143
  onChange={(v) => setAssignee(Array.isArray(v) ? v[0] : v)}
142
144
  people={team}
145
+ aria-label="task 負責人(單選)"
143
146
  />
144
147
  <Label>↑ 指派 task:assignee 通常一人</Label>
145
148
  </Rule>
@@ -152,6 +155,7 @@ export const SingleVsMultiRule: Story = {
152
155
  value={reviewers}
153
156
  onChange={(v) => setReviewers(Array.isArray(v) ? v : [v])}
154
157
  people={team}
158
+ aria-label="PR reviewers(多選)"
155
159
  />
156
160
  <Label>↑ PR reviewers:可選多人,已選 {reviewers.length} 人</Label>
157
161
  </Rule>
@@ -177,7 +181,7 @@ export const EmptyStateRule: Story = {
177
181
  title="edit 模式 — 未選時顯示「請選擇人員」佔位提示"
178
182
  note="跟 Select / Combobox 的 placeholder 行為一致。不用半形連字號,因為 edit mode 是可操作的"
179
183
  >
180
- <PeoplePicker value={null} onChange={() => {}} people={team} />
184
+ <PeoplePicker value={null} onChange={() => {}} people={team} aria-label="指派人員(空值 placeholder demo)" />
181
185
  </Rule>
182
186
 
183
187
  <Rule
@@ -60,7 +60,7 @@ const samplePeople = [
60
60
  },
61
61
  ]
62
62
 
63
- // @story-trait-rationale: pre-existing trait gaps (Default/WithError) tracked separately; this PR scope = add display mode card to mode showcase only.
63
+ // @story-trait-rationale: pre-existing trait gaps (Default/WithError) tracked separately; this PR scope = add view mode card to mode showcase only.
64
64
  /* ── 單人(互動) ── */
65
65
  const SinglePicker = () => {
66
66
  const [val, setVal] = React.useState<PersonValue | null>(samplePeople[0])
@@ -72,8 +72,8 @@ const SinglePicker = () => {
72
72
  </div>
73
73
  {/* @story-trait-rationale: pre-existing trait gaps tracked separately */}
74
74
  <div>
75
- <h3 className="text-body font-bold text-foreground mb-2">display</h3>
76
- <PeoplePicker mode="display" value={samplePeople[0]} />
75
+ <h3 className="text-body font-bold text-foreground mb-2">view</h3>
76
+ <PeoplePicker mode="view" value={samplePeople[0]} />
77
77
  </div>
78
78
  <div>
79
79
  <h3 className="text-body font-bold text-foreground mb-2">readonly</h3>
@@ -100,7 +100,7 @@ const MultiPicker = () => {
100
100
  <div className="flex flex-col gap-6 max-w-xs">
101
101
  <div>
102
102
  <h3 className="text-body font-bold text-foreground mb-2">edit(可互動,多選)</h3>
103
- <PeoplePicker value={val} people={samplePeople} onChange={setVal} />
103
+ <PeoplePicker value={val} people={samplePeople} onChange={setVal} aria-label="專案協作者(edit multi demo)" />
104
104
  </div>
105
105
  <div>
106
106
  <h3 className="text-body font-bold text-foreground mb-2">readonly</h3>
@@ -120,7 +120,7 @@ const SizePicker = ({ size }: { size: 'sm' | 'md' | 'lg' }) => {
120
120
  const [val, setVal] = React.useState<PersonValue | null>(samplePeople[0])
121
121
  return (
122
122
  <div className="flex items-center gap-3">
123
- <PeoplePicker size={size} value={val} people={samplePeople} onChange={(v) => setVal(v[0] ?? null)} className="max-w-xs" />
123
+ <PeoplePicker size={size} value={val} people={samplePeople} onChange={(v) => setVal(v[0] ?? null)} className="max-w-xs" aria-label={`負責人(size=${size})`} />
124
124
  <Button variant="primary" size={size}>送出</Button>
125
125
  <span className="text-caption text-fg-muted">size="{size}"</span>
126
126
  </div>
@@ -48,7 +48,7 @@ export { PEOPLE_PICKER_LENGTH1_WRAPPER_CLASS, getPeoplePickerTagWrapperClass }
48
48
  // 差別在 tagRenderer 視覺):
49
49
  // - **'stack'**(default,baseline 既有視覺)— Avatar 疊合 + `+N` overflow indicator,
50
50
  // 不可 wrap。tagRenderer 渲染 avatar stack(visible count 走 shared `avatar-stack-overflow`
51
- // primitive deterministic formula,2026-05-15 Bug 3 fix;display 路徑 `<MultiPersonDisplay>` 同 primitive)。
51
+ // primitive deterministic formula,2026-05-15 Bug 3 fix;view 路徑 `<MultiPersonDisplay>` 同 primitive)。
52
52
  // 對齊 Notion / Linear / Atlassian / Slack 多人 quick-glance idiom。
53
53
  // - **'pill'**(opt-in)— 每人 Tag pill,可 wrap。Wrap `<Combobox tagRenderer>`,
54
54
  // tagRenderer 用 Tag 元件 `avatar` prop SSOT(不塞 children)。
@@ -59,8 +59,12 @@ export { PEOPLE_PICKER_LENGTH1_WRAPPER_CLASS, getPeoplePickerTagWrapperClass }
59
59
  // 讓 consumer 可傳 `id` / `data-testid` / `onBlur` / `onFocus` / `aria-*` 等 HTML root props,
60
60
  // component 內部 `...rest` forward 到 trigger 容器(對齊 DS 既有 Combobox / Select 慣例)。
61
61
  // `onChange` 衝突走 Omit(本 component 用 PersonValue[] custom signature)。
62
- export interface PeoplePickerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
63
- /** Field mode(edit / display / readonly / disabled),默認 inherit Field context 或 'edit' */
62
+ // 2026-07-17 Dim 26 修:`defaultValue` 一併 Omit 本元件 controlled-only(spec「Controlled-only
63
+ // rationale」),React.HTMLAttributes 內建的 `defaultValue` 若不 Omit 會讓 consumer 傳入假
64
+ // uncontrolled API,runtime 經 `...rest` 靜默轉送 trigger div 後無效。Omit 本身即 type-level
65
+ // regression(consumer 傳 defaultValue 直接 TS 編譯錯)。DS 先例 steps.tsx:166 同款 Omit。
66
+ export interface PeoplePickerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'> {
67
+ /** Field mode(edit / view / readonly / disabled),默認 inherit Field context 或 'edit' */
64
68
  mode?: FieldMode
65
69
  /** Field chrome variant(對齊 Select / Combobox)*/
66
70
  variant?: FieldVariant
@@ -113,9 +117,9 @@ export interface PeoplePickerProps extends Omit<React.HTMLAttributes<HTMLDivElem
113
117
  */
114
118
  searchIn?: 'menu' | 'trigger'
115
119
  /**
116
- * Display 是否渲 ChevronDown + Field naked wrapper(D-path opt-in,2026-05-08)
117
- * — DataTable cell display↔edit 像素級對齊用。預設 false(裸 PersonDisplay,backward compat)。
118
- * 設 true 時 display 走 fieldWrapperStyles(naked variant)+ ItemSuffix ChevronDown,
120
+ * View 態是否渲 ChevronDown + Field naked wrapper(D-path opt-in,2026-05-08)
121
+ * — DataTable cell view↔edit 像素級對齊用。預設 false(裸 PersonDisplay,backward compat)。
122
+ * 設 true 時 view 走 fieldWrapperStyles(naked variant)+ ItemSuffix ChevronDown,
119
123
  * 與 edit (Select / Combobox wrapped) 同 DOM 結構,消除 Layer-B padding mismatch。
120
124
  */
121
125
  showDisplayEndIcon?: boolean
@@ -156,8 +160,8 @@ const PeoplePicker = React.forwardRef<HTMLDivElement, PeoplePickerProps>(functio
156
160
  const isMulti = Array.isArray(value)
157
161
  const isEmpty = !value || (isMulti && value.length === 0)
158
162
 
159
- // 2026-07-05 D3 P0 修:以下派生 + 4 hooks 原宣告在 display/readonly/single/pill 四個 early return
160
- // 之後 — 同一 mounted instance 的 resolvedMode 於 edit↔display/disabled 切換(<Field mode>/<Field
163
+ // 2026-07-05 D3 P0 修:以下派生 + 4 hooks 原宣告在 view/readonly/single/pill 四個 early return
164
+ // 之後 — 同一 mounted instance 的 resolvedMode 於 edit↔view/disabled 切換(<Field mode>/<Field
161
165
  // disabled> cascade 正是 runtime 翻轉機制)時 hook 數 7→11 變動 → React #310 crash(與 Combobox
162
166
  // beta.76 / LinkInput 2026-07-04 同家族)。全部 hoist 到任何 early return 之前(對齊 select.tsx
163
167
  // 「所有 hooks 必在 early return 前 call」canonical);effect 加 mode/stack guard,邏輯不變。
@@ -206,23 +210,38 @@ const PeoplePicker = React.forwardRef<HTMLDivElement, PeoplePickerProps>(functio
206
210
  else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = el
207
211
  }, [ref])
208
212
 
209
- // ── mode='display' ────────────────────────────────────────────────────────
213
+ // ── mode='view' ────────────────────────────────────────────────────────
210
214
  // Default(showDisplayEndIcon=false):裸 PersonDisplay / MultiPersonDisplay — backward compat。
211
215
  // Opt-in(showDisplayEndIcon=true,2026-05-08 D-path):Field naked wrapper + ItemSuffix ChevronDown,
212
- // 與 edit (Select / Combobox wrapped) 同 DOM 結構消除 cell display↔edit 像素偏移。
213
- if (resolvedMode === 'display') {
216
+ // 與 edit (Select / Combobox wrapped) 同 DOM 結構消除 cell view↔edit 像素偏移。
217
+ if (resolvedMode === 'view') {
214
218
  if (!showDisplayEndIcon) {
215
- if (isEmpty) return <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>
216
- return isMulti
217
- ? <MultiPersonDisplay value={value as PersonValue[]} size={size} measured />
218
- : <PersonDisplay value={value as PersonValue} size={size} />
219
+ // 2026-07-17 Dim 9 修:裸顯示分支原直接 return PersonDisplay/MultiPersonDisplay/span,
220
+ // 遺失 ref / className / DOM-safe rest props(違 spec「forwardRef + 單一 root」root props 契約,
221
+ // readonly/disabled 分支不對稱)。補穩定 forwarding root:`w-full min-w-0` 保留
222
+ // PersonDisplay 既有 w-full truncate 寬度約束鏈(單人 fill + 多人 stack 左靠不變),
223
+ // 對齊 switch.tsx:209 view 分支轉發先例。
224
+ return (
225
+ <div ref={ref} className={cn('w-full min-w-0', className)} aria-label={ariaLabel} {...rest}>
226
+ {isEmpty
227
+ ? <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>
228
+ : isMulti
229
+ ? <MultiPersonDisplay value={value as PersonValue[]} size={size} measured />
230
+ : <PersonDisplay value={value as PersonValue} size={size} />}
231
+ </div>
232
+ )
219
233
  }
220
234
  // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)
221
235
  const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']
222
236
  return (
223
237
  <div
224
- className={cn(fieldWrapperStyles({ mode: 'display', variant: resolvedVariant, width, size }), className)}
225
- data-field-mode="display"
238
+ // 2026-07-17 Dim 9 修:D-path view root 補 ref / aria-label / {...rest} 轉發(原只套 className,
239
+ // 遺失 root props;對齊 readonly/disabled 分支與 spec root props 契約)。
240
+ ref={ref}
241
+ className={cn(fieldWrapperStyles({ mode: 'view', variant: resolvedVariant, width, size }), className)}
242
+ data-field-mode="view"
243
+ aria-label={ariaLabel}
244
+ {...rest}
226
245
  >
227
246
  <span className={cn('flex-1 min-w-0 inline-flex items-center', nakedCellRowModeAlign)}>
228
247
  {isEmpty
@@ -371,7 +390,7 @@ const PeoplePicker = React.forwardRef<HTMLDivElement, PeoplePickerProps>(functio
371
390
  // **2026-05-15 Bug 3 fix(Claude+Codex Step 5 比稿 consensus)**:visible count 走 shared
372
391
  // `avatar-stack-overflow` primitive deterministic formula(取代 Combobox DOM offsetWidth-based
373
392
  // useOverflowCount + 60px fallback 不 deterministic),pass override 給 Combobox bypass internal
374
- // measurement。`MultiPersonDisplay`(display path)同 primitive,display + edit 結果一致。
393
+ // measurement。`MultiPersonDisplay`(view path)同 primitive,view + edit 結果一致。
375
394
  // 對齊 user verbatim SSOT「同 cell width 同 overflow 判斷」+ codex Q3 consensus shared primitive。
376
395
  const handleMultiChange = (next: string[]) => {
377
396
  onChange?.(next.map(name => findPerson(people, name)))
@@ -430,10 +449,10 @@ const PeoplePicker = React.forwardRef<HTMLDivElement, PeoplePickerProps>(functio
430
449
  // 既有 OverflowIndicator default 'circle' SSOT。
431
450
  overflowShape="circle"
432
451
  // 2026-05-15 Bug 3 fix:formula-based visible count override(避免 Combobox DOM measurement +
433
- // 60px fallback 不 deterministic)。SSOT in `./avatar-stack-overflow.ts`,display + edit 共用。
452
+ // 60px fallback 不 deterministic)。SSOT in `./avatar-stack-overflow.ts`,view + edit 共用。
434
453
  visibleCountOverride={stackVisibleCount}
435
454
  // 2026-05-14 I4 fix(per codex+Layer A 共識):hidden items 在 `+N` overflow popover 顯
436
- // Tag with avatar(對齊 display MultiPersonDisplay popover SSOT,user 抓 display vs edit
455
+ // Tag with avatar(對齊 view path MultiPersonDisplay popover SSOT,user 抓 display vs edit
437
456
  // overflow 視覺不一致)。
438
457
  renderHiddenTag={(item) => {
439
458
  const p = resolvePerson(findPerson(people, item.value))
@@ -186,7 +186,7 @@ function MultiPersonDisplay({
186
186
  max?: number
187
187
  /**
188
188
  * 2026-05-15 codex Round 5 C+ SSOT fix:`measured=true` 啟動 container-width 量測(取代 hardcode `max ?? 3`)
189
- * → display + edit stack 同 algorithm(同 cell width → 同 overflow 判斷),不再 display 用固定 3 vs edit
189
+ * → view + edit stack 同 algorithm(同 cell width → 同 overflow 判斷),不再 view 用固定 3 vs edit
190
190
  * 用 useOverflowCount。對齊 field-controls.spec.md:286 「4-mode 共享 renderer」contract + user round 3
191
191
  * verbatim「同空間兩判斷點」SSOT directive。Default false 保 backward compat(non-cell context 仍 max ?? 3)。
192
192
  */
@@ -201,8 +201,8 @@ function MultiPersonDisplay({
201
201
  // 切換時 hook 數 0↔3 變動必 crash(現靠 consumer 三元切換 element type 偶然避開)。hoist 到 return 前。
202
202
  // 2026-05-15 Bug 3 fix(Claude+Codex Step 5 比稿 consensus):消費 shared `avatar-stack-overflow`
203
203
  // primitive。原 inline canvas-based formula 是 dual-implementation 違反 user SSOT「同 cell width 同
204
- // overflow 判斷」(edit path 用 Combobox useOverflowCount DOM offsetWidth / display path 用 inline
205
- // canvas)。**抽 primitive 統一**:display + edit 共用 `getAvatarStackVisibleCount` formula。
204
+ // overflow 判斷」(edit path 用 Combobox useOverflowCount DOM offsetWidth / view path 用 inline
205
+ // canvas)。**抽 primitive 統一**:view + edit 共用 `getAvatarStackVisibleCount` formula。
206
206
  // SSOT in `./avatar-stack-overflow.ts`,M14 mechanical guard 防 future drift。
207
207
  const containerRef = React.useRef<HTMLSpanElement>(null)
208
208
  const [measuredCount, setMeasuredCount] = React.useState<number | null>(null)
@@ -161,14 +161,17 @@ export const PlacementMatrix: Story = {
161
161
  {(['top', 'right', 'bottom', 'left'] as const).flatMap(side =>
162
162
  (['start', 'center', 'end'] as const).map(align => (
163
163
  <div key={`${side}-${align}`} className="flex items-center gap-3 border border-dashed border-divider rounded-md p-3">
164
+ {/* side/align 屬定位參數,標在格外 caption(非浮層可見內容);浮層本體用真實使用情境 */}
165
+ <span className="text-caption font-mono text-fg-muted shrink-0 whitespace-nowrap">{side} / {align}</span>
166
+ {/* @overlay-open-skip:12-cell 定位矩陣示範 side×align 落點,全部 defaultOpen 會讓 12 個浮層互相重疊;點擊逐格開啟即可觀察定位,開啟態視覺覆蓋於 Inspector(defaultOpen) */}
164
167
  <Popover>
165
168
  <PopoverTrigger asChild>
166
- <Button variant="tertiary" size="sm" className="font-mono">{side}/{align}</Button>
169
+ <Button variant="tertiary" size="sm">使用說明</Button>
167
170
  </PopoverTrigger>
168
- <PopoverContent side={side} align={align}>
169
- <PopoverBody><div className="text-caption">side=<span className="font-mono">{side}</span>, align=<span className="font-mono">{align}</span></div></PopoverBody>
171
+ {/* naked popover(無 PopoverTitle)→ 自傳 aria-label 讓 role="dialog" 有 accessible name */}
172
+ <PopoverContent side={side} align={align} aria-label="使用說明">
173
+ <PopoverBody><div className="text-caption text-fg-secondary">拖曳卡片可調整優先順序,變更會即時同步給協作者。</div></PopoverBody>
170
174
  </PopoverContent>
171
- {/* each placement variant already wrapped in PopoverBody for consistent padding */}
172
175
  </Popover>
173
176
  </div>
174
177
  ))
@@ -218,13 +221,13 @@ export const SizeMatrix: Story = {
218
221
  <div className="flex items-center gap-3 border border-dashed border-divider rounded-md p-3">
219
222
  <span className="text-caption font-mono w-20">w-72(預設)</span>
220
223
  <Popover><PopoverTrigger asChild><Button variant="tertiary" size="sm">開啟</Button></PopoverTrigger>
221
- <PopoverContent><PopoverBody><div className="text-caption">預設寬度 w-72</div></PopoverBody></PopoverContent>
224
+ <PopoverContent aria-label="預設寬度示範"><PopoverBody><div className="text-caption">預設寬度 w-72</div></PopoverBody></PopoverContent>
222
225
  </Popover>
223
226
  </div>
224
227
  <div className="flex items-center gap-3 border border-dashed border-divider rounded-md p-3">
225
228
  <span className="text-caption font-mono w-20">w-96</span>
226
229
  <Popover><PopoverTrigger asChild><Button variant="tertiary" size="sm">開啟</Button></PopoverTrigger>
227
- <PopoverContent className="w-96"><PopoverBody><div className="text-caption">自訂 w-96</div></PopoverBody></PopoverContent>
230
+ <PopoverContent className="w-96" aria-label="自訂寬度示範"><PopoverBody><div className="text-caption">自訂 w-96</div></PopoverBody></PopoverContent>
228
231
  </Popover>
229
232
  </div>
230
233
  </div>
@@ -244,7 +247,7 @@ export const StateBehavior: Story = {
244
247
  <div className="flex flex-col gap-8">
245
248
  <div>
246
249
  <H3>開啟 / 關閉</H3>
247
- <Desc>Radix 提供 data-state 切換 + DS Tailwind animate 驅動(popover.tsx L70-71):進場 `fade-in + zoom-in-95 + slide-from-side`,離場鏡像。所有方向都使用 side-aware slide 動畫,浮層看起來從 trigger 「長出來」。</Desc>
250
+ <Desc>Radix 提供 data-state 切換 + DS Tailwind animate 驅動(popover.tsx L105-106):進場 `fade-in + zoom-in-95 + slide-from-side`,離場 `fade-out + zoom-out`(無 slide-out,對齊 shadcn 慣例)。進場方向 side-aware slide,浮層看起來從 trigger 「長出來」。</Desc>
248
251
  <ul className="text-caption text-fg-muted list-disc pl-5 space-y-1">
249
252
  <li>點擊 trigger → 開啟</li>
250
253
  <li>點 Popover 外部 → 關閉(click outside)</li>