@qijenchen/design-system 0.1.0-beta.85 → 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 (477) 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 +11 -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 +7 -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 +9 -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/Field/field-context.ts +17 -11
  323. package/src/components/Field/field-controls.spec.md +44 -37
  324. package/src/components/Field/field-types.ts +14 -12
  325. package/src/components/Field/field-wrapper.tsx +59 -43
  326. package/src/components/Field/field.anatomy.stories.tsx +6 -6
  327. package/src/components/Field/field.principles.stories.tsx +5 -28
  328. package/src/components/Field/field.spec.md +4 -4
  329. package/src/components/Field/field.stories.tsx +103 -72
  330. package/src/components/Field/field.tsx +36 -4
  331. package/src/components/Field/form-validation.spec.md +2 -0
  332. package/src/components/Field/use-form-validation.ts +1 -1
  333. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
  334. package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
  335. package/src/components/FileItem/file-item.tsx +10 -8
  336. package/src/components/FileUpload/file-upload.principles.stories.tsx +7 -7
  337. package/src/components/FileUpload/file-upload.spec.md +3 -2
  338. package/src/components/FileUpload/file-upload.tsx +5 -1
  339. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
  340. package/src/components/FileViewer/file-viewer.principles.stories.tsx +1 -1
  341. package/src/components/FileViewer/file-viewer.spec.md +1 -1
  342. package/src/components/FileViewer/file-viewer.stories.tsx +1 -1
  343. package/src/components/FileViewer/file-viewer.tsx +3 -2
  344. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -4
  345. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -1
  346. package/src/components/HoverCard/hover-card.spec.md +2 -2
  347. package/src/components/HoverCard/hover-card.tsx +4 -2
  348. package/src/components/InlineEdit/inline-edit.anatomy.stories.tsx +184 -0
  349. package/src/components/InlineEdit/inline-edit.principles.stories.tsx +142 -0
  350. package/src/components/InlineEdit/inline-edit.spec.md +71 -46
  351. package/src/components/InlineEdit/inline-edit.stories.tsx +20 -19
  352. package/src/components/InlineEdit/inline-edit.tsx +211 -125
  353. package/src/components/Input/input.anatomy.stories.tsx +15 -15
  354. package/src/components/Input/input.principles.stories.tsx +3 -3
  355. package/src/components/Input/input.spec.md +4 -4
  356. package/src/components/Input/input.stories.tsx +3 -3
  357. package/src/components/Input/input.tsx +13 -13
  358. package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -2
  359. package/src/components/LinkInput/link-input.principles.stories.tsx +1 -1
  360. package/src/components/LinkInput/link-input.spec.md +6 -2
  361. package/src/components/LinkInput/link-input.stories.tsx +8 -8
  362. package/src/components/LinkInput/link-input.tsx +21 -19
  363. package/src/components/Notice/notice.principles.stories.tsx +1 -1
  364. package/src/components/Notice/notice.tsx +8 -2
  365. package/src/components/NumberInput/number-input.anatomy.stories.tsx +6 -6
  366. package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
  367. package/src/components/NumberInput/number-input.spec.md +6 -6
  368. package/src/components/NumberInput/number-input.stories.tsx +4 -4
  369. package/src/components/NumberInput/number-input.tsx +17 -5
  370. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +8 -4
  371. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +6 -7
  372. package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
  373. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -30
  374. package/src/components/PeoplePicker/avatar-stack-overflow.ts +1 -1
  375. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +13 -10
  376. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +5 -1
  377. package/src/components/PeoplePicker/people-picker.stories.tsx +5 -5
  378. package/src/components/PeoplePicker/people-picker.tsx +39 -20
  379. package/src/components/PeoplePicker/person-display.tsx +3 -3
  380. package/src/components/Popover/popover.anatomy.stories.tsx +10 -7
  381. package/src/components/Popover/popover.principles.stories.tsx +49 -28
  382. package/src/components/Popover/popover.spec.md +4 -2
  383. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
  384. package/src/components/ProfileCard/profile-card.tsx +1 -1
  385. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +14 -13
  386. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +8 -23
  387. package/src/components/ProgressBar/progress-bar.stories.tsx +8 -2
  388. package/src/components/README.md +1 -0
  389. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +8 -3
  390. package/src/components/RadioGroup/radio-group.stories.tsx +3 -3
  391. package/src/components/RadioGroup/radio-group.tsx +19 -14
  392. package/src/components/Rating/rating.anatomy.stories.tsx +1 -1
  393. package/src/components/Rating/rating.principles.stories.tsx +3 -3
  394. package/src/components/Rating/rating.spec.md +2 -2
  395. package/src/components/Rating/rating.stories.tsx +4 -18
  396. package/src/components/Rating/rating.tsx +5 -1
  397. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
  398. package/src/components/ScrollArea/scroll-area.spec.md +1 -1
  399. package/src/components/ScrollArea/scroll-area.tsx +5 -2
  400. package/src/components/SegmentedControl/segmented-control.principles.stories.tsx +4 -1
  401. package/src/components/SegmentedControl/segmented-control.spec.md +5 -10
  402. package/src/components/SegmentedControl/segmented-control.tsx +6 -3
  403. package/src/components/Select/select.anatomy.stories.tsx +13 -7
  404. package/src/components/Select/select.principles.stories.tsx +3 -3
  405. package/src/components/Select/select.spec.md +9 -1
  406. package/src/components/Select/select.stories.tsx +41 -27
  407. package/src/components/Select/select.tsx +44 -26
  408. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +44 -26
  409. package/src/components/SelectMenu/select-menu.principles.stories.tsx +0 -1
  410. package/src/components/SelectMenu/select-menu.stories.tsx +7 -19
  411. package/src/components/SelectMenu/select-menu.tsx +38 -5
  412. package/src/components/SelectionControl/boolean-value.tsx +2 -2
  413. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
  414. package/src/components/SelectionControl/selection-item.principles.stories.tsx +4 -4
  415. package/src/components/Separator/separator.principles.stories.tsx +1 -1
  416. package/src/components/Sheet/sheet.principles.stories.tsx +5 -3
  417. package/src/components/Sheet/sheet.tsx +4 -1
  418. package/src/components/Sidebar/sidebar.tsx +1 -1
  419. package/src/components/Slider/slider.anatomy.stories.tsx +5 -4
  420. package/src/components/Slider/slider.principles.stories.tsx +6 -6
  421. package/src/components/Slider/slider.spec.md +13 -1
  422. package/src/components/Slider/slider.stories.tsx +44 -32
  423. package/src/components/Slider/slider.tsx +13 -4
  424. package/src/components/Steps/steps.anatomy.stories.tsx +7 -7
  425. package/src/components/Steps/steps.principles.stories.tsx +1 -1
  426. package/src/components/Steps/steps.spec.md +4 -2
  427. package/src/components/Steps/steps.stories.tsx +4 -56
  428. package/src/components/Steps/steps.tsx +31 -7
  429. package/src/components/Switch/switch.anatomy.stories.tsx +3 -3
  430. package/src/components/Switch/switch.principles.stories.tsx +1 -1
  431. package/src/components/Switch/switch.spec.md +4 -4
  432. package/src/components/Switch/switch.stories.tsx +3 -3
  433. package/src/components/Switch/switch.tsx +16 -10
  434. package/src/components/Tabs/tabs.anatomy.stories.tsx +6 -0
  435. package/src/components/Tabs/tabs.principles.stories.tsx +10 -2
  436. package/src/components/Tabs/tabs.spec.md +5 -2
  437. package/src/components/Tabs/tabs.stories.tsx +36 -44
  438. package/src/components/Tabs/tabs.tsx +101 -36
  439. package/src/components/Tag/tag.anatomy.stories.tsx +2 -2
  440. package/src/components/Tag/tag.principles.stories.tsx +4 -4
  441. package/src/components/Tag/tag.spec.md +3 -1
  442. package/src/components/Tag/tag.tsx +5 -3
  443. package/src/components/Textarea/textarea.anatomy.stories.tsx +6 -6
  444. package/src/components/Textarea/textarea.spec.md +5 -5
  445. package/src/components/Textarea/textarea.tsx +24 -24
  446. package/src/components/TimePicker/time-picker.anatomy.stories.tsx +12 -11
  447. package/src/components/TimePicker/time-picker.principles.stories.tsx +37 -44
  448. package/src/components/TimePicker/time-picker.spec.md +6 -5
  449. package/src/components/TimePicker/time-picker.stories.tsx +3 -3
  450. package/src/components/TimePicker/time-picker.tsx +24 -14
  451. package/src/components/Toast/toast.principles.stories.tsx +1 -1
  452. package/src/components/Toast/toast.stories.tsx +1 -1
  453. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +15 -6
  454. package/src/components/Tooltip/tooltip.principles.stories.tsx +4 -4
  455. package/src/components/Tooltip/tooltip.spec.md +9 -3
  456. package/src/components/Tooltip/tooltip.stories.tsx +1 -1
  457. package/src/components/Tooltip/tooltip.tsx +12 -2
  458. package/src/components/TreeView/tree-view.anatomy.stories.tsx +16 -14
  459. package/src/components/TreeView/tree-view.principles.stories.tsx +44 -11
  460. package/src/components/TreeView/tree-view.spec.md +8 -6
  461. package/src/components/TreeView/tree-view.stories.tsx +9 -43
  462. package/src/components/TreeView/tree-view.tsx +36 -0
  463. package/src/index.ts +13 -10
  464. package/src/lib/drag-visual.ts +2 -2
  465. package/src/lib/utils.ts +19 -0
  466. package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
  467. package/src/patterns/element-anatomy/item-anatomy.tsx +4 -2
  468. package/src/patterns/header-canonical/header-canonical.spec.md +9 -0
  469. package/src/patterns/header-canonical/header-canonical.stories.tsx +31 -5
  470. package/src/tokens/categorical-color.ts +5 -5
  471. package/src/tokens/color/color.spec.md +4 -4
  472. package/src/tokens/color/primitives.css +9 -2
  473. package/src/tokens/opacity/opacity.spec.md +1 -1
  474. package/src/tokens/orphan-tokens.spec.md +1 -1
  475. package/src/tokens/typography/typography.spec.md +2 -0
  476. package/src/tokens/uiSize/uiSize.css +5 -0
  477. package/src/tokens/uiSize/uiSize.spec.md +1 -1
@@ -1,6 +1,7 @@
1
1
  // @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.
2
- // code-quality-allow: file-size — foundational composite(Tabs + overflow scroll mode + dropdown switcher + inline action slot)在單一 wrapper SSOT 內,拆分會破壞 a11y / focus management chain。當前 515 < cap 800。
2
+ // code-quality-allow: file-size — foundational composite(Tabs + overflow scroll mode + dropdown switcher + inline action slot)在單一 wrapper SSOT 內,拆分會破壞 a11y / focus management chain。當前 ~578 < cap 800。
3
3
  import * as React from 'react'
4
+ import { createPortal } from 'react-dom'
4
5
  import * as TabsPrimitive from '@radix-ui/react-tabs'
5
6
  import { cva } from 'class-variance-authority'
6
7
  import type { LucideIcon } from 'lucide-react'
@@ -55,6 +56,15 @@ interface TabsContextValue {
55
56
  }
56
57
  const TabsContext = React.createContext<TabsContextValue>({ size: 'md' })
57
58
 
59
+ // ── Inline-action overlay context(2026-07-18 決策1,axe aria-required-children 正規修)──
60
+ // role=tablist 只能擁有 role=tab;per-tab inlineAction(如「更多 ▾」dropdown)若 DOM-在 tablist 內
61
+ // = critical 違規(axe 實測確認)。Radix 要求 trigger 為 TabsList 的 React 子代(roving focus
62
+ // context),故 trigger 必 DOM-在 tablist 內;唯一正規解 = 把 action **portal 出 tablist** 到一個
63
+ // sibling overlay 層,以 trigger 的量測座標定位(涵蓋 3 種 overflow 模式,viewport-relative)。
64
+ // aria-owns 不可行:axe required-children 計 DOM-owned 子代,不因 aria-owns 排除。
65
+ // 代價(user 2026-07-18 接受):鍵盤 Tab 序 = 全部 tab 後才到 action(action 現為 overlay 內獨立 tab stop)。
66
+ const TabsActionOverlayContext = React.createContext<HTMLDivElement | null>(null)
67
+
58
68
  // ── Root ──
59
69
  // Wrap Radix Tabs 以支援 value/onValueChange 的 context pass-through
60
70
  // (menu 模式的 overflow items 需要能 proxy click 觸發同一個 onValueChange)
@@ -170,27 +180,26 @@ const TabsList = React.forwardRef<
170
180
  TabsListProps
171
181
  >(({ className, size = 'sm', overflow = 'none', children, ...props }, ref) => {
172
182
  const tabsSizeContext = React.useMemo(() => ({ size }), [size])
183
+ // Overlay 層(決策1):per-tab inlineAction 由 TabsTrigger portal 到此,DOM-在 tablist 外,
184
+ // 以量測座標定位在對應 tab 之上。pointer-events-none(action span 自帶 auto);z 疊在 tab 之上。
185
+ const [overlayEl, setOverlayEl] = React.useState<HTMLDivElement | null>(null)
186
+
187
+ let listNode: React.ReactNode
173
188
  if (overflow === 'scroll') {
174
- return (
175
- <TabsContext.Provider value={tabsSizeContext}>
176
- <ScrollTabsList ref={ref} className={className} {...props}>
177
- {children}
178
- </ScrollTabsList>
179
- </TabsContext.Provider>
189
+ listNode = (
190
+ <ScrollTabsList ref={ref} className={className} {...props}>
191
+ {children}
192
+ </ScrollTabsList>
180
193
  )
181
- }
182
- if (overflow === 'menu') {
183
- return (
184
- <TabsContext.Provider value={tabsSizeContext}>
185
- <MenuTabsList ref={ref} className={className} {...props}>
186
- {children}
187
- </MenuTabsList>
188
- </TabsContext.Provider>
194
+ } else if (overflow === 'menu') {
195
+ listNode = (
196
+ <MenuTabsList ref={ref} className={className} {...props}>
197
+ {children}
198
+ </MenuTabsList>
189
199
  )
190
- }
191
- // none(預設)
192
- return (
193
- <TabsContext.Provider value={tabsSizeContext}>
200
+ } else {
201
+ // none(預設)
202
+ listNode = (
194
203
  <TabsPrimitive.List
195
204
  ref={ref}
196
205
  className={cn(TABS_LIST_BASE, 'w-full', className)}
@@ -198,6 +207,18 @@ const TabsList = React.forwardRef<
198
207
  >
199
208
  {children}
200
209
  </TabsPrimitive.List>
210
+ )
211
+ }
212
+
213
+ return (
214
+ <TabsContext.Provider value={tabsSizeContext}>
215
+ <TabsActionOverlayContext.Provider value={overlayEl}>
216
+ <div className="relative">
217
+ {listNode}
218
+ {/* inline-action overlay:sibling of tablist(非其 DOM 子代)→ 修 axe aria-required-children */}
219
+ <div ref={setOverlayEl} className="pointer-events-none absolute inset-0 z-10" />
220
+ </div>
221
+ </TabsActionOverlayContext.Provider>
201
222
  </TabsContext.Provider>
202
223
  )
203
224
  })
@@ -449,7 +470,10 @@ const tabsTriggerVariants = cva(
449
470
  )
450
471
 
451
472
  interface TabsTriggerProps
452
- extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> {
473
+ // asChild Omit(2026-07-18 決策2):Tab trigger 固定渲染 [startIcon?, <span>{children}</span>,
474
+ // suffix?] 多節點陣列 → Radix Slot 的 React.Children.only 恆 crash(每個 tab 都會)。
475
+ // children(label)保留合法;TabsRoot / TabsList 為真容器不收窄。
476
+ extends Omit<React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>, 'asChild'> {
453
477
  /** 左側 icon(LucideIcon) */
454
478
  startIcon?: LucideIcon
455
479
  /** 右側 badge(通常是計數指示器) */
@@ -481,23 +505,56 @@ const TabsTrigger = React.forwardRef<
481
505
  TabsTriggerProps
482
506
  >(({ className, startIcon: StartIcon, badge, endIcon: EndIcon, inlineAction, children, ...props }, ref) => {
483
507
  const { size } = React.useContext(TabsContext)
508
+ const overlayEl = React.useContext(TabsActionOverlayContext)
484
509
  // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)
485
510
  const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']
486
511
  const hasSuffix = badge != null || EndIcon !== undefined
487
- // 2026-07-05 D4 修(deep-audit「Tabs:inlineAction slot 造成 button button」):
488
- // Radix TabsTrigger render 原生 <button>,inlineAction(ItemInlineActionButton)也是原生
489
- // <button> 巢狀 interactive = HTML 不合法 + axe nested-interactive(serious)+ tab 的
490
- // accessible name 混入 action 內容。改 sibling 佈局( SidebarMenuButton suffixNode
491
- // canonical,sidebar.tsx):wrapper span(relative)內 trigger + 絕對定位 action;trigger
492
- // paddingRight 預留 action 空間 — icon 寬 + 原 gap(有 badge/endIcon 時 4px = suffix 內
493
- // gap-1;僅 action 時 8px = slot 間 gap-2),幾何與巢狀版一致。click 天然分流(action 不在
494
- // trigger 子樹,不冒泡),原 stopPropagation 五連發不再需要。
512
+ // inlineAction 幾何:trigger paddingRight 預留 action 空間 — icon 寬 + 原 gap(有 badge/endIcon
513
+ // 4px = suffix 內 gap-1;僅 action 時 8px = slot 間 gap-2)。2026-07-05 修 button-巢-button
514
+ // (sibling 佈局);2026-07-18 決策1 再進化:action 不再是 tablist DOM 內的 sibling(axe
515
+ // aria-required-children critical),改 portal TabsList overlay 層(tablist 外),以量測座標
516
+ // 定位在 trigger 右緣。trigger 仍在 tablist 內(Radix roving focus 要求)。
495
517
  const hasAction = inlineAction != null
496
518
  const actionPaddingRight = hasAction ? iconSize + (hasSuffix ? 4 : 8) : undefined
497
519
 
520
+ // 量測 trigger 位置(viewport-relative,涵蓋 none/scroll/menu 三模式),同步 overlay 內 action 座標。
521
+ const triggerElRef = React.useRef<HTMLButtonElement | null>(null)
522
+ const setTriggerRef = React.useCallback(
523
+ (el: HTMLButtonElement | null) => {
524
+ triggerElRef.current = el
525
+ if (typeof ref === 'function') ref(el)
526
+ else if (ref) (ref as React.MutableRefObject<HTMLButtonElement | null>).current = el
527
+ },
528
+ [ref],
529
+ )
530
+ const [actionPos, setActionPos] = React.useState<{ left: number; top: number; height: number } | null>(null)
531
+
532
+ React.useLayoutEffect(() => {
533
+ if (!hasAction || !overlayEl) return
534
+ const el = triggerElRef.current
535
+ if (!el) return
536
+ const compute = () => {
537
+ const t = triggerElRef.current?.getBoundingClientRect()
538
+ const o = overlayEl.getBoundingClientRect()
539
+ if (!t) return
540
+ setActionPos({ left: t.right - o.left, top: t.top - o.top, height: t.height })
541
+ }
542
+ compute()
543
+ const ro = new ResizeObserver(compute)
544
+ ro.observe(el)
545
+ ro.observe(overlayEl)
546
+ window.addEventListener('resize', compute)
547
+ window.addEventListener('scroll', compute, true) // capture:含 scroll-mode 捲動容器內部捲動
548
+ return () => {
549
+ ro.disconnect()
550
+ window.removeEventListener('resize', compute)
551
+ window.removeEventListener('scroll', compute, true)
552
+ }
553
+ }, [hasAction, overlayEl])
554
+
498
555
  const trigger = (
499
556
  <TabsPrimitive.Trigger
500
- ref={ref}
557
+ ref={setTriggerRef}
501
558
  className={cn(tabsTriggerVariants({ size }), className)}
502
559
  style={hasAction ? { paddingRight: actionPaddingRight } : undefined}
503
560
  {...props}
@@ -516,14 +573,22 @@ const TabsTrigger = React.forwardRef<
516
573
  if (!hasAction) return trigger
517
574
 
518
575
  return (
519
- // sibling 佈局 wrapper:span(relative)內 trigger + 絕對定位 action。
520
- // Radix roving focus 用 collection context 收 trigger,不要求 trigger 是 List 直接子層。
521
- <span className="relative inline-flex">
576
+ <>
522
577
  {trigger}
523
- <span className="absolute top-1/2 -translate-y-1/2 right-0 inline-flex items-center">
524
- {inlineAction}
525
- </span>
526
- </span>
578
+ {overlayEl &&
579
+ actionPos &&
580
+ createPortal(
581
+ // action portal 到 overlay(tablist 外);left=trigger.right + `-translate-x-full` → action
582
+ // 右緣對齊 trigger 右緣;top/height + items-center → 垂直置中(同原 right-0 + top-1/2 幾何)。
583
+ <span
584
+ className="pointer-events-auto absolute inline-flex -translate-x-full items-center"
585
+ style={{ left: actionPos.left, top: actionPos.top, height: actionPos.height }}
586
+ >
587
+ {inlineAction}
588
+ </span>,
589
+ overlayEl,
590
+ )}
591
+ </>
527
592
  )
528
593
  })
529
594
  TabsTrigger.displayName = 'TabsTrigger'
@@ -31,7 +31,7 @@ type SizeKey = 'sm' | 'md' | 'lg'
31
31
  type ColorSpec = { bg: string; text: string; border: string }
32
32
  type SolidSpec = 'subtle' | 'solid'
33
33
 
34
- // neutral + 全 12 色相(順序對齊 SSOT CATEGORICAL_HUES,1:1 primitives.css / color token 列)
34
+ // neutral + 全 12 色相(順序對齊 SSOT CATEGORICAL_HUES = categorical 指派序;與 primitives.css 檔內宣告序本就不同,見 categorical-color.ts )
35
35
  const VARIANTS: VariantKey[] = ['neutral', ...CATEGORICAL_HUES]
36
36
  const SIZES: SizeKey[] = ['sm', 'md', 'lg']
37
37
 
@@ -326,7 +326,7 @@ const InspectorInner = () => {
326
326
  onRemove={withDismiss ? () => {} : undefined}
327
327
  solid={solid}
328
328
  >
329
- Label
329
+ 設計
330
330
  </Tag>
331
331
  </div>
332
332
 
@@ -237,7 +237,7 @@ export const IconRule: Story = {
237
237
  <div>
238
238
  <Rule
239
239
  title="icon — 類別圖示(tag label 的視覺強化)"
240
- note="用 LucideIcon,Tag 統一 16pxicon 顏色繼承 Tag 文字色(有色時跟色)"
240
+ note="用 LucideIcon,Tag 一律固定 16px、不隨 Tag 尺寸縮放——讓所有 Tag 的 icon 前綴維持相同節奏,一整排並排掃視時對齊不跳動。icon 顏色繼承 Tag 文字色(有色 variant 就跟該色),而不是自成一色:避免 icon 變成 Tag 內第二個視覺焦點,搶走 label 的主體性。"
241
241
  >
242
242
  <div className="flex items-center gap-2 flex-wrap">
243
243
  <Tag color="blue" icon={TagIcon}>Feature</Tag>
@@ -247,13 +247,13 @@ export const IconRule: Story = {
247
247
 
248
248
  <Rule
249
249
  title="avatar — 人員 / 組織類 Tag 前綴"
250
- note="多選 Combobox 的已選成員用 avatar + name 組合"
250
+ note="多選 Combobox 的已選成員用 avatar + name 組合。avatar 只提供視覺辨識(一眼看到「是個人」),真正承載身份的是名字;兩者並用才能避免相似頭像、或圖片載入失敗時的誤認——所以 avatar Tag 一定要保留可見姓名,不能只放頭像。人員 / 組織標籤同理。"
251
251
  >
252
252
  <div className="flex items-center gap-2 flex-wrap">
253
- <Tag color="neutral" avatar={<img src="https://i.pravatar.cc/40?img=1" className="w-4 h-4 rounded-full" />}>
253
+ <Tag color="neutral" avatar={<img src="https://i.pravatar.cc/40?img=1" alt="" className="w-4 h-4 rounded-full" />}>
254
254
  Ada Chen
255
255
  </Tag>
256
- <Tag color="neutral" avatar={<img src="https://i.pravatar.cc/40?img=2" className="w-4 h-4 rounded-full" />}>
256
+ <Tag color="neutral" avatar={<img src="https://i.pravatar.cc/40?img=2" alt="" className="w-4 h-4 rounded-full" />}>
257
257
  Alice
258
258
  </Tag>
259
259
  </div>
@@ -220,6 +220,8 @@ Inline Action 的其他規則(尺寸、hover 背景 pattern)不變。
220
220
  | `icon` | `LucideIcon` | 左側 icon,Tag 統一 16px。與 avatar 互斥 |
221
221
  | `avatar` | `ReactNode` | 左側 avatar。與 icon 互斥 |
222
222
  | `onRemove` | `() => void` | 可移除——Tag 自動渲染 remove 按鈕並控制尺寸與互動樣式(從集合移除 item) |
223
+ | `removeAriaLabel` | `string` | remove 按鈕 aria-label 的目標名(對齊 FileUpload `removeAriaLabel` 同場景 + `<動作>AriaLabel` 命名慣例)。**children 為非字串 ReactNode 時必傳**,否則 SR 只讀得出泛稱「移除」、辨識不出移除哪個 Tag;children 為字串時預設取 children |
224
+ | ~~`dismissLabel`~~ | `string` | **@deprecated 2026-07-17 → `removeAriaLabel`**(dismiss 保留給 Alert/Notice/Toast「通知被忽略」;過渡一版仍可用) |
223
225
  | `solid` | `boolean` | 深底配對前景模式(step-6 背景;yellow/amber/orange/lime 用深字,其餘白字),預設 false |
224
226
  | `unbounded` | `boolean` | 預設 false = 寬度 cap 160px;true = 寬度交給 parent constrain(cell-as-input narrow cell 場景),詳「邊界案例」 |
225
227
 
@@ -271,7 +273,7 @@ Tag 是**純顯示 indicator**(Family 3 indicator variant,見「與 Button 的
271
273
  Tag 是**純視覺 indicator**(非互動 control,互動版本是 Chip),預設 ARIA 行為:
272
274
 
273
275
  - **靜態 Tag**(無 `onRemove`):render 為 `<div data-tag-root>`(無 role、`inline-flex`,SR 等價於無語意 generic container,視覺上為 inline-level 跟隨 inline flow)。若 Tag 表達狀態語意(「進行中」「已逾期」),consumer 應在父 region 用 `aria-label` 或視覺 prefix(icon)讓 SR 理解 context
274
- - **Removable Tag**(`onRemove` 存在):inline X close 走 `<ItemInlineActionButton icon={X} size="md" aria-label="移除 {tagLabel}" />`(item-anatomy SSOT 的原生 `<button>`,非 `Button` 元件)— 對齊 inline-action canonical(`patterns/element-anatomy/inline-action.spec.md`)
276
+ - **Removable Tag**(`onRemove` 存在):inline X close 走 `<ItemInlineActionButton icon={X} size="md" aria-label="移除 {name}" />`(item-anatomy SSOT 的原生 `<button>`,非 `Button` 元件)— 對齊 inline-action canonical(`patterns/element-anatomy/inline-action.spec.md`)。**名稱來源**:`removeAriaLabel` prop 優先(舊名 `dismissLabel` deprecated 仍支援),否則取字串 children;**若 children 是非字串 ReactNode(如 avatar + 排版節點)且未傳 `removeAriaLabel`,aria-label 會退化成泛稱「移除」**——多個 Tag 並排時 SR 無法區分移除哪一個,故非字串 children 必傳 `removeAriaLabel`
275
277
  - **Keyboard**:靜態 Tag 不取得 focus(無互動);**例外:截斷時條件性 `tabIndex=0`**(2026-07-06 拍板)——全文 tooltip 需鍵盤可達,APG Tooltip pattern 原文「popup 在元素收到鍵盤焦點或 hover 時顯示」= trigger 可 focus 是 W3C 官方前提;Radix Tooltip focus-open 自動生效,未截斷不入 tab 序。dismissible 的 X 走原生 button 鍵盤(Tab → focus → Enter/Space → dismiss)
276
278
  - **Color-only 語意警告**:Tag 用色相區分狀態時必有文字 label 或 prefix icon(色盲 user 友好);對齊 WCAG 1.4.1「不僅靠顏色傳達」
277
279
  - **驗證**:Storybook a11y addon panel 0 critical violation;WCAG AA contrast ≥ 4.5:1(text)/ 3:1(icon)
@@ -68,7 +68,9 @@ export interface TagProps
68
68
  avatar?: React.ReactNode
69
69
  /** 可移除——Tag 自動渲染 remove 按鈕並控制尺寸與互動樣式(從集合移除 item) */
70
70
  onRemove?: () => void
71
- /** remove 按鈕的 aria-label 目標名(a11y)。children 為非字串 ReactNode 時建議傳,否則 SR 讀不出移除哪個 tag。預設取 string children。 */
71
+ /** remove 按鈕的 aria-label 目標名(a11y)。children 為非字串 ReactNode 時建議傳,否則 SR 讀不出移除哪個 tag。預設取 string children。對齊 FileUpload `removeAriaLabel`(同「移除集合項目」場景 + DS `<動作>AriaLabel` 命名慣例)。 */
72
+ removeAriaLabel?: string
73
+ /** @deprecated 2026-07-17 改名為 `removeAriaLabel`(對齊 FileUpload + onRemove callback 語意;dismiss 保留給 Alert/Notice/Toast「通知被忽略」)。舊名過渡一版後移除。 */
72
74
  dismissLabel?: string
73
75
  /** 深底模式(step-6 背景 + on-emphasis 配對前景;亮色 hue yellow/amber/orange/lime 用深字 --on-emphasis-dark,green 白字例外) */
74
76
  solid?: boolean
@@ -123,7 +125,7 @@ function TagDismiss({ onRemove, label, solid, color }: { onRemove: () => void; l
123
125
  }
124
126
 
125
127
  function TagInner(
126
- { className, color, size, icon: Icon, avatar, onRemove, dismissLabel, solid, unbounded = false, children, style, ...props }: TagProps,
128
+ { className, color, size, icon: Icon, avatar, onRemove, removeAriaLabel, dismissLabel, solid, unbounded = false, children, style, ...props }: TagProps,
127
129
  forwardedRef: React.ForwardedRef<HTMLDivElement>,
128
130
  ) {
129
131
  const solidClass = solid ? SOLID_CLASSES[color ?? 'neutral'] : undefined
@@ -202,7 +204,7 @@ function TagInner(
202
204
  {Icon && <Icon size={16} aria-hidden />}
203
205
  {avatar && <span className="shrink-0 w-4 h-4 rounded-full overflow-hidden inline-grid place-content-center [&>*]:w-full [&>*]:h-full">{avatar}</span>}
204
206
  <span data-tag-text="" className="px-1 truncate min-w-0">{children}</span>
205
- {onRemove && <TagDismiss onRemove={onRemove} label={dismissLabel || label} solid={solid} color={color ?? 'neutral'} />}
207
+ {onRemove && <TagDismiss onRemove={onRemove} label={removeAriaLabel ?? dismissLabel ?? label} solid={solid} color={color ?? 'neutral'} />}
206
208
  </div>
207
209
  )
208
210
 
@@ -34,7 +34,7 @@ export const Overview: Story = {
34
34
  <thead><tr><Th>Prop</Th><Th>Type</Th><Th>Default</Th><Th>說明</Th></tr></thead>
35
35
  <tbody>
36
36
  {[
37
- ['mode', "'edit' | 'display' | 'readonly' | 'disabled'", "'edit'", 'Field mode(display 渲染純文字 div;readonly 用底色 + padding 標示閱讀區)'],
37
+ ['mode', "'edit' | 'view' | 'readonly' | 'disabled'", "'edit'", 'Field mode(view 渲染純文字 div;readonly 用底色 + padding 標示閱讀區)'],
38
38
  ['size', "'sm' | 'md' | 'lg'", "'md'", 'sm/md text-body,lg text-body-lg'],
39
39
  ['rows', 'number', '3', '預設可見行數'],
40
40
  ['placeholder', 'string', '—', '空值提示'],
@@ -82,7 +82,7 @@ export const Inspector: Story = {
82
82
  defaultValue: '',
83
83
  },
84
84
  argTypes: {
85
- mode: { control: 'radio', options: ['edit', 'display', 'readonly', 'disabled'] },
85
+ mode: { control: 'radio', options: ['edit', 'view', 'readonly', 'disabled'] },
86
86
  size: { control: 'radio', options: ['sm', 'md', 'lg'] },
87
87
  rows: { control: { type: 'number', min: 1, max: 20 } },
88
88
  error: { control: 'boolean', description: 'error 視覺(border-error + aria-invalid)' },
@@ -126,10 +126,10 @@ export const ModeMatrix: Story = {
126
126
  />
127
127
  </div>
128
128
  <div>
129
- <H3>display — 純展示(渲 div 取代 textarea,whitespace-pre-wrap 保留多行)</H3>
129
+ <H3>view — 純展示(渲 div 取代 textarea,whitespace-pre-wrap 保留多行)</H3>
130
130
  <Desc>對齊 Carbon read-only / Cloudscape display mode。常見於檢視已提交的留言 / 評論。</Desc>
131
131
  <Textarea
132
- mode="display"
132
+ mode="view"
133
133
  rows={3}
134
134
  value="我覺得這個專案非常有潛力,建議加強 onboarding 的引導流程..."
135
135
  />
@@ -201,11 +201,11 @@ export const ColorMatrix: Story = {
201
201
  <Td><TokenCell token="--fg-muted" display="fg-muted" /></Td>
202
202
  </tr>
203
203
  <tr>
204
- <Td mono>display</Td>
204
+ <Td mono>view</Td>
205
205
  <Td>透明(zero-chrome,host context 提供邊界)</Td>
206
206
  <Td>透明(無邊框)</Td>
207
207
  <Td><TokenCell token="--foreground" display="foreground" /></Td>
208
- <Td>—(display 無 placeholder;空值顯半形 -(hyphen)foreground)</Td>
208
+ <Td>—(view 無 placeholder;空值顯半形 -(hyphen)foreground)</Td>
209
209
  </tr>
210
210
  <tr>
211
211
  <Td mono>readonly</Td>
@@ -27,7 +27,7 @@ benchmark:
27
27
 
28
28
  Textarea 是**多行文字**的輸入與顯示元件——Input 的多行版本。格式化邏輯為 identity(value → value)。
29
29
 
30
- **Layout Family**:Family 4(Field control)— multi-line variant。**繼承** Family 4 的 token(字體 / padding / border / state 色彩),但**結構簡化為單一 native `<textarea>`**(edit 態裸 `<textarea>`、display 態裸 `<div>`)——無 fieldWrapper composite、無 startIcon / content / endAction 多 slot anatomy(見下方「與 Input 的差異」表與「禁止事項」)。高度可隨 rows / resize-y 擴展、不受 `--field-height-*` 約束。
30
+ **Layout Family**:Family 4(Field control)— multi-line variant。**繼承** Family 4 的 token(字體 / padding / border / state 色彩),但**結構簡化為單一 native `<textarea>`**(edit 態裸 `<textarea>`、view 態裸 `<div>`)——無 fieldWrapper composite、無 startIcon / content / endAction 多 slot anatomy(見下方「與 Input 的差異」表與「禁止事項」)。高度可隨 rows / resize-y 擴展、不受 `--field-height-*` 約束。
31
31
 
32
32
  **實作基礎**:native `<textarea>` + 橋接 DS token,無 external primitive base。shadcn 同類做法。
33
33
 
@@ -71,7 +71,7 @@ Textarea 是**多行文字**的輸入與顯示元件——Input 的多行版本
71
71
 
72
72
  - **`rows` prop**:預設 `3`,控制初始可見行數
73
73
  - **`resize-y`**:使用者可手動拖拉下邊緣垂直 resize
74
- - **`variant="naked"` 例外**:cell-as-input substrate(DataTable cell)四 mode 一律 `!resize-none` 覆蓋 `resize-y`,並以 `!h-full` 撐滿 cell——cell 內手動 resize 會破壞表格佈局(`textarea.tsx` naked compoundVariants,intentional divergence)
74
+ - **`variant="naked"` 例外**:cell-as-input substrate(DataTable cell)naked 僅 edit/view 兩 mode(readonly/disabled×naked 死格 2026-07-16 移除),一律 `!resize-none` 覆蓋 `resize-y`,並以 `!h-full` 撐滿 cell——cell 內手動 resize 會破壞表格佈局(`textarea.tsx` naked compoundVariants,intentional divergence)
75
75
  - **`min-h-*` className**:消費者可透過 Tailwind utility 覆寫最小高度
76
76
  - **禁止 `resize: horizontal` 或 `both`**:水平 resize 破壞 form 佈局
77
77
 
@@ -104,12 +104,12 @@ native `<textarea>` 自帶 `value` / `defaultValue` / `onChange` triplet — Tex
104
104
 
105
105
  ### Readonly 特例
106
106
 
107
- 不同於 Input 的 readonly(同高度、緊湊底色),`variant="default"` 的 Textarea readonly **保留 padding,並填上 `bg-readonly` 底色標示閱讀區**——多行內容需要明確的閱讀區域邊界訊號,沒有這塊填色區會無法與周圍純文字內容區分。`variant="naked"`(cell-as-input substrate)readonly 透明且 `!px-0 !py-0`——邊界訊號由 host context(table cell)提供(`textarea.tsx` compoundVariants,intentional divergence;`bare` variant 2026-07-09 退役)。
107
+ 不同於 Input 的 readonly(同高度、緊湊底色),`variant="default"` 的 Textarea readonly **保留 padding,並填上 `bg-readonly` 底色標示閱讀區**——多行內容需要明確的閱讀區域邊界訊號,沒有這塊填色區會無法與周圍純文字內容區分。`variant="naked"`(cell-as-input substrate)view 態透明且 `!px-0 !py-0`——邊界訊號由 host context(table cell)提供(`textarea.tsx` compoundVariants,intentional divergence;readonly/disabled×naked 死格 2026-07-16 移除;`bare` variant 2026-07-09 退役)。
108
108
 
109
109
  ### 邊界案例
110
110
 
111
- - **空值**:edit 態顯示 placeholder;display / readonly 態 value 為空(`null` / `''`)時渲染半形 `-`(`EMPTY_DISPLAY`)+ `text-foreground`(disabled → fg-disabled)
112
- - **極長文字**:edit 態高度固定於 rows / resize 結果,內容超出時 native 內部捲動;display 態 `<div>` 隨內容增高(`whitespace-pre-wrap break-words`),無截斷
111
+ - **空值**:edit 態顯示 placeholder;view / readonly 態 value 為空(`null` / `''`)時渲染半形 `-`(`EMPTY_DISPLAY`)+ `text-foreground`(disabled → fg-disabled)
112
+ - **極長文字**:edit 態高度固定於 rows / resize 結果,內容超出時 native 內部捲動;view 態 `<div>` 隨內容增高(`whitespace-pre-wrap break-words`),無截斷
113
113
  - **resize 上限**:`resize-y` 預設無上限;需限制時 consumer 傳 `max-h-*` className(native resize 受 max-height 約束)
114
114
 
115
115
  ---
@@ -11,12 +11,12 @@ import { useControllable } from '@/design-system/hooks/use-controllable'
11
11
  * Textarea — 多行文字輸入
12
12
  *
13
13
  * ── 定位 ────────────────────────────────────────────────────────────────
14
- * 多行版本的 Input,edit / display / readonly / disabled 四態與 Input 邏輯一致(Phase B1 2026-05-05)。
14
+ * 多行版本的 Input,edit / view / readonly / disabled 四態與 Input 邏輯一致(Phase B1 2026-05-05)。
15
15
  * 不同於 Input:
16
16
  * - 沒有固定 field-height(高度由 rows 或 min-h 決定)
17
17
  * - 沒有 startIcon / endAction(textarea 慣例不放 icon)
18
18
  * - readonly 呈現保留邊框與 padding,只改底色,讓多行文字有合理閱讀區
19
- * - display 渲染 <div> + white-space:pre-wrap 保留多行文本
19
+ * - view 渲染 <div> + white-space:pre-wrap 保留多行文本
20
20
  *
21
21
  * ── Padding 規則 ───────────────────────────────────────────────────────
22
22
  * 多行內容必須有上下內距才能閱讀舒適。不沿用 Input 的 items-center,
@@ -48,7 +48,7 @@ const textareaVariants = cva(
48
48
  variants: {
49
49
  mode: {
50
50
  edit: '',
51
- display: '',
51
+ view: '',
52
52
  readonly: '',
53
53
  disabled: '',
54
54
  },
@@ -80,11 +80,13 @@ const textareaVariants = cva(
80
80
  },
81
81
  { mode: 'edit', variant: 'default', error: false, className: 'focus-within:!border-primary focus-within:hover:!border-primary' },
82
82
  {
83
- mode: 'display',
83
+ // 2026-07-16 round16 Model A(user GO,推翻 2026-05-13 Path Ⅰ 的 `!px-0 !py-0`):
84
+ // Textarea view×default = **edit 幾何減 chrome** — 保留 base `px-[var(--field-px)] py-2`(= edit 內距),
85
+ // 只拔 border/bg。多行 read↔edit 零跳(view 與 edit 同一顆 Textarea、py-2 天生一致)。跟 Field wrapper
86
+ // view×default 同 SSOT(field-wrapper.tsx)。
87
+ mode: 'view',
84
88
  variant: 'default',
85
- // 2026-05-13 Q3 Path Ⅰ:Textarea default display zero chrome,!px-0 !py-0 override base `px-[var(--field-px)] py-2`
86
- // (跟 Input 同 SSOT,per field-controls.spec.md (d))
87
- className: 'bg-transparent border border-transparent !px-0 !py-0',
89
+ className: 'bg-transparent border border-transparent',
88
90
  },
89
91
  {
90
92
  mode: 'readonly',
@@ -118,7 +120,7 @@ const textareaVariants = cva(
118
120
  '!px-[var(--table-cell-px)] !py-[var(--table-cell-py)]',
119
121
  'border border-border',
120
122
  'hover:border-border-hover',
121
- // textarea UA stylesheet 預設 line-height: normal(1.2-1.5 不定),會跟 display
123
+ // textarea UA stylesheet 預設 line-height: normal(1.2-1.5 不定),會跟 view
122
124
  // `<div>` text-body line-height: 1.5(21px @ 14px)不一致 → cell 進 edit 後 height
123
125
  // shift。顯式 leading 對齊 div 行為。
124
126
  '!leading-[1.5]',
@@ -126,16 +128,14 @@ const textareaVariants = cva(
126
128
  },
127
129
  { mode: 'edit', variant: 'naked', error: false, className: 'focus-visible:!border-primary focus-visible:hover:!border-primary' },
128
130
  // 2026-05-13 Q1 R4 verify(per codex Q1 verdict 補 Textarea nuance):
129
- // Textarea naked display/readonly/disabled 用 `!h-full`,**不**對齊 Field wrapper 的 `!h-auto`。
131
+ // Textarea view×naked 用 `!h-full`,**不**對齊 Field wrapper 的 `!h-auto`。
130
132
  // Why divergence:textarea 是 native form element 帶 intrinsic rows-based height,且 cell 內
131
133
  // multi-line text 需要撐滿 cell 而非依 line-height intrinsic。`!h-full` 讓 textarea 填滿 cell
132
134
  // 高度,文字 anchored to cell.top + cell padding(同視覺結果 Field wrapper autoRow !h-auto)。
133
135
  // 此 divergence intentional + documented;非 SSOT violation。
134
- { mode: 'display', variant: 'naked', className: 'bg-transparent !rounded-none !h-full !resize-none !px-0 !py-0 border border-transparent !leading-[1.5]' },
135
- { mode: 'readonly', variant: 'naked', className: 'bg-transparent !rounded-none !h-full !resize-none !px-0 !py-0 border border-transparent !leading-[1.5]' },
136
- // 2026-05-13 codex V2 fix:移除 `opacity-disabled` blanket(對齊 field-wrapper.tsx naked R3 fix +
137
- // color.spec.md:729 逃生艙 rule)。Textarea 已用具體 `text-fg-disabled` token,不需要 wrapper opacity。
138
- { mode: 'disabled', variant: 'naked', className: 'bg-transparent !rounded-none cursor-not-allowed text-fg-disabled !h-full !resize-none !px-0 !py-0 border border-transparent !leading-[1.5]' },
136
+ { mode: 'view', variant: 'naked', className: 'bg-transparent !rounded-none !h-full !resize-none !px-0 !py-0 border border-transparent !leading-[1.5]' },
137
+ // 2026-07-16 round16:`readonly×naked` / `disabled×naked` 死格移除(鏡射 field-wrapper.tsx;
138
+ // DataTable cell disabled + readonly 從不入 naked,全庫 0 消費)。
139
139
  ],
140
140
  defaultVariants: {
141
141
  mode: 'edit',
@@ -149,7 +149,7 @@ const textareaVariants = cva(
149
149
  export interface TextareaProps
150
150
  extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>,
151
151
  Omit<VariantProps<typeof textareaVariants>, 'mode' | 'variant'> {
152
- /** Field display mode */
152
+ /** Field view mode */
153
153
  mode?: FieldMode
154
154
  /**
155
155
  * Visual chrome(正交於 mode);Phase B1(2026-05-05)新增。透傳自 FieldContext.variant,per-prop override。
@@ -193,22 +193,22 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
193
193
  const resolvedMode: FieldMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly })
194
194
  const emptyDisplay = useFieldEmptyDisplay()
195
195
  const isEditable = resolvedMode === 'edit'
196
- const isDisplay = resolvedMode === 'display'
196
+ const isView = resolvedMode === 'view'
197
197
  const inputId = idProp ?? fieldCtx?.id
198
198
  const ariaDescribedBy = ariaDescribedByProp ?? fieldCtx?.descriptionId
199
199
  const ariaErrorMessage = ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined)
200
200
 
201
- // ── display mode(+ readonly 空值)純展示,渲染 <div> 取代 <textarea>(white-space:pre-wrap 保留多行) ──
201
+ // ── view mode(+ readonly 空值)純展示,渲染 <div> 取代 <textarea>(white-space:pre-wrap 保留多行) ──
202
202
  // 對齊 Carbon read-only / Cloudscape display-mode
203
- // 2026-07-08 user 拍板:readonly 空值也走 display-div 顯 '-'(readonly **有值** 仍走下方
203
+ // 2026-07-08 user 拍板:readonly 空值也走 view-div 顯 '-'(readonly **有值** 仍走下方
204
204
  // native <textarea readOnly> 保留選取/複製語意 — field-controls.spec.md「null / undefined 值」)。
205
- // 2026-07-13 fix:空值判斷需認得 uncontrolled defaultValue — readonly / display 也要顯示內容,
205
+ // 2026-07-13 fix:空值判斷需認得 uncontrolled defaultValue — readonly / view 也要顯示內容,
206
206
  // 不可誤判為空值渲 '-'(原為靜態 value ?? defaultValue)。
207
207
  // 2026-07-14 R2(dual-model consensus,同 Input input.tsx):resolved value 改走 useControllable
208
208
  // 內部 SSOT(Radix idiom)。原靜態版有 typing-stale gap:uncontrolled 打字後外部切
209
- // display / readonly 仍顯示 stale defaultValue。uncontrolled 時 native textarea 由
209
+ // view / readonly 仍顯示 stale defaultValue。uncontrolled 時 native textarea 由
210
210
  // value={resolved} 內部驅動(defaultValue 只作初始值,不落 DOM attribute),同時修
211
- // remount-stale(切 display 再回 edit 從 defaultValue attribute 重掛)+ form.reset() stale
211
+ // remount-stale(切 view 再回 edit 從 defaultValue attribute 重掛)+ form.reset() stale
212
212
  // (HTML 標準 reset 不發 input event,下方 reset bridge 修)。controlled(傳 value)時
213
213
  // useControllable 純 passthrough,行為與原本完全一致。consumer onChange 不接進 hook
214
214
  // (保留 native event signature),於 <textarea> onChange 另行轉發。
@@ -218,10 +218,10 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
218
218
  defaultValue: defaultValue ?? '',
219
219
  })
220
220
  const displayValue = resolved != null && resolved !== '' ? String(resolved) : null
221
- const showDisplaySpan = isDisplay || (resolvedMode === 'readonly' && displayValue == null)
221
+ const showDisplaySpan = isView || (resolvedMode === 'readonly' && displayValue == null)
222
222
 
223
223
  // form.reset() bridge(uncontrolled only):reset 恢復 defaultValue 但不發 input event →
224
- // 手動把 resolved 歸位 defaultValue。keyed on showDisplaySpan:display 分支不掛 native
224
+ // 手動把 resolved 歸位 defaultValue。keyed on showDisplaySpan:view 分支不掛 native
225
225
  // textarea,回 edit 重掛時 effect 重跑補掛 listener。
226
226
  const innerRef = React.useRef<HTMLTextAreaElement | null>(null)
227
227
  React.useEffect(() => {
@@ -240,7 +240,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
240
240
  }, [ref])
241
241
 
242
242
  if (showDisplaySpan) {
243
- const spanMode = isDisplay ? 'display' : 'readonly'
243
+ const spanMode = isView ? 'view' : 'readonly'
244
244
  return (
245
245
  <div
246
246
  id={inputId}
@@ -28,7 +28,7 @@ type Story = StoryObj<typeof TimePicker>
28
28
 
29
29
  export const Overview: Story = {
30
30
  name: '元件總覽',
31
- render: () => <TimePicker value="09:00" onChange={() => {}} />,
31
+ render: () => <TimePicker value="09:00" onChange={() => {}} aria-label="時間選擇器總覽" />,
32
32
  }
33
33
 
34
34
  /**
@@ -105,9 +105,9 @@ export const ColorMatrix: Story = {
105
105
  </table>
106
106
  </div>
107
107
  <div className="flex gap-3 mt-3">
108
- <TimePicker value="09:00" onChange={() => {}} />
109
- <TimePicker value="09:00" onChange={() => {}} error />
110
- <TimePicker value="09:00" onChange={() => {}} disabled />
108
+ <TimePicker value="09:00" onChange={() => {}} aria-label="時間欄位配色(預設)" />
109
+ <TimePicker value="09:00" onChange={() => {}} error aria-label="時間欄位配色(錯誤狀態)" />
110
+ <TimePicker value="09:00" onChange={() => {}} disabled aria-label="時間欄位配色(停用)" />
111
111
  </div>
112
112
  </div>
113
113
 
@@ -169,15 +169,15 @@ export const SizeMatrix: Story = {
169
169
  <div className="flex flex-col gap-3">
170
170
  <div className="flex items-center gap-3">
171
171
  <span className="w-12 text-caption text-fg-muted">sm</span>
172
- <TimePicker size="sm" value="09:00" onChange={() => {}} />
172
+ <TimePicker size="sm" value="09:00" onChange={() => {}} aria-label="小尺寸時間選擇器" />
173
173
  </div>
174
174
  <div className="flex items-center gap-3">
175
175
  <span className="w-12 text-caption text-fg-muted">md ★default</span>
176
- <TimePicker size="md" value="09:00" onChange={() => {}} />
176
+ <TimePicker size="md" value="09:00" onChange={() => {}} aria-label="中尺寸時間選擇器" />
177
177
  </div>
178
178
  <div className="flex items-center gap-3">
179
179
  <span className="w-12 text-caption text-fg-muted">lg</span>
180
- <TimePicker size="lg" value="09:00" onChange={() => {}} />
180
+ <TimePicker size="lg" value="09:00" onChange={() => {}} aria-label="大尺寸時間選擇器" />
181
181
  </div>
182
182
  </div>
183
183
  ),
@@ -205,15 +205,15 @@ export const PrecisionMatrix: Story = {
205
205
  <div className="flex flex-col gap-3 w-80">
206
206
  <div>
207
207
  <div className="text-caption text-fg-muted mb-1">兩欄(showSeconds=false,預設)</div>
208
- <TimePicker value="09:00" onChange={() => {}} />
208
+ <TimePicker value="09:00" onChange={() => {}} aria-label="時分兩欄時間選擇器" />
209
209
  </div>
210
210
  <div>
211
211
  <div className="text-caption text-fg-muted mb-1">三欄(showSeconds=true)</div>
212
- <TimePicker value="09:00:00" onChange={() => {}} showSeconds />
212
+ <TimePicker value="09:00:00" onChange={() => {}} showSeconds aria-label="時分秒三欄時間選擇器" />
213
213
  </div>
214
214
  <div>
215
215
  <div className="text-caption text-fg-muted mb-1">minuteStep=15(會議常用)</div>
216
- <TimePicker value="09:00" onChange={() => {}} minuteStep={15} />
216
+ <TimePicker value="09:00" onChange={() => {}} minuteStep={15} aria-label="15 分鐘間隔時間選擇器" />
217
217
  </div>
218
218
  <div>
219
219
  <div className="text-caption text-fg-muted mb-1">disabledTime:0-5 點不可選</div>
@@ -221,6 +221,7 @@ export const PrecisionMatrix: Story = {
221
221
  value="09:00"
222
222
  onChange={() => {}}
223
223
  disabledTime={() => ({ disabledHours: [0, 1, 2, 3, 4, 5] })}
224
+ aria-label="限制可選時段的時間選擇器"
224
225
  />
225
226
  </div>
226
227
  </div>
@@ -246,7 +247,7 @@ export const Inspector: Story = {
246
247
  },
247
248
  render: (args) => {
248
249
  const [v, setV] = React.useState(args.value ?? '')
249
- return <TimePicker {...args} value={v} onChange={setV} />
250
+ return <TimePicker {...args} value={v} onChange={setV} aria-label="時間選擇器檢閱器" />
250
251
  },
251
252
  }
252
253