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

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 (604) hide show
  1. package/CLAUDE.md +8 -8
  2. package/README.md +3 -2
  3. package/dist/components/Alert/alert.js.map +1 -1
  4. package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
  5. package/dist/components/AppShell/app-shell.d.ts +1 -1
  6. package/dist/components/AppShell/app-shell.d.ts.map +1 -1
  7. package/dist/components/AppShell/app-shell.js +22 -2
  8. package/dist/components/AppShell/app-shell.js.map +1 -1
  9. package/dist/components/Avatar/avatar.d.ts.map +1 -1
  10. package/dist/components/Avatar/avatar.js +4 -4
  11. package/dist/components/Avatar/avatar.js.map +1 -1
  12. package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
  13. package/dist/components/Breadcrumb/breadcrumb.js +1 -1
  14. package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
  15. package/dist/components/Button/button.d.ts +2 -2
  16. package/dist/components/Button/button.d.ts.map +1 -1
  17. package/dist/components/Button/button.js +12 -3
  18. package/dist/components/Button/button.js.map +1 -1
  19. package/dist/components/Calendar/calendar.d.ts +13 -5
  20. package/dist/components/Calendar/calendar.d.ts.map +1 -1
  21. package/dist/components/Calendar/calendar.js +7 -1
  22. package/dist/components/Calendar/calendar.js.map +1 -1
  23. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  24. package/dist/components/Carousel/carousel.js +8 -3
  25. package/dist/components/Carousel/carousel.js.map +1 -1
  26. package/dist/components/Chart/chart.d.ts.map +1 -1
  27. package/dist/components/Chart/chart.js +1 -0
  28. package/dist/components/Chart/chart.js.map +1 -1
  29. package/dist/components/Checkbox/checkbox.d.ts +1 -1
  30. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  31. package/dist/components/Checkbox/checkbox.js +8 -4
  32. package/dist/components/Checkbox/checkbox.js.map +1 -1
  33. package/dist/components/Chip/chip.d.ts +1 -1
  34. package/dist/components/Chip/chip.d.ts.map +1 -1
  35. package/dist/components/Chip/chip.js.map +1 -1
  36. package/dist/components/Coachmark/coachmark.d.ts +10 -3
  37. package/dist/components/Coachmark/coachmark.d.ts.map +1 -1
  38. package/dist/components/Coachmark/coachmark.js.map +1 -1
  39. package/dist/components/Combobox/combobox.d.ts +5 -1
  40. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  41. package/dist/components/Combobox/combobox.js +30 -13
  42. package/dist/components/Combobox/combobox.js.map +1 -1
  43. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  44. package/dist/components/DataTable/cell-registry.js +53 -62
  45. package/dist/components/DataTable/cell-registry.js.map +1 -1
  46. package/dist/components/DataTable/column-types.d.ts +10 -4
  47. package/dist/components/DataTable/column-types.d.ts.map +1 -1
  48. package/dist/components/DataTable/column-types.js.map +1 -1
  49. package/dist/components/DataTable/data-table-column-visibility-panel.d.ts +1 -1
  50. package/dist/components/DataTable/data-table-column-visibility-panel.js +183 -0
  51. package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -0
  52. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  53. package/dist/components/DataTable/data-table-filter-panel.js +607 -0
  54. package/dist/components/DataTable/data-table-filter-panel.js.map +1 -0
  55. package/dist/components/DataTable/data-table-interaction-layer.d.ts +11 -15
  56. package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
  57. package/dist/components/DataTable/data-table-interaction-layer.js +1 -3
  58. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  59. package/dist/components/DataTable/data-table-sort-manager.js +140 -0
  60. package/dist/components/DataTable/data-table-sort-manager.js.map +1 -0
  61. package/dist/components/DataTable/data-table.d.ts +5 -6
  62. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  63. package/dist/components/DataTable/data-table.js +36 -15
  64. package/dist/components/DataTable/data-table.js.map +1 -1
  65. package/dist/components/DataTable/filter-operators.js +140 -0
  66. package/dist/components/DataTable/filter-operators.js.map +1 -0
  67. package/dist/components/DataTable/filter-tree.js +312 -0
  68. package/dist/components/DataTable/filter-tree.js.map +1 -0
  69. package/dist/components/DataTable/index.d.ts +3 -0
  70. package/dist/components/DataTable/index.d.ts.map +1 -1
  71. package/dist/components/DataTable/index.js +12 -1
  72. package/dist/components/DataTable/index.js.map +1 -1
  73. package/dist/components/DataTable/lib/column-meta.js +25 -0
  74. package/dist/components/DataTable/lib/column-meta.js.map +1 -0
  75. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  76. package/dist/components/DateGrid/date-grid.js +3 -1
  77. package/dist/components/DateGrid/date-grid.js.map +1 -1
  78. package/dist/components/DatePicker/date-picker.d.ts +7 -1
  79. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  80. package/dist/components/DatePicker/date-picker.js +105 -74
  81. package/dist/components/DatePicker/date-picker.js.map +1 -1
  82. package/dist/components/Dialog/dialog.d.ts +20 -3
  83. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  84. package/dist/components/Dialog/dialog.js +38 -20
  85. package/dist/components/Dialog/dialog.js.map +1 -1
  86. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  87. package/dist/components/DropdownMenu/dropdown-menu.js +3 -1
  88. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  89. package/dist/components/Field/field-context.d.ts +45 -5
  90. package/dist/components/Field/field-context.d.ts.map +1 -1
  91. package/dist/components/Field/field-context.js +19 -0
  92. package/dist/components/Field/field-context.js.map +1 -1
  93. package/dist/components/Field/field-edit-keys.d.ts +21 -0
  94. package/dist/components/Field/field-edit-keys.d.ts.map +1 -0
  95. package/dist/components/Field/field-edit-keys.js +19 -0
  96. package/dist/components/Field/field-edit-keys.js.map +1 -0
  97. package/dist/components/Field/field-types.d.ts +20 -1
  98. package/dist/components/Field/field-types.d.ts.map +1 -1
  99. package/dist/components/Field/field-types.js.map +1 -1
  100. package/dist/components/Field/field-wrapper.d.ts +3 -2
  101. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  102. package/dist/components/Field/field-wrapper.js +38 -39
  103. package/dist/components/Field/field-wrapper.js.map +1 -1
  104. package/dist/components/Field/field.d.ts +2 -1
  105. package/dist/components/Field/field.d.ts.map +1 -1
  106. package/dist/components/Field/field.js.map +1 -1
  107. package/dist/components/FieldControlGroup/field-control-group.d.ts +1 -1
  108. package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
  109. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  110. package/dist/components/FileItem/file-item.js +1 -1
  111. package/dist/components/FileItem/file-item.js.map +1 -1
  112. package/dist/components/FileUpload/file-upload.d.ts +1 -1
  113. package/dist/components/FileUpload/file-upload.js.map +1 -1
  114. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  115. package/dist/components/FileViewer/file-viewer.js +7 -2
  116. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  117. package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
  118. package/dist/components/FileViewer/image-renderer.js +22 -1
  119. package/dist/components/FileViewer/image-renderer.js.map +1 -1
  120. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  121. package/dist/components/HoverCard/hover-card.js +5 -3
  122. package/dist/components/HoverCard/hover-card.js.map +1 -1
  123. package/dist/components/InlineEdit/index.d.ts +2 -0
  124. package/dist/components/InlineEdit/index.d.ts.map +1 -0
  125. package/dist/components/InlineEdit/index.js +5 -0
  126. package/dist/components/InlineEdit/index.js.map +1 -0
  127. package/dist/components/InlineEdit/inline-edit.d.ts +59 -0
  128. package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -0
  129. package/dist/components/InlineEdit/inline-edit.js +164 -0
  130. package/dist/components/InlineEdit/inline-edit.js.map +1 -0
  131. package/dist/components/Input/input.d.ts +6 -11
  132. package/dist/components/Input/input.d.ts.map +1 -1
  133. package/dist/components/Input/input.js +43 -13
  134. package/dist/components/Input/input.js.map +1 -1
  135. package/dist/components/LinkInput/link-input.d.ts +2 -2
  136. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  137. package/dist/components/LinkInput/link-input.js +6 -5
  138. package/dist/components/LinkInput/link-input.js.map +1 -1
  139. package/dist/components/Menu/menu-item.d.ts +14 -2
  140. package/dist/components/Menu/menu-item.d.ts.map +1 -1
  141. package/dist/components/Menu/menu-item.js +25 -3
  142. package/dist/components/Menu/menu-item.js.map +1 -1
  143. package/dist/components/NumberInput/number-input.d.ts +3 -3
  144. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  145. package/dist/components/NumberInput/number-input.js +6 -5
  146. package/dist/components/NumberInput/number-input.js.map +1 -1
  147. package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
  148. package/dist/components/OverflowIndicator/overflow-indicator.js +2 -6
  149. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  150. package/dist/components/Pagination/pagination.js +1 -1
  151. package/dist/components/Pagination/pagination.js.map +1 -1
  152. package/dist/components/PeoplePicker/index.js +7 -1
  153. package/dist/components/PeoplePicker/index.js.map +1 -1
  154. package/dist/components/PeoplePicker/people-picker.d.ts +9 -2
  155. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  156. package/dist/components/PeoplePicker/people-picker.js +23 -10
  157. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  158. package/dist/components/PeoplePicker/person-display.d.ts +6 -3
  159. package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
  160. package/dist/components/PeoplePicker/person-display.js +19 -14
  161. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  162. package/dist/components/Popover/popover.d.ts.map +1 -1
  163. package/dist/components/Popover/popover.js +3 -1
  164. package/dist/components/Popover/popover.js.map +1 -1
  165. package/dist/components/ProfileCard/profile-card.d.ts +2 -2
  166. package/dist/components/ProfileCard/profile-card.js +3 -3
  167. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  168. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  169. package/dist/components/RadioGroup/radio-group.js +15 -9
  170. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  171. package/dist/components/ScrollArea/scroll-area.d.ts +11 -1
  172. package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
  173. package/dist/components/ScrollArea/scroll-area.js +6 -3
  174. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  175. package/dist/components/Select/select.d.ts +46 -5
  176. package/dist/components/Select/select.d.ts.map +1 -1
  177. package/dist/components/Select/select.js +109 -28
  178. package/dist/components/Select/select.js.map +1 -1
  179. package/dist/components/SelectMenu/select-menu.d.ts +3 -1
  180. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  181. package/dist/components/SelectMenu/select-menu.js +8 -1
  182. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  183. package/dist/components/SelectionControl/boolean-value.d.ts +11 -0
  184. package/dist/components/SelectionControl/boolean-value.d.ts.map +1 -0
  185. package/dist/components/SelectionControl/boolean-value.js +23 -0
  186. package/dist/components/SelectionControl/boolean-value.js.map +1 -0
  187. package/dist/components/SelectionControl/index.d.ts +1 -0
  188. package/dist/components/SelectionControl/index.d.ts.map +1 -1
  189. package/dist/components/SelectionControl/index.js +2 -0
  190. package/dist/components/SelectionControl/index.js.map +1 -1
  191. package/dist/components/Sheet/index.js +2 -1
  192. package/dist/components/Sheet/sheet.d.ts +7 -1
  193. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  194. package/dist/components/Sheet/sheet.js +5 -2
  195. package/dist/components/Sheet/sheet.js.map +1 -1
  196. package/dist/components/Sidebar/sidebar.d.ts +9 -4
  197. package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
  198. package/dist/components/Sidebar/sidebar.js +8 -10
  199. package/dist/components/Sidebar/sidebar.js.map +1 -1
  200. package/dist/components/Slider/slider.d.ts.map +1 -1
  201. package/dist/components/Slider/slider.js.map +1 -1
  202. package/dist/components/Switch/switch.d.ts +6 -6
  203. package/dist/components/Switch/switch.d.ts.map +1 -1
  204. package/dist/components/Switch/switch.js +3 -2
  205. package/dist/components/Switch/switch.js.map +1 -1
  206. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  207. package/dist/components/Tabs/tabs.js +15 -1
  208. package/dist/components/Tabs/tabs.js.map +1 -1
  209. package/dist/components/Textarea/textarea.d.ts +2 -2
  210. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  211. package/dist/components/Textarea/textarea.js +50 -42
  212. package/dist/components/Textarea/textarea.js.map +1 -1
  213. package/dist/components/TimePicker/time-columns.d.ts.map +1 -1
  214. package/dist/components/TimePicker/time-columns.js +42 -38
  215. package/dist/components/TimePicker/time-columns.js.map +1 -1
  216. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  217. package/dist/components/TimePicker/time-picker.js +8 -6
  218. package/dist/components/TimePicker/time-picker.js.map +1 -1
  219. package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
  220. package/dist/components/Tooltip/tooltip.js +7 -5
  221. package/dist/components/Tooltip/tooltip.js.map +1 -1
  222. package/dist/components/TreeView/tree-view.d.ts +56 -1
  223. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  224. package/dist/components/TreeView/tree-view.js +211 -12
  225. package/dist/components/TreeView/tree-view.js.map +1 -1
  226. package/dist/index.d.ts +115 -58
  227. package/dist/index.d.ts.map +1 -1
  228. package/dist/index.js +93 -108
  229. package/dist/index.js.map +1 -1
  230. package/dist/patterns/element-anatomy/item-anatomy.d.ts +7 -6
  231. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  232. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  233. package/dist/tokens/categorical-color.js +15 -0
  234. package/dist/tokens/categorical-color.js.map +1 -1
  235. package/dist/tokens/motion/motion.d.ts +7 -6
  236. package/dist/tokens/motion/motion.d.ts.map +1 -1
  237. package/dist/tokens/motion/motion.js +6 -6
  238. package/dist/tokens/motion/motion.js.map +1 -1
  239. package/dist/tokens/motion/overlay-motion.d.ts +19 -0
  240. package/dist/tokens/motion/overlay-motion.d.ts.map +1 -0
  241. package/dist/tokens/motion/overlay-motion.js +9 -0
  242. package/dist/tokens/motion/overlay-motion.js.map +1 -0
  243. package/dist/tokens/uiSize/icon-size.js +5 -0
  244. package/dist/tokens/uiSize/icon-size.js.map +1 -0
  245. package/ds-canonical/fork/governance.lock +31 -19
  246. package/ds-canonical/fork/hooks/_log-fire.sh +46 -0
  247. package/ds-canonical/fork/hooks/block_prototype_imports.py +15 -4
  248. package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +145 -13
  249. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +2 -1
  250. package/ds-canonical/fork/hooks/check_dynamic_tailwind_class.sh +47 -0
  251. package/ds-canonical/fork/hooks/check_escape_marker_abuse.sh +27 -0
  252. package/ds-canonical/fork/hooks/check_field_family_invariants.sh +61 -1
  253. package/ds-canonical/fork/hooks/check_layout_space_magic_numbers.sh +28 -4
  254. package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +28 -0
  255. package/ds-canonical/fork/hooks/check_story_determinism.sh +2 -0
  256. package/ds-canonical/fork/hooks/check_story_invariants.sh +119 -0
  257. package/ds-canonical/fork/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
  258. package/ds-canonical/fork/manifest.json +15 -0
  259. package/ds-canonical/fork/preamble.md +20 -18
  260. package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +3 -3
  261. package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +3 -3
  262. package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +1 -1
  263. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +3 -3
  264. package/ds-canonical/fork/skills/propose-options/SKILL.md +3 -3
  265. package/ds-canonical/fork/skills/prototype/SKILL.md +1 -1
  266. package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +2 -2
  267. package/ds-canonical/fork/skills/visual-audit/SKILL.md +2 -2
  268. package/ds-canonical/hooks/README.md +1 -1
  269. package/ds-canonical/hooks/block_prototype_imports.py +15 -4
  270. package/ds-canonical/hooks/check_audit_post_report_validator.sh +65 -7
  271. package/ds-canonical/hooks/check_codex_brief_invariants.sh +28 -1
  272. package/ds-canonical/hooks/check_consumer_app_invariants.sh +145 -13
  273. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +2 -1
  274. package/ds-canonical/hooks/check_dynamic_tailwind_class.sh +47 -0
  275. package/ds-canonical/hooks/check_escape_marker_abuse.sh +27 -0
  276. package/ds-canonical/hooks/check_field_family_invariants.sh +61 -1
  277. package/ds-canonical/hooks/check_layout_space_magic_numbers.sh +28 -4
  278. package/ds-canonical/hooks/check_naming_and_abstraction.sh +1 -1
  279. package/ds-canonical/hooks/check_opacity_token_usage.sh +28 -0
  280. package/ds-canonical/hooks/check_pixel_quantified_audit.sh +3 -2
  281. package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +6 -1
  282. package/ds-canonical/hooks/check_ssot_header_declaration.sh +3 -1
  283. package/ds-canonical/hooks/check_story_determinism.sh +2 -0
  284. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +8 -7
  285. package/ds-canonical/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
  286. package/ds-canonical/hooks/lib/_story_compile_drift.sh +1 -1
  287. package/ds-canonical/hooks/lib/_token_hygiene.sh +10 -1
  288. package/ds-canonical/hooks/stop_self_audit.sh +31 -0
  289. package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +24 -0
  290. package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +44 -0
  291. package/ds-canonical/hooks/tests/test_check_codex_brief_invariants.sh +43 -2
  292. package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +112 -0
  293. package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +9 -2
  294. package/ds-canonical/hooks/tests/test_check_dynamic_tailwind_class.sh +76 -0
  295. package/ds-canonical/hooks/tests/test_check_escape_marker_abuse.sh +24 -0
  296. package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +27 -2
  297. package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +39 -0
  298. package/ds-canonical/hooks/tests/test_check_tabs_content_chrome_body_double_gap.sh +76 -0
  299. package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
  300. package/ds-canonical/references/build-ui-canonicals.md +2 -1
  301. package/ds-canonical/references/failure-class-registry.json +145 -0
  302. package/ds-canonical/references/governance-audit-coverage.md +33 -0
  303. package/ds-canonical/references/item-anatomy-recipe.md +2 -2
  304. package/ds-canonical/references/scenario-definition.md +6 -9
  305. package/ds-canonical/references/ssot-consultation.md +2 -2
  306. package/ds-canonical/references/ssot-index.md +5 -4
  307. package/ds-canonical/references/story-baseline-registry.json +8 -2
  308. package/ds-canonical/references/structural-token-retention.md +6 -9
  309. package/ds-canonical/rules/meta-patterns.md +8 -8
  310. package/ds-canonical/rules/self-verify.md +4 -3
  311. package/ds-canonical/rules/story-rules.md +2 -2
  312. package/ds-canonical/rules/ui-development.md +2 -2
  313. package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +3 -3
  314. package/ds-canonical/skills/code-quality-audit/SKILL.md +3 -3
  315. package/ds-canonical/skills/codex-collab/SKILL.md +9 -7
  316. package/ds-canonical/skills/codex-collab/references/brief-template.md +1 -1
  317. package/ds-canonical/skills/codify-principle/SKILL.md +2 -2
  318. package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
  319. package/ds-canonical/skills/component-quality-gate/references/checklist.md +2 -2
  320. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +51 -58
  321. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +29 -0
  322. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +29 -10
  323. package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +21 -0
  324. package/ds-canonical/skills/delivery-handoff/SKILL.md +1 -1
  325. package/ds-canonical/skills/design-system-audit/SKILL.md +26 -25
  326. package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +13 -1
  327. package/ds-canonical/skills/design-system-audit/references/rule-placement.md +1 -1
  328. package/ds-canonical/skills/governance-health/SKILL.md +1 -1
  329. package/ds-canonical/skills/knowledge-prune/SKILL.md +3 -0
  330. package/ds-canonical/skills/knowledge-prune/references/prune-dimensions-d5-d10.md +1 -0
  331. package/ds-canonical/skills/new-component/SKILL.md +5 -5
  332. package/ds-canonical/skills/new-component/references/new-component-checklist.md +1 -1
  333. package/ds-canonical/skills/product-ui-audit/SKILL.md +3 -3
  334. package/ds-canonical/skills/propose-options/SKILL.md +3 -3
  335. package/ds-canonical/skills/prototype/SKILL.md +1 -1
  336. package/ds-canonical/skills/scan-similar-bugs/SKILL.md +2 -2
  337. package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +3 -3
  338. package/ds-canonical/skills/story-writing/SKILL.md +3 -3
  339. package/ds-canonical/skills/visual-audit/SKILL.md +2 -2
  340. package/ds-story-manifest.json +29 -20
  341. package/llms-full.txt +22 -1
  342. package/llms.txt +5 -4
  343. package/package.json +7 -3
  344. package/src/components/Accordion/accordion.principles.stories.tsx +1 -1
  345. package/src/components/Accordion/accordion.stories.tsx +16 -1
  346. package/src/components/Alert/alert.principles.stories.tsx +1 -1
  347. package/src/components/Alert/alert.spec.md +8 -10
  348. package/src/components/Alert/alert.stories.tsx +28 -28
  349. package/src/components/Alert/alert.tsx +1 -1
  350. package/src/components/AppShell/_demo-helpers.tsx +6 -3
  351. package/src/components/AppShell/app-shell.spec.md +4 -4
  352. package/src/components/AppShell/app-shell.tsx +28 -2
  353. package/src/components/AspectRatio/aspect-ratio.spec.md +1 -1
  354. package/src/components/AspectRatio/aspect-ratio.stories.tsx +7 -89
  355. package/src/components/Avatar/avatar.anatomy.stories.tsx +7 -3
  356. package/src/components/Avatar/avatar.principles.stories.tsx +19 -15
  357. package/src/components/Avatar/avatar.spec.md +2 -2
  358. package/src/components/Avatar/avatar.stories.tsx +8 -6
  359. package/src/components/Avatar/avatar.tsx +13 -5
  360. package/src/components/Badge/badge.principles.stories.tsx +1 -1
  361. package/src/components/Badge/badge.spec.md +2 -0
  362. package/src/components/Badge/badge.stories.tsx +4 -2
  363. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +6 -4
  364. package/src/components/Breadcrumb/breadcrumb.stories.tsx +6 -5
  365. package/src/components/Breadcrumb/breadcrumb.tsx +3 -2
  366. package/src/components/BulkActionBar/bulk-action-bar.spec.md +4 -2
  367. package/src/components/Button/button.principles.stories.tsx +1 -1
  368. package/src/components/Button/button.spec.md +2 -2
  369. package/src/components/Button/button.tsx +18 -3
  370. package/src/components/Calendar/calendar.anatomy.stories.tsx +12 -10
  371. package/src/components/Calendar/calendar.principles.stories.tsx +14 -6
  372. package/src/components/Calendar/calendar.spec.md +5 -4
  373. package/src/components/Calendar/calendar.stories.tsx +7 -4
  374. package/src/components/Calendar/calendar.tsx +26 -7
  375. package/src/components/Carousel/carousel.anatomy.stories.tsx +5 -4
  376. package/src/components/Carousel/carousel.spec.md +2 -2
  377. package/src/components/Carousel/carousel.tsx +11 -3
  378. package/src/components/Chart/chart.anatomy.stories.tsx +7 -4
  379. package/src/components/Chart/chart.principles.stories.tsx +1 -1
  380. package/src/components/Chart/chart.stories.tsx +2 -8
  381. package/src/components/Chart/chart.tsx +3 -0
  382. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +28 -49
  383. package/src/components/Checkbox/checkbox.principles.stories.tsx +1 -1
  384. package/src/components/Checkbox/checkbox.spec.md +11 -26
  385. package/src/components/Checkbox/checkbox.stories.tsx +11 -8
  386. package/src/components/Checkbox/checkbox.tsx +18 -13
  387. package/src/components/Chip/chip.anatomy.stories.tsx +44 -24
  388. package/src/components/Chip/chip.principles.stories.tsx +1 -1
  389. package/src/components/Chip/chip.spec.md +5 -4
  390. package/src/components/Chip/chip.tsx +5 -1
  391. package/src/components/CircularProgress/circular-progress.principles.stories.tsx +7 -4
  392. package/src/components/CircularProgress/circular-progress.spec.md +3 -3
  393. package/src/components/CircularProgress/circular-progress.stories.tsx +2 -2
  394. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +2 -2
  395. package/src/components/Coachmark/coachmark.principles.stories.tsx +2 -1
  396. package/src/components/Coachmark/coachmark.spec.md +5 -5
  397. package/src/components/Coachmark/coachmark.stories.tsx +3 -1
  398. package/src/components/Coachmark/coachmark.tsx +10 -3
  399. package/src/components/Combobox/combobox.anatomy.stories.tsx +2 -2
  400. package/src/components/Combobox/combobox.tsx +53 -19
  401. package/src/components/Command/command.anatomy.stories.tsx +2 -2
  402. package/src/components/Command/command.principles.stories.tsx +1 -1
  403. package/src/components/Command/command.stories.tsx +42 -10
  404. package/src/components/DataTable/cell-registry.tsx +86 -63
  405. package/src/components/DataTable/column-types.ts +10 -1
  406. package/src/components/DataTable/data-table-column-visibility-panel.tsx +1 -1
  407. package/src/components/DataTable/data-table-filter-panel.tsx +4 -1
  408. package/src/components/DataTable/data-table-interaction-layer.tsx +15 -19
  409. package/src/components/DataTable/data-table.anatomy.stories.tsx +15 -13
  410. package/src/components/DataTable/data-table.principles.stories.tsx +9 -9
  411. package/src/components/DataTable/data-table.spec.md +32 -12
  412. package/src/components/DataTable/data-table.stories.tsx +16 -17
  413. package/src/components/DataTable/data-table.tsx +103 -60
  414. package/src/components/DataTable/index.ts +3 -0
  415. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +4 -4
  416. package/src/components/DateGrid/date-grid.principles.stories.tsx +1 -1
  417. package/src/components/DateGrid/date-grid.spec.md +1 -1
  418. package/src/components/DateGrid/date-grid.tsx +3 -1
  419. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +8 -8
  420. package/src/components/DatePicker/date-picker.principles.stories.tsx +3 -3
  421. package/src/components/DatePicker/date-picker.spec.md +3 -3
  422. package/src/components/DatePicker/date-picker.stories.tsx +4 -5
  423. package/src/components/DatePicker/date-picker.tsx +99 -45
  424. package/src/components/DescriptionList/description-list.anatomy.stories.tsx +12 -10
  425. package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
  426. package/src/components/DescriptionList/description-list.spec.md +3 -3
  427. package/src/components/DescriptionList/description-list.stories.tsx +7 -6
  428. package/src/components/Dialog/dialog.anatomy.stories.tsx +1 -0
  429. package/src/components/Dialog/dialog.spec.md +9 -0
  430. package/src/components/Dialog/dialog.stories.tsx +66 -5
  431. package/src/components/Dialog/dialog.tsx +60 -25
  432. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
  433. package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +8 -6
  434. package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -5
  435. package/src/components/DropdownMenu/dropdown-menu.tsx +18 -9
  436. package/src/components/Empty/empty.anatomy.stories.tsx +1 -1
  437. package/src/components/Empty/empty.spec.md +2 -2
  438. package/src/components/Field/field-context.ts +69 -5
  439. package/src/components/Field/field-controls.spec.md +62 -8
  440. package/src/components/Field/field-edit-keys.ts +66 -0
  441. package/src/components/Field/field-types.ts +58 -9
  442. package/src/components/Field/field-wrapper.tsx +50 -42
  443. package/src/components/Field/field.anatomy.stories.tsx +9 -6
  444. package/src/components/Field/field.spec.md +1 -1
  445. package/src/components/Field/field.stories.tsx +1 -1
  446. package/src/components/Field/field.tsx +2 -1
  447. package/src/components/Field/form-validation.spec.md +3 -3
  448. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +6 -6
  449. package/src/components/FieldControlGroup/field-control-group.spec.md +9 -4
  450. package/src/components/FieldControlGroup/field-control-group.tsx +1 -1
  451. package/src/components/FileItem/file-item.anatomy.stories.tsx +1 -1
  452. package/src/components/FileItem/file-item.spec.md +5 -8
  453. package/src/components/FileItem/file-item.stories.tsx +36 -25
  454. package/src/components/FileItem/file-item.tsx +8 -6
  455. package/src/components/FileUpload/file-upload.anatomy.stories.tsx +6 -6
  456. package/src/components/FileUpload/file-upload.principles.stories.tsx +5 -5
  457. package/src/components/FileUpload/file-upload.spec.md +1 -1
  458. package/src/components/FileUpload/file-upload.tsx +1 -1
  459. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +4 -3
  460. package/src/components/FileViewer/file-viewer.principles.stories.tsx +3 -3
  461. package/src/components/FileViewer/file-viewer.spec.md +30 -12
  462. package/src/components/FileViewer/file-viewer.stories.tsx +1 -2
  463. package/src/components/FileViewer/file-viewer.tsx +12 -4
  464. package/src/components/FileViewer/image-renderer.tsx +35 -7
  465. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -2
  466. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -3
  467. package/src/components/HoverCard/hover-card.spec.md +5 -5
  468. package/src/components/HoverCard/hover-card.stories.tsx +16 -16
  469. package/src/components/HoverCard/hover-card.tsx +5 -3
  470. package/src/components/InlineEdit/index.ts +5 -0
  471. package/src/components/InlineEdit/inline-edit.spec.md +149 -0
  472. package/src/components/InlineEdit/inline-edit.stories.tsx +246 -0
  473. package/src/components/InlineEdit/inline-edit.tsx +287 -0
  474. package/src/components/Input/input.anatomy.stories.tsx +8 -7
  475. package/src/components/Input/input.principles.stories.tsx +2 -12
  476. package/src/components/Input/input.spec.md +13 -18
  477. package/src/components/Input/input.tsx +69 -22
  478. package/src/components/LinkInput/link-input.anatomy.stories.tsx +1 -1
  479. package/src/components/LinkInput/link-input.principles.stories.tsx +2 -2
  480. package/src/components/LinkInput/link-input.tsx +11 -10
  481. package/src/components/Menu/menu-item.anatomy.stories.tsx +3 -3
  482. package/src/components/Menu/menu-item.principles.stories.tsx +1 -1
  483. package/src/components/Menu/menu-item.spec.md +22 -15
  484. package/src/components/Menu/menu-item.stories.tsx +39 -11
  485. package/src/components/Menu/menu-item.tsx +38 -4
  486. package/src/components/Notice/notice.principles.stories.tsx +1 -1
  487. package/src/components/Notice/notice.spec.md +16 -3
  488. package/src/components/NumberInput/number-input.anatomy.stories.tsx +3 -3
  489. package/src/components/NumberInput/number-input.principles.stories.tsx +1 -1
  490. package/src/components/NumberInput/number-input.spec.md +4 -4
  491. package/src/components/NumberInput/number-input.tsx +19 -12
  492. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +10 -8
  493. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +3 -3
  494. package/src/components/OverflowIndicator/overflow-indicator.spec.md +4 -2
  495. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +71 -63
  496. package/src/components/OverflowIndicator/overflow-indicator.tsx +9 -8
  497. package/src/components/Pagination/pagination.spec.md +1 -1
  498. package/src/components/Pagination/pagination.stories.tsx +2 -6
  499. package/src/components/Pagination/pagination.tsx +1 -1
  500. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +4 -4
  501. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +2 -2
  502. package/src/components/PeoplePicker/people-picker.spec.md +6 -6
  503. package/src/components/PeoplePicker/people-picker.tsx +56 -16
  504. package/src/components/PeoplePicker/person-display.tsx +32 -16
  505. package/src/components/Popover/popover.anatomy.stories.tsx +2 -2
  506. package/src/components/Popover/popover.tsx +3 -1
  507. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +12 -23
  508. package/src/components/ProfileCard/profile-card.spec.md +3 -3
  509. package/src/components/ProfileCard/profile-card.tsx +6 -6
  510. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +3 -1
  511. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +1 -1
  512. package/src/components/ProgressBar/progress-bar.stories.tsx +50 -42
  513. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +10 -28
  514. package/src/components/RadioGroup/radio-group.stories.tsx +29 -32
  515. package/src/components/RadioGroup/radio-group.tsx +41 -15
  516. package/src/components/Rating/rating.spec.md +11 -3
  517. package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
  518. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
  519. package/src/components/ScrollArea/scroll-area.spec.md +7 -3
  520. package/src/components/ScrollArea/scroll-area.stories.tsx +1 -1
  521. package/src/components/ScrollArea/scroll-area.tsx +17 -4
  522. package/src/components/Select/select.anatomy.stories.tsx +3 -3
  523. package/src/components/Select/select.spec.md +27 -17
  524. package/src/components/Select/select.stories.tsx +33 -0
  525. package/src/components/Select/select.tsx +190 -36
  526. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +4 -4
  527. package/src/components/SelectMenu/select-menu.spec.md +15 -7
  528. package/src/components/SelectMenu/select-menu.tsx +16 -3
  529. package/src/components/SelectionControl/boolean-value.tsx +39 -0
  530. package/src/components/SelectionControl/index.ts +1 -0
  531. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +2 -2
  532. package/src/components/SelectionControl/selection-item.principles.stories.tsx +2 -2
  533. package/src/components/SelectionControl/selection-item.spec.md +3 -3
  534. package/src/components/SelectionControl/selection-item.stories.tsx +3 -3
  535. package/src/components/Separator/separator.spec.md +2 -2
  536. package/src/components/Separator/separator.stories.tsx +6 -27
  537. package/src/components/Sheet/sheet.anatomy.stories.tsx +1 -1
  538. package/src/components/Sheet/sheet.principles.stories.tsx +1 -1
  539. package/src/components/Sheet/sheet.spec.md +2 -2
  540. package/src/components/Sheet/sheet.tsx +9 -5
  541. package/src/components/Sidebar/sidebar.anatomy.stories.tsx +16 -12
  542. package/src/components/Sidebar/sidebar.principles.stories.tsx +10 -8
  543. package/src/components/Sidebar/sidebar.spec.md +13 -15
  544. package/src/components/Sidebar/sidebar.stories.tsx +8 -7
  545. package/src/components/Sidebar/sidebar.tsx +37 -25
  546. package/src/components/Skeleton/skeleton.stories.tsx +4 -11
  547. package/src/components/Slider/slider.anatomy.stories.tsx +3 -3
  548. package/src/components/Slider/slider.principles.stories.tsx +2 -2
  549. package/src/components/Slider/slider.spec.md +5 -5
  550. package/src/components/Slider/slider.tsx +3 -1
  551. package/src/components/Steps/steps.anatomy.stories.tsx +4 -4
  552. package/src/components/Steps/steps.principles.stories.tsx +1 -1
  553. package/src/components/Steps/steps.spec.md +1 -1
  554. package/src/components/Switch/switch.anatomy.stories.tsx +7 -7
  555. package/src/components/Switch/switch.principles.stories.tsx +4 -4
  556. package/src/components/Switch/switch.spec.md +8 -8
  557. package/src/components/Switch/switch.stories.tsx +9 -24
  558. package/src/components/Switch/switch.tsx +17 -13
  559. package/src/components/Tabs/tabs.anatomy.stories.tsx +10 -7
  560. package/src/components/Tabs/tabs.spec.md +1 -1
  561. package/src/components/Tabs/tabs.tsx +21 -1
  562. package/src/components/Textarea/textarea.anatomy.stories.tsx +1 -1
  563. package/src/components/Textarea/textarea.principles.stories.tsx +1 -1
  564. package/src/components/Textarea/textarea.spec.md +4 -4
  565. package/src/components/Textarea/textarea.tsx +72 -46
  566. package/src/components/TimePicker/time-columns.tsx +6 -2
  567. package/src/components/TimePicker/time-picker.principles.stories.tsx +1 -1
  568. package/src/components/TimePicker/time-picker.spec.md +3 -3
  569. package/src/components/TimePicker/time-picker.tsx +16 -10
  570. package/src/components/Toast/toast.stories.tsx +7 -4
  571. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +4 -4
  572. package/src/components/Tooltip/tooltip.principles.stories.tsx +3 -3
  573. package/src/components/Tooltip/tooltip.spec.md +1 -1
  574. package/src/components/Tooltip/tooltip.stories.tsx +4 -39
  575. package/src/components/Tooltip/tooltip.tsx +8 -6
  576. package/src/components/TreeView/tree-view.anatomy.stories.tsx +6 -3
  577. package/src/components/TreeView/tree-view.spec.md +42 -11
  578. package/src/components/TreeView/tree-view.stories.tsx +1 -0
  579. package/src/components/TreeView/tree-view.tsx +322 -17
  580. package/src/index.ts +584 -60
  581. package/src/patterns/action-bar/action-bar.spec.md +9 -0
  582. package/src/patterns/action-bar/action-bar.stories.tsx +6 -3
  583. package/src/patterns/element-anatomy/inline-action.spec.md +2 -0
  584. package/src/patterns/element-anatomy/item-anatomy.spec.md +14 -5
  585. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +6 -4
  586. package/src/patterns/element-anatomy/item-anatomy.tsx +7 -6
  587. package/src/patterns/header-canonical/header-canonical.spec.md +5 -4
  588. package/src/patterns/overlay-surface/overlay-surface.spec.md +22 -1
  589. package/src/styles/tokens.css +3 -0
  590. package/src/tokens/README.md +1 -1
  591. package/src/tokens/color/color.spec.md +3 -3
  592. package/src/tokens/color/color.stories.tsx +3 -3
  593. package/src/tokens/density/density.stories.tsx +16 -3
  594. package/src/tokens/layoutSpace/layoutSpace.spec.md +3 -1
  595. package/src/tokens/motion/motion.css +33 -21
  596. package/src/tokens/motion/motion.spec.md +32 -15
  597. package/src/tokens/motion/motion.stories.tsx +58 -5
  598. package/src/tokens/motion/motion.ts +7 -6
  599. package/src/tokens/motion/overlay-motion.ts +26 -0
  600. package/src/tokens/orphan-tokens.spec.md +4 -4
  601. package/src/tokens/token-system.spec.md +1 -0
  602. package/src/tokens/uiSize/uiSize.spec.md +1 -1
  603. package/src/tokens/uiSize/uiSize.stories.tsx +4 -2
  604. package/src/tokens/utility-registry.json +160 -23
@@ -1,8 +1,12 @@
1
1
  ---
2
2
  component: Input
3
3
  family: 4
4
- variants: {}
5
- sizes: {}
4
+ variants:
5
+ default: { when: "Field wrapper 完整 chrome(bg-surface + border + hover/focus 回饋)— 表單 / Field 內嵌" }
6
+ sizes:
7
+ sm: { when: "field-height 28、icon 16、text-body" }
8
+ md: { when: "default 一般表單(field-height 32、icon 16)" } # ★ default
9
+ lg: { when: "touch / 大字級(field-height 36、icon 20、text-body-lg)" }
6
10
  traits:
7
11
  - hasInteractiveStates
8
12
  - isInputLike
@@ -92,7 +96,7 @@ endAction(host 內嵌 inline action)vs 獨立 `<Button iconOnly>` 的分界詳 `
92
96
 
93
97
  `<Input mode="display">` 是 identity 顯示:
94
98
  - 有值:原樣輸出
95
- - null / undefined / 空字串:`—`(em dash),`text-fg-muted`
99
+ - null / undefined / 空字串:半形 `-`(hyphen),`text-foreground`(display/readonly 供檢視值同色;disabled → fg-disabled)
96
100
 
97
101
  ---
98
102
 
@@ -125,26 +129,17 @@ endAction(host 內嵌 inline action)vs 獨立 `<Button iconOnly>` 的分界詳 `
125
129
 
126
130
  ## Variant(visual chrome,正交於 mode)
127
131
 
128
- Input 有兩個 visual chrome variant,**獨立於 mode**(mode 是 state,variant 是 chrome look):
132
+ Input 有**一個公開** visual chrome variant `default`(+ 一個 `@internal` 的 `naked`),**獨立於 mode**(mode 是 state,variant 是 chrome look):
129
133
 
130
134
  | Variant | 原則 | 適用場景 | 世界級對照 |
131
135
  |---------|------|---------|-----------|
132
- | `'default'`(預設) | 完整 field chrome(背景 + 常態 border + hover/focus 回饋)——明確邀請輸入 | 表單 / Field 內嵌 / 標準 edit UI | Material Input / Ant Input default |
133
- | `'bare'` | 常態無 chrome,hover / focus 才現 border;sizing(padding / typography / height)不變 | **Toolbar inline editing**(FileViewer zoom input / chart config / rich text toolbar number input / Sidebar inline rename) | VS Code settings input / Figma toolbar number / Notion prop input |
136
+ | `'default'`(預設) | 完整 field chrome(背景 + 常態 border + hover/focus 回饋)——明確邀請輸入 | 表單 / Field 內嵌 / 標準 edit UI / toolbar 內小尺寸輸入(如 FileViewer zoom,搭 `size="sm" autoWidth`) | [Material Input](https://github.com/material-components/material-web/blob/main/docs/components/text-field.md) / [Ant Input](https://github.com/ant-design/ant-design/blob/master/components/input/index.en-US.md) default |
134
137
 
135
- **判斷法**:Input 放在表單或 Field 內 → `default`;放在 Toolbar chrome 或 page-body inline → `bare`。
138
+ **透傳**:在 `<Field variant="default">` 內自動繼承 context variant,per-prop override(code:`useResolvedFieldVariant`)。
136
139
 
137
- **透傳**:在 `<Field variant="default|bare">` 內自動繼承 context variant,per-prop override(code:`useResolvedFieldVariant`)。
140
+ > **內部 variant `naked`(@internal,非公開)**:`FieldVariant` 型別含第二值 `'naked'`,但**不是公開 Input variant**——單獨使用無視覺邊界,**不可 standalone**。naked = 剝除 chrome(無 padding / bg / rounded),但 **edit 態仍由自身 border-based state machine 渲染 border + focus 藍框**(field-wrapper.tsx edit×naked compound = cell-as-input state machine;非「完全無 border」、亦非「host cell 自管」);正被 `FieldSurfaceContext='table-cell'` 取代。consumer 只用 `default`。對齊 public-vs-internal canonical(`ui-development.md`:能直接被使用且用在合理地方才公開)。
138
141
 
139
- > **內部 variant `naked`(@internal,非公開)**:`FieldVariant` 型別含第三值 `'naked'`(完全無 chrome / border / focus ring),但**不是公開 Input variant**——單獨使用無視覺邊界,**不可 standalone**。僅供 DS 內部 cell-as-input 組合(host cell 自管 border + focus visual;正被 `FieldSurfaceContext='table-cell'` 取代)。consumer 只用 `default` / `bare`。對齊 public-vs-internal canonical(`ui-development.md`:能直接被使用且用在合理地方才公開)。
140
-
141
- **`bare` 使用情境的 canonical 要求**:
142
- - 外層 chrome 必須已提供「這是可編輯」的 affordance(Toolbar 的 icon / prop label / row structure);否則 user 看不到 input chrome 找不到可編輯位置
143
- - 保留 DS field-height(`h-field-sm/md/lg`)、typography、icon tier、error 視覺——**bare 只動 chrome 不動 sizing**
144
-
145
- **禁止**:
146
- - ❌ 在**表單** context 用 `bare`——表單需要明確的 field chrome 邀請輸入,bare 會失去 affordance
147
- - ❌ 拿 `bare` 來當「空白 div 替代品」——variant 不是拿掉視覺的工具,而是特定 chrome context 的 canonical
142
+ > **`bare` variant 退役(2026-07-09,user 拍板)**:原「透明外殼、hover/focus 才顯 border」variant,宣稱用途 = toolbar inline editing,但全 repo(DS + apps + stories)**零真實 JSX 消費者**——FileViewer zoom 實為 `<Input size="sm" autoWidth>`(default variant),非 bare;唯一出現處是一個 反例 story + 過時註解。toolbar 小輸入用 `default`(對齊「邊框給互動元素、輸入框就該有邊框」原則),cell-as-input `naked` `bare` 純冗餘,移除。Benchmark:[Ant Input](https://github.com/ant-design/ant-design/blob/master/components/input/index.en-US.md) `variant="borderless"`(概念合法),但 [Material M3](https://github.com/material-components/material-web/blob/main/docs/components/text-field.md) 只有 filled/outlined、無無邊框變體;我們無真實消費場景 不留純預留 variant。詳 `field-types.ts` FieldVariant note。
148
143
 
149
144
  ---
150
145
 
@@ -166,7 +161,7 @@ Input 有兩個 visual chrome variant,**獨立於 mode**(mode 是 state,variant
166
161
 
167
162
  **禁止**:
168
163
  - ❌ **表單 Field 內**——Field 欄位必須欄寬對齊,寬度隨值跳動會破壞 grid layout
169
- - ❌ **搭配 `variant="default"` 放在主表單區**——auto-width 預設搭 `variant="bare"`(toolbar inline 語意),default chrome 自動對齊 Field canonical
164
+ - ❌ **放在主表單欄位區**——auto-width 讓欄寬隨值跳動、破壞 grid 對齊;autoWidth 適用 toolbar / page-body inline 場景,搭 `variant="default"`(小尺寸有框輸入,如 FileViewer zoom;`bare` 2026-07-09 退役)
170
165
 
171
166
  **世界級對照**:VS Code settings inline input 用同 pattern;Notion property field、Airtable cell edit 皆 auto-size。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
172
167
 
@@ -3,9 +3,10 @@ import * as React from 'react'
3
3
  import { type VariantProps } from 'class-variance-authority'
4
4
  import type { LucideIcon } from 'lucide-react'
5
5
  import { cn } from '@/lib/utils'
6
- import type { FieldMode, FieldVariant } from '@/design-system/components/Field/field-types'
7
- import { fieldWrapperStyles, bareInputStyles, EMPTY_DISPLAY } from '@/design-system/components/Field/field-wrapper'
8
- import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from '@/design-system/components/Field/field-context'
6
+ import type { FieldMode, FieldVariant, FieldVariantInternal } from '@/design-system/components/Field/field-types'
7
+ import { fieldWrapperStyles, bareInputStyles } from '@/design-system/components/Field/field-wrapper'
8
+ import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid, useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'
9
+ import { useControllable } from '@/design-system/hooks/use-controllable'
9
10
  import { ItemInlineAction, ItemPrefix, type InlineActionConfig } from '@/design-system/patterns/element-anatomy/item-anatomy'
10
11
  import { CircularProgress } from '@/design-system/components/CircularProgress/circular-progress'
11
12
  import { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'
@@ -14,19 +15,17 @@ import { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'
14
15
 
15
16
  export interface InputProps
16
17
  extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>,
17
- Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant'> {
18
+ Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant' | 'width'> {
18
19
  /** Field display mode */
19
20
  mode?: FieldMode
20
21
  /**
21
- * Visual chrome(正交於 mode);Phase B1(2026-05-05)從 `variant` 改名 `chrome`,對齊 FieldContext.variant 透傳。
22
- * 公開 variant 兩個:
22
+ * Visual variant(正交於 mode),對齊 FieldContext.variant 透傳。
23
+ * 公開 variant 一個(2026-07-09 `bare` 退役,詳 field-types.ts FieldVariant note;naked 2026-07-14 型別收窄至 FieldVariantInternal):
23
24
  * - `'default'`(預設)— Field wrapper 完整 variant:bg-surface + 明顯 border + hover/focus 回饋。適用表單、Field 內嵌。
24
- * - `'bare'` — 透明 variant,hover / focus 才出現 border。適用 Toolbar inline editing(如 FileViewer zoom input / chart config toolbar / rich text toolbar number input)。保留 padding / typography / height,只拿掉背景和常態 border。
25
25
  *
26
- * @internal `'naked'` — 完全無 chrome / 無 border / 無 focus ring。單獨使用無視覺邊界,**不可直接 standalone 用**;僅供 DS 內部 cell-as-input 組合(host cell 自管 border + focus visual,正被 `FieldSurfaceContext='table-cell'` 取代)。consumer 請用 `default` / `bare`。
26
+ * @internal `'naked'` — 完全無 chrome / 無 border / 無 focus ring。單獨使用無視覺邊界,**不可直接 standalone 用**;僅供 DS 內部 cell-as-input 組合(edit×naked border/hover/focus/error field-wrapper 自渲 state machine 提供,非 host cell 自管;詳 field-wrapper.tsx)。consumer 請用 `default`(2026-07-14 起型別層已擋:公開 FieldVariant 不含 naked,僅 DataTable cell-registry 經 WithFieldVariantInternal 通道傳入)。
27
27
  *
28
- * 透傳:在 `<Field variant="default|bare">` 內自動繼承 context.variant;per-prop override context。
29
- * 世界級對照(bare):VS Code settings input / Figma toolbar number / Notion prop input。
28
+ * 透傳:在 `<Field variant="default">` 內自動繼承 context.variant;per-prop override context。
30
29
  */
31
30
  variant?: FieldVariant
32
31
  /** Error 狀態(正交於 mode)。border-error + aria-invalid。 */
@@ -63,7 +62,7 @@ export interface InputProps
63
62
  * **不要用在**:表單 Field(Field 需要欄寬對齊,不該隨值跳動)
64
63
  *
65
64
  * **fallback**:不支援 `field-sizing` 的瀏覽器會退化為 `w-auto`(wrapper 縮到 content 尺寸,
66
- * input 本身有 min-width 避免消失)。UX 上稍不一致但不致斷;若必須精準對齊所有瀏覽器,
65
+ * input min-w-0,依 content/placeholder 撐寬)。UX 上稍不一致但不致斷;若必須精準對齊所有瀏覽器,
67
66
  * consumer 可自行傳 `style={{ width: ... }}` 顯式寬度,不走 auto。
68
67
  */
69
68
  autoWidth?: boolean
@@ -88,6 +87,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
88
87
  disabled: disabledProp,
89
88
  readOnly,
90
89
  value,
90
+ defaultValue,
91
91
  id: idProp,
92
92
  'aria-describedby': ariaDescribedByProp,
93
93
  'aria-errormessage': ariaErrorMessageProp,
@@ -102,10 +102,11 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
102
102
  const size = useResolvedFieldSize(sizeProp)
103
103
  const disabled = useResolvedFieldDisabled(disabledProp)
104
104
  // chrome 透傳:per-prop override context;context 沒值則 'default'
105
- const variant: FieldVariant = useResolvedFieldVariant(variantProp)
105
+ const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
106
106
  // 2026-06-08 SSOT:mode 經 useResolvedFieldMode 統一解析(prop > 有效 disabled > fieldCtx.mode > readOnly > 'edit')。
107
107
  // loading 期間 input 保持可編輯(Ant Input.Search 派),只用 aria-busy + endAction Spinner,不動 mode。
108
108
  const resolvedMode: FieldMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly })
109
+ const emptyDisplay = useFieldEmptyDisplay()
109
110
  const isEditable = resolvedMode === 'edit'
110
111
  const isDisplay = resolvedMode === 'display'
111
112
  // error 合併:自身 error prop OR Field context invalid
@@ -114,18 +115,59 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
114
115
  const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']
115
116
  const iconColor = resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted'
116
117
 
117
- // ── display mode:純展示,渲染 <span> 取代 <input> ──
118
+ // ── display mode(+ readonly 空值)純展示,渲染 <span> 取代 <input> ──
118
119
  // 對齊 Carbon read-only / PatternFly inline-edit hidden-input / Cloudscape display-mode
119
- if (isDisplay) {
120
- const displayValue = value != null && value !== '' ? String(value) : null
120
+ // 2026-07-08 user 拍板:readonly 空值也走 display-span 顯 '-'(readonly **有值** 仍走下方
121
+ // native <input readOnly> 保留選取/複製語意 — field-controls.spec.md「null / undefined 值」)
122
+ // 2026-07-14 audit Dim 26(dual-mode coherence):uncontrolled `defaultValue` 也要被
123
+ // display / readonly-空值判定認得 — 原本只讀 `value`,uncontrolled Input 切 display 會誤顯 '-'。
124
+ // 2026-07-14 R2(dual-model consensus,取代同日稍早 onChange-mirror 版):resolved value 改走
125
+ // useControllable 內部 SSOT(Radix idiom,同 Select / Pagination 既有消費)。uncontrolled 時
126
+ // native input 由 value={resolved} 內部驅動(defaultValue 只作初始值,不落 DOM attribute),
127
+ // 修 mirror 版兩個真缺陷:
128
+ // (1) remount-stale — 切 display / readonly(native input unmount)再回 edit,native input 從
129
+ // defaultValue attribute 重掛 → 顯示 stale 初始值,與 mirror 判定分歧;
130
+ // (2) form.reset() stale — HTML 標準 reset 不發 input event,onChange mirror 停在 reset 前的值
131
+ // (下方 reset bridge 修)。
132
+ // controlled(傳 value)時 useControllable 純 passthrough,行為與原本完全一致。
133
+ // consumer onChange 不接進 hook(保留 native event signature),於 <input> onChange 另行轉發。
134
+ const isControlled = value !== undefined
135
+ const [resolved, setResolved] = useControllable<string | number | readonly string[]>({
136
+ value: value as string | number | readonly string[] | undefined,
137
+ defaultValue: defaultValue ?? '',
138
+ })
139
+ const displayValue = resolved != null && resolved !== '' ? String(resolved) : null
140
+ const showDisplaySpan = isDisplay || (resolvedMode === 'readonly' && displayValue == null)
141
+
142
+ // form.reset() bridge(uncontrolled only):reset 恢復 defaultValue 但不發 input event →
143
+ // 手動把 resolved 歸位 defaultValue。keyed on showDisplaySpan:display 分支不掛 native input,
144
+ // 回 edit 重掛時 effect 重跑補掛 listener。
145
+ const innerRef = React.useRef<HTMLInputElement | null>(null)
146
+ React.useEffect(() => {
147
+ if (isControlled || showDisplaySpan) return
148
+ const form = innerRef.current?.form
149
+ if (!form) return
150
+ const handleReset = () => setResolved(defaultValue ?? '')
151
+ form.addEventListener('reset', handleReset)
152
+ return () => form.removeEventListener('reset', handleReset)
153
+ }, [isControlled, showDisplaySpan, defaultValue, setResolved])
154
+ // Merge refs(LinkInput setRef idiom,link-input.tsx:138)
155
+ const setRef = React.useCallback((el: HTMLInputElement | null) => {
156
+ innerRef.current = el
157
+ if (typeof ref === 'function') ref(el)
158
+ else if (ref) (ref as React.MutableRefObject<HTMLInputElement | null>).current = el
159
+ }, [ref])
160
+
161
+ if (showDisplaySpan) {
162
+ const spanMode = isDisplay ? 'display' : 'readonly'
121
163
  return (
122
164
  <div
123
165
  className={cn(
124
- fieldWrapperStyles({ mode: 'display', variant: variant, size }),
166
+ fieldWrapperStyles({ mode: spanMode, variant: variant, size }),
125
167
  autoWidth && 'inline-flex w-auto',
126
168
  className,
127
169
  )}
128
- data-field-mode="display"
170
+ data-field-mode={spanMode}
129
171
  >
130
172
  {StartIcon && (
131
173
  <ItemPrefix>
@@ -143,10 +185,10 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
143
185
  // cell display canonical:single-line value 過長 → ellipsis。Textarea display 走 wrap path,
144
186
  // 不在此處;Input display 永遠 single-line。)
145
187
  'truncate',
146
- displayValue == null && 'text-fg-muted',
188
+ displayValue == null && fieldEmptyColorClass(resolvedMode),
147
189
  )}
148
190
  >
149
- {displayValue ?? EMPTY_DISPLAY}
191
+ {displayValue ?? emptyDisplay}
150
192
  </span>
151
193
  </div>
152
194
  )
@@ -174,10 +216,10 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
174
216
  </ItemPrefix>
175
217
  )}
176
218
  <input
177
- ref={ref}
219
+ ref={setRef}
178
220
  type="text"
179
221
  id={idProp ?? fieldCtx?.id}
180
- value={value as string | number | readonly string[] | undefined}
222
+ value={resolved}
181
223
  readOnly={resolvedMode === 'readonly'}
182
224
  disabled={resolvedMode === 'disabled'}
183
225
  aria-invalid={resolvedError || undefined}
@@ -192,6 +234,11 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
192
234
  autoWidth && '[field-sizing:content] w-auto min-w-0',
193
235
  )}
194
236
  {...props}
237
+ // 置於 {...props} 後:寫回 resolved SSOT(controlled 時 useControllable 內部為 no-op)再轉發 consumer onChange。
238
+ onChange={(e) => {
239
+ setResolved(e.target.value)
240
+ props.onChange?.(e)
241
+ }}
195
242
  />
196
243
  {loading ? (
197
244
  <CircularProgress size={iconSize} className="shrink-0" />
@@ -219,7 +266,7 @@ export const inputMeta = {
219
266
  family: 4,
220
267
  variants: {
221
268
  default: { purpose: 'Field wrapper 完整 chrome(bg-surface + border + hover/focus 回饋)— 表單 / Field 內嵌' },
222
- bare: { purpose: '透明 chrome,hover / focus 才現 border Toolbar inline editing(zoom input / config toolbar)' },
269
+ // (2026-07-09 `bare` variant 退役 field-types.ts FieldVariant note)
223
270
  },
224
271
  sizes: {
225
272
  sm: { fieldHeight: 28, iconSize: 16, typography: 'body' },
@@ -257,7 +257,7 @@ export const Overview = {
257
257
  <tbody>
258
258
  {[
259
259
  ['mode', "'edit' | 'display' | 'readonly' | 'disabled'", "'edit'", 'FieldMode 四模式'],
260
- ['variant', "'default' | 'bare' | 'naked'", "'default'", 'Field chrome variant(baretoolbar / cell-as-input 透明 chrome)'],
260
+ ['variant', "'default'", "'default'", 'Field chrome variant(naked:cell-as-input 透明 chrome,@internal,2026-07-14 型別收窄至 FieldVariantInternal)'],
261
261
  ['size', "'sm' | 'md' | 'lg'", "'md'", '尺寸,與 Button 共用 token'],
262
262
  ['error', 'boolean', 'false', '錯誤狀態,紅色邊框 + aria-invalid'],
263
263
  ['value', 'string | null', '—', 'URL 值'],
@@ -172,8 +172,8 @@ export const EmptyRule: Story = {
172
172
  </Rule>
173
173
 
174
174
  <Rule
175
- title="readonly 空值 → 顯示 empty placeholder(—)"
176
- note="table cell / 個人資料展示頁,沒填 website 時以 em-dash 表達「未填」,跟其他 Field readonly 空值呈現一致"
175
+ title="readonly 空值 → 顯示半形連字號(-)"
176
+ note="table cell / 個人資料展示頁,沒填 website 時以半形連字號 - 表達「未填」,跟其他 Field readonly 空值呈現一致"
177
177
  >
178
178
  <LinkInput mode="readonly" value={null} />
179
179
  </Rule>
@@ -2,9 +2,9 @@ import * as React from 'react'
2
2
  import { Pencil } from 'lucide-react'
3
3
  import type { VariantProps } from 'class-variance-authority'
4
4
  import { cn } from '@/lib/utils'
5
- import type { FieldMode, FieldVariant } from '@/design-system/components/Field/field-types'
6
- import { fieldWrapperStyles, bareInputStyles, EMPTY_DISPLAY, fieldDisplayTextClass } from '@/design-system/components/Field/field-wrapper'
7
- import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from '@/design-system/components/Field/field-context'
5
+ import type { FieldMode, FieldVariant, FieldVariantInternal } from '@/design-system/components/Field/field-types'
6
+ import { fieldWrapperStyles, bareInputStyles, fieldDisplayTextClass } from '@/design-system/components/Field/field-wrapper'
7
+ import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid, useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'
8
8
  import { ItemInlineAction } from '@/design-system/patterns/element-anatomy/item-anatomy'
9
9
 
10
10
  // ── URL Validation ──────────────────────────────────────────────────────────
@@ -52,12 +52,12 @@ export interface LinkInputProps
52
52
  // defaultValue」宣稱的型別面機械封鎖 — 原本仍在型別 surface 且經 {...props} spread 到已
53
53
  // controlled 的 input(React dev warning)。
54
54
  extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'value' | 'defaultValue' | 'onChange'>,
55
- Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant'> {
55
+ Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant' | 'width'> {
56
56
  mode?: FieldMode
57
57
  /**
58
58
  * Visual chrome(2026-05-05 Phase B3)。對齊 FieldContext.variant 透傳。
59
59
  * - `'default'`(預設)— Field wrapper 完整 chrome(form / Field 內嵌)。
60
- * - `'bare'` — 透明 variant,hover/focus 才現 border(Toolbar inline edit / DataTable cell-as-input)
60
+ * (2026-07-09 `bare` variant 退役;naked = cell-as-input substrate,@internal)
61
61
  *
62
62
  * mode='display' 時 chrome 無視覺意義(display 完全無 wrapper);chrome 僅作用於 edit / readonly / disabled。
63
63
  */
@@ -109,9 +109,10 @@ const LinkInput = React.forwardRef<HTMLInputElement, LinkInputProps>(
109
109
  // 2026-06-08 SSOT:mode 經 useResolvedFieldMode 統一解析(prop > 有效 disabled > fieldCtx.mode > readOnly > 'edit')。
110
110
  // spec field-controls.spec.md L125「readOnly 原生屬性自動覆蓋 mode」契約落地。
111
111
  const resolvedMode: FieldMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly })
112
+ const emptyDisplay = useFieldEmptyDisplay()
112
113
  const isEditable = resolvedMode === 'edit'
113
114
  // chrome resolution:per-prop > context > 'default'
114
- const resolvedVariant: FieldVariant = useResolvedFieldVariant(variantProp)
115
+ const resolvedVariant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
115
116
 
116
117
  // 2026-07-05 D4:invalid 經 useResolvedFieldInvalid 統一接 fieldCtx.invalid(field-controls.spec.md
117
118
  // resolver 契約「error/invalid = prop OR fieldCtx.invalid」)— 原本紅框只吃 errorProp||localError、
@@ -156,7 +157,7 @@ const LinkInput = React.forwardRef<HTMLInputElement, LinkInputProps>(
156
157
  // anchor / span 必套 `fieldDisplayTextClass(size)`(sm/md→text-body,lg→text-body-lg)
157
158
  // — 對齊跨 Field family display 視覺尺寸統一。原無 font-size class → 用 browser default
158
159
  // 字體 → 跟其他 Field display 不一致(user 抓 I2)。truncate 同需,長 URL ellipsis(I1)。
159
- if (!value) return <span className={cn(fieldDisplayTextClass(size), 'text-fg-muted block truncate')}>{EMPTY_DISPLAY}</span>
160
+ if (!value) return <span className={cn(fieldDisplayTextClass(size), fieldEmptyColorClass(resolvedMode), 'block truncate')}>{emptyDisplay}</span>
160
161
  return <span className={cn(fieldDisplayTextClass(size), 'block truncate')}>{renderLinkAnchor(value, label)}</span>
161
162
  }
162
163
  return (
@@ -167,7 +168,7 @@ const LinkInput = React.forwardRef<HTMLInputElement, LinkInputProps>(
167
168
  <span className="flex-1 min-w-0 truncate">
168
169
  {value
169
170
  ? renderLinkAnchor(value, label)
170
- : <span className="text-fg-muted">{EMPTY_DISPLAY}</span>
171
+ : <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>
171
172
  }
172
173
  </span>
173
174
  </div>
@@ -232,10 +233,10 @@ const LinkInput = React.forwardRef<HTMLInputElement, LinkInputProps>(
232
233
  {resolvedMode === 'disabled'
233
234
  ? (displayText
234
235
  ? <span className="text-fg-disabled">{displayText}</span>
235
- : <span className="text-fg-muted">{EMPTY_DISPLAY}</span>)
236
+ : <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>)
236
237
  : (value
237
238
  ? renderLinkAnchor(value, label)
238
- : <span className="text-fg-muted">{EMPTY_DISPLAY}</span>)
239
+ : <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>)
239
240
  }
240
241
  </span>
241
242
  </div>
@@ -551,7 +551,7 @@ export const ColorMatrix = {
551
551
  <div className="flex flex-col gap-8">
552
552
  <div className="flex flex-col gap-1">
553
553
  <H3>State 色彩對照</H3>
554
- <Desc>單選用 bg-neutral-selected 背景指示選中,多選用 Checkbox 勾選——背景不變。disabled 時所有元素統一 fg-disabled。色塊即時渲染,切 dark mode 自動更新。</Desc>
554
+ <Desc>單選用 bg-neutral-selected 背景指示選中,多選用 Checkbox 勾選——背景不變。disabled 時文字 / startIcon 用 fg-disabled、tag / endContent / startContent 用 opacity-disabled、checkbox 走自身 disabled、avatar 不變暗。色塊即時渲染,切 dark mode 自動更新。</Desc>
555
555
  </div>
556
556
 
557
557
  {/* Single-select */}
@@ -745,10 +745,10 @@ export const SizeMatrix = {
745
745
  <MenuItem size={sz} startIcon={Mail} description="每日摘要信件">
746
746
  電子郵件通知
747
747
  </MenuItem>
748
- <MenuItem size={sz} avatar={{ alt: "Alice", color: "indigo" as const }}>
748
+ <MenuItem size={sz} avatar={{ src: "https://i.pravatar.cc/48?u=alice-chen", alt: "Alice", color: "indigo" as const }}>
749
749
  Alice Chen
750
750
  </MenuItem>
751
- <MenuItem size={sz} avatar={{ alt: "Bob", color: "magenta" as const }} description="工程部門">
751
+ <MenuItem size={sz} avatar={{ src: "https://i.pravatar.cc/48?u=bob-wang", alt: "Bob", color: "magenta" as const }} description="工程部門">
752
752
  Bob Wang
753
753
  </MenuItem>
754
754
  <MenuItem size={sz} checkbox checked={true} startIcon={Bell}>
@@ -74,7 +74,7 @@ export const CompositionRules: Story = {
74
74
  <h4>Pattern 3 — ContextMenu + MenuItem(右鍵選單,未來)</h4>
75
75
  <p>右鍵觸發 contextual action → 預留消費者(Radix ContextMenu + MenuItem 同 layout primitive)。</p>
76
76
 
77
- <p className="text-fg-muted">禁止:在產品頁面裡自己用 <code>&lt;div&gt;</code> 拼一列選單(像 <code>&lt;div className="flex px-2 py-1.5"&gt;</code>)— 一律使用 MenuItem,才能確保所有選單列的對齊與間距一致。各區塊的對齊規則(左側圖示/頭像在 24px 內對齊、文字往下堆疊、右側標記可有可無)由選項列共用設計準則統一規範。</p>
77
+ <p className="text-fg-muted">禁止:在產品頁面裡自己用 <code>&lt;div&gt;</code> 拼一列選單(像 <code>&lt;div className="flex px-2 py-1.5"&gt;</code>)— 一律透過上方的 MenuItem 系選單元件(DropdownMenu / SelectMenu 等,內部消費 MenuItem),才能確保所有選單列的對齊與間距一致。MenuItem 本身是 internal 排版元件,在產品頁面直接裸用會失去外層選單的無障礙 / 鍵盤 / 焦點管理(詳 spec「何時用 / 何時不用」)。各區塊的對齊規則(左側圖示/頭像在 24px 內對齊、文字往下堆疊、右側標記可有可無)由選項列共用設計準則統一規範。</p>
78
78
  </div>
79
79
  </div>
80
80
  ),
@@ -28,14 +28,16 @@ MenuItem 是所有 menu 類元件的**共用視覺佈局層**——處理 prefix
28
28
  ## 結構
29
29
 
30
30
  ```
31
- prefix content suffix
32
- [checkbox?] [icon? | avatar?] [label + description?] [tag?]
31
+ prefix content suffix
32
+ [checkbox?] [icon? | avatar? | content?] [label + description?] [tag?]
33
33
  ```
34
34
 
35
- - **Prefix**:checkbox(多選,由父層注入)+ startIcon 或 avatar(互斥)
35
+ - **Prefix**:checkbox(多選,由父層注入)+ startIcon 或 avatar 或 startContent(三者互斥)
36
36
  - **Content**:label + 可選 description
37
37
  - **Suffix**:tag(ml-auto 靠右)
38
38
 
39
+ **`startContent`(2026-07-10 加,WM TypeIcon 彩色徽章需求)**:LucideIcon / Avatar 表達不了的自訂 leading 視覺(類型徽章 = 彩底方塊 + glyph、status 組合點等)。**渲染在同一個 prefix 對齊容器內——佔位與排列邏輯跟 startIcon 一模一樣**(gap / 24px 閾值對齊 / shrink 全繼承 `itemPrefixAlignVariants` SSOT,零平行 layout),consumer 唯一責任是節點尺寸對齊 `ICON_SIZE[size]`(sm/md=16px、lg=20px)。與 `endContent` 對稱(同為自訂 ReactNode slot)。disabled 時容器套 `opacity-disabled`(對齊 `tag` / `endContent` 自訂節點同款處理;**與 `avatar` 不變暗的分界**:Avatar 有自己的 self-dim 體系〔`fieldCtx.disabled` 觸發,同一顆 `opacity-disabled` token,avatar.spec.md「Disabled」段〕,MenuItem 非 Field context 故不觸發;startContent 是任意 consumer 節點、無任何 DS context 可自 dim → 容器套 opacity 是唯一可靠機制。放頭像請用 `avatar` prop 非 startContent)。
40
+
39
41
  prefix icon 跟 label 同色(foreground),不是 fg-muted。詳見 item-anatomy.spec.md 的 Icon 色彩原則。
40
42
 
41
43
  ---
@@ -59,14 +61,19 @@ prefix icon 跟 label 同色(foreground),不是 fg-muted。詳見 item-ana
59
61
 
60
62
  ---
61
63
 
62
- ## startIcon vs avatar
64
+ ## startIcon vs avatar vs startContent(怎麼選)
63
65
 
64
- | | `startIcon` | `avatar` |
65
- |---|---|---|
66
- | 型別 | `LucideIcon` | `AvatarData` |
67
- | 角色 | **描述**選項的性質(形容詞) | **代表**選項的身份(名詞) |
68
- | 尺寸 | 16/20px(icon tier,元件控制) | description 決定:無 desc → 20/24/24px,有 desc → 32/32/40px |
69
- | 對齊 | 永遠 inline(≤ 24px) | desc → inline;有 desc → block |
66
+ **一句話判準:看內容「是什麼」,不是看喜好** —— 線性圖示 → `startIcon`;人/實體頭像 `avatar`;其他任何自訂視覺 → `startContent`。每槽綁不同的自動行為,型別即理由:
67
+
68
+ | | `startIcon` | `avatar` | `startContent` |
69
+ |---|---|---|---|
70
+ | 型別 | `LucideIcon` | `AvatarData` | `ReactNode` |
71
+ | 角色 | **描述**性質(形容詞) | **代表**身份(名詞) | 圖示/頭像表達不了的視覺(類型徽章、狀態組合點) |
72
+ | 尺寸 | 16/20px(**元件自動控**) | 由 description 決定:無 desc → 20/24/24px,有 desc → 32/32/40px(**元件自動推導**) | consumer 自理,對齊 `ICON_SIZE[size]` |
73
+ | disabled | 自動 `fg-disabled` 換色 | 不變暗(自有 fieldCtx self-dim 體系) | 容器自動 `opacity-disabled` |
74
+ | 對齊 | 永遠 inline(≤ 24px) | 無 desc → inline;有 desc → block | inline(同 startIcon 容器) |
75
+
76
+ **為什麼三槽而不是一個 ReactNode 口**:合成一口(Atlassian `iconBefore: ReactNode` 式)會失去前兩槽的自動行為 —— 每個 consumer 都得自己記尺寸、自己處理 disabled、自己接 hoverCard = 漂移溫床。世界級同構:MUI List 正是三分 —— [`ListItemIcon`](https://mui.com/material-ui/api/list-item-icon/) 與 [`ListItemAvatar`](https://mui.com/material-ui/api/list-item-avatar/) 是**兩個獨立 wrapper 元件**、任意節點則直接放 —— 我們用 props 表達同一套分工。
70
77
 
71
78
  ### avatar 尺寸推導
72
79
 
@@ -150,7 +157,7 @@ Menu 的設計目的是「**快速掃視多個選項挑一個**」。垂直空
150
157
  | default | transparent | foreground | — |
151
158
  | hover | `--neutral-hover` | foreground | 滑鼠 hover |
152
159
  | selected(單選) | `--neutral-selected` | foreground | bg 高亮(不用 ✓ 勾號,避免影響 prefix 對齊) |
153
- | **selected × hover(疊加)** | `--neutral-selected`(**selected 勝**) | foreground | 2026-07-04 Q2 拍板 M5 明文:bg 是唯一選中指示器,hover 不得洗掉。code = selected 時關 hover bg(menu-item.tsx)。對齊 Ant Menu `:not(-item-selected)` / VS Code list `:hover:not(.selected)`;消費者(DropdownMenu highlighted)同規則 |
160
+ | **selected × hover(疊加)** | `--neutral-selected`(**selected 勝**) | foreground | 2026-07-04 Q2 拍板 M5 明文:bg 是唯一選中指示器,hover 不得洗掉。code = selected 時關 hover bg(menu-item.tsx)。對齊 Ant Menu `:not(-item-selected)` / VS Code list `:hover:not(.selected)`。**消費者差異**:DropdownMenu 在 selected × highlighted(鍵盤 cursor)深化一階為 `--neutral-selected-active`(2026-07-05 D4,WCAG 2.4.7 cursor 可見;dropdown-menu.tsx `selected && data-[highlighted]:bg-neutral-selected-active`)——此為 wrapper 層鍵盤 cursor 增強,MenuItem primitive 的 selected × hover(滑鼠)仍維持 `--neutral-selected` 不深化 |
154
161
  | selected(多選) | transparent | foreground | checkbox 勾選 |
155
162
  | disabled | transparent | `--fg-disabled` | disabled prop |
156
163
 
@@ -163,7 +170,7 @@ Menu 的設計目的是「**快速掃視多個選項挑一個**」。垂直空
163
170
 
164
171
  單選和 DropdownMenu RadioItem 統一用 `bg-neutral-selected`。不用 ✓ 勾號——勾號在 prefix 會影響群組內對齊。
165
172
 
166
- disabled 時:文字(label / description)與 `startIcon` 套 `fg-disabled`;`checkbox` 套用自身 `disabled` 樣式(非 `fg-disabled`);`tag` / `endContent` 容器套 `opacity-disabled`;`avatar` 在 MenuItem 內不變暗(Avatar 僅在 disabled Field wrapper context 內自 dim,MenuItem 非 Field wrapper)。
173
+ disabled 時:文字(label / description)與 `startIcon` 套 `fg-disabled`;`checkbox` 套用自身 `disabled` 樣式(非 `fg-disabled`);`tag` / `endContent` / `startContent` 容器套 `opacity-disabled`(自訂節點自帶配色、不能硬換字色,降透明是唯一通用機制;MUI 佐證:disabled MenuItem 整列 root 直接套 `opacity: disabledOpacity` 連 avatar 都 dim — 我們比它細粒度);`avatar` 在 MenuItem 內不變暗(Avatar 僅在 disabled Field wrapper context 內自 dim,MenuItem 非 Field wrapper)。
167
174
 
168
175
  ### 浮層關閉行為
169
176
 
@@ -206,10 +213,10 @@ Menu item 的 prefix icon 跟 label 同色(foreground),不是 fg-muted。P
206
213
 
207
214
  ## 禁止事項
208
215
 
209
- - ❌ `startIcon` `avatar` 不可同時使用
216
+ - ❌ `startIcon` / `avatar` / `startContent` 三者不可同時使用(同一 prefix 位置)
210
217
  - ❌ 無 `description` 時不可使用 > 24px 的 avatar
211
218
  - ❌ disabled item 不可有 hover 效果
212
- - ❌ disabled item 內的子元件不可保持 enabled 外觀——文字 / `startIcon` 套 `fg-disabled`、`checkbox` 用自身 `disabled` 樣式、`tag` / `endContent` 套 `opacity-disabled`(`avatar` 在 MenuItem 內維持原樣,因 Avatar 僅在 disabled Field wrapper context 內自 dim)
219
+ - ❌ disabled item 內的子元件不可保持 enabled 外觀——文字 / `startIcon` 套 `fg-disabled`、`checkbox` 用自身 `disabled` 樣式、`tag` / `endContent` / `startContent` 套 `opacity-disabled`(`avatar` 在 MenuItem 內維持原樣,因 Avatar 僅在 disabled Field wrapper context 內自 dim)
213
220
  - ❌ header item 不可被選中
214
221
  - ❌ 不在 item 內放獨立互動元素(如 Button)——item 本身就是互動單位
215
222
 
@@ -217,7 +224,7 @@ Menu item 的 prefix icon 跟 label 同色(foreground),不是 fg-muted。P
217
224
 
218
225
  ## 何時用 / 何時不用
219
226
 
220
- **MenuItem 是 internal primitive**——不直接使用,透過 `SelectMenu` / `DropdownMenu` 等外層 menu 元件消費。
227
+ **MenuItem 是 internal primitive**——不進 root barrel front-door、不裸用;一般透過 `SelectMenu` / `DropdownMenu` 等外層 menu 元件消費;consumer 自建 nav row 等場景可經 per-component subpath 包裝並自行確認後使用(`.claude/rules/ui-development.md`「Root barrel front-door 排除」SSOT,internal ≠ 禁用)。
221
228
 
222
229
  | 場景 | 正確做法 |
223
230
  |------|---------|
@@ -1,17 +1,20 @@
1
1
  import React from 'react'
2
2
  import type { Meta, StoryObj } from '@storybook/react'
3
3
  import { useState } from 'react'
4
- import { Mail, Star, Bell, Settings, Plus, Folder, FileText, BarChart3 } from 'lucide-react'
4
+ import { Mail, Star, Bell, Settings, Plus, Folder, FileText, BarChart3, Bug, Rocket } from 'lucide-react'
5
5
  import { MenuItem, MenuGroup, MenuFooter } from './menu-item'
6
+ import { CAT_SOLID, type CategoricalHue } from '@/design-system/tokens/categorical-color'
6
7
  import { ProfileCard, ProfileCardDefaultActions } from '@/design-system/components/ProfileCard/profile-card'
7
8
 
8
9
  // DS-wide canonical:person avatar 必 hover → ProfileCard(含 status / statusMessage / fields
9
10
  // / actions / onViewMore,profile-card.spec.md 重要資訊)。demo helper:
11
+ // 真實人像(2026-07-08 sweep):固定 u= key 保 determinism(對齊 avatar.stories.tsx pravatar 慣例)
12
+ const avatarSrc = (name: string, px = 96) => `https://i.pravatar.cc/${px}?u=${name.toLowerCase().replace(/\s+/g, '-')}`
10
13
  const personHover = (name: string, subtitle?: string) => (
11
14
  <ProfileCard
12
15
  name={name}
13
16
  subtitle={subtitle ?? 'Design|D-0042|EMP-1001'}
14
- avatar={{ alt: name }}
17
+ avatar={{ src: avatarSrc(name), alt: name }}
15
18
  status="online"
16
19
  statusMessage="Out of Office: Back on Monday!"
17
20
  actions={<ProfileCardDefaultActions />}
@@ -77,9 +80,9 @@ export const AvatarInline: Story = {
77
80
  name: '頭像',
78
81
  render: () => (
79
82
  <MenuContainer><MenuGroup>
80
- <MenuItem avatar={{ alt: "Alice Chen", color: "indigo", hoverCard: personHover('Alice Chen') }}>Alice Chen</MenuItem>
81
- <MenuItem avatar={{ alt: "Bob Wang", color: "magenta", hoverCard: personHover('Bob Wang') }}>Bob Wang</MenuItem>
82
- <MenuItem avatar={{ alt: "Carol Lin", color: "green", hoverCard: personHover('Carol Lin') }}>Carol Lin</MenuItem>
83
+ <MenuItem avatar={{ src: avatarSrc('Alice Chen', 48), alt: "Alice Chen", color: "indigo", hoverCard: personHover('Alice Chen') }}>Alice Chen</MenuItem>
84
+ <MenuItem avatar={{ src: avatarSrc('Bob Wang', 48), alt: "Bob Wang", color: "magenta", hoverCard: personHover('Bob Wang') }}>Bob Wang</MenuItem>
85
+ <MenuItem avatar={{ src: avatarSrc('Carol Lin', 48), alt: "Carol Lin", color: "green", hoverCard: personHover('Carol Lin') }}>Carol Lin</MenuItem>
83
86
  </MenuGroup></MenuContainer>
84
87
  ),
85
88
  }
@@ -90,13 +93,38 @@ export const AvatarBlock: Story = {
90
93
  name: '頭像 + 說明文字',
91
94
  render: () => (
92
95
  <MenuContainer><MenuGroup>
93
- <MenuItem avatar={{ alt: "Alice Chen", color: "indigo", hoverCard: personHover('Alice Chen', '設計部門') }} description="設計部門">Alice Chen</MenuItem>
94
- <MenuItem avatar={{ alt: "Bob Wang", color: "magenta", hoverCard: personHover('Bob Wang', '工程部門') }} description="工程部門">Bob Wang</MenuItem>
95
- <MenuItem avatar={{ alt: "Carol Lin", color: "green", hoverCard: personHover('Carol Lin', '行銷部門') }} description="行銷部門">Carol Lin</MenuItem>
96
+ <MenuItem avatar={{ src: avatarSrc('Alice Chen', 64), alt: "Alice Chen", color: "indigo", hoverCard: personHover('Alice Chen', '設計部門') }} description="設計部門">Alice Chen</MenuItem>
97
+ <MenuItem avatar={{ src: avatarSrc('Bob Wang', 64), alt: "Bob Wang", color: "magenta", hoverCard: personHover('Bob Wang', '工程部門') }} description="工程部門">Bob Wang</MenuItem>
98
+ <MenuItem avatar={{ src: avatarSrc('Carol Lin', 64), alt: "Carol Lin", color: "green", hoverCard: personHover('Carol Lin', '行銷部門') }} description="行銷部門">Carol Lin</MenuItem>
96
99
  </MenuGroup></MenuContainer>
97
100
  ),
98
101
  }
99
102
 
103
+ // ── startContent(自訂 leading 徽章;佔位排列 = startIcon 同容器,見 menu-item.spec.md「結構」)──
104
+
105
+ /** 類型徽章 demo helper — 消費 CAT_SOLID token pair(彩底 + 深/白字由 token 決定);size-4 = ICON_SIZE sm/md */
106
+ const TypeBadge = ({ hue, glyph: Glyph }: { hue: CategoricalHue; glyph: typeof FileText }) => (
107
+ <span aria-hidden className={`inline-flex size-4 shrink-0 items-center justify-center rounded-md ${CAT_SOLID[hue]}`}>
108
+ <Glyph className="size-3" strokeWidth={2.5} />
109
+ </span>
110
+ )
111
+
112
+ export const WithStartContent: Story = {
113
+ name: '自訂前綴內容',
114
+ render: () => (
115
+ // 工作項類型設定的左側導覽(Jira / Meegle type settings):類型徽章 = 彩底 + glyph,
116
+ // LucideIcon 表達不了 → startContent;selected = 目前編輯中的類型
117
+ <MenuContainer width={224}>
118
+ <MenuGroup>
119
+ <MenuItem size="sm" startContent={<TypeBadge hue="indigo" glyph={FileText} />} selected>Task</MenuItem>
120
+ <MenuItem size="sm" startContent={<TypeBadge hue="red" glyph={Bug} />}>Bug</MenuItem>
121
+ <MenuItem size="sm" startContent={<TypeBadge hue="turquoise" glyph={Rocket} />}>Release</MenuItem>
122
+ <MenuItem size="sm" startContent={<TypeBadge hue="purple" glyph={FileText} />} disabled>Epic(已停用)</MenuItem>
123
+ </MenuGroup>
124
+ </MenuContainer>
125
+ ),
126
+ }
127
+
100
128
  // ── 狀態 ──
101
129
 
102
130
  export const States: Story = {
@@ -197,9 +225,9 @@ const FullExampleDemo = () => {
197
225
  <MenuContainer>
198
226
  <MenuGroup>
199
227
  <MenuItem header>成員</MenuItem>
200
- <MenuItem checkbox checked={selected.alice} onClick={() => toggle('alice')} avatar={{ alt: "Alice", color: "indigo" }} description="設計部門">Alice Chen</MenuItem>
201
- <MenuItem checkbox checked={selected.bob} onClick={() => toggle('bob')} avatar={{ alt: "Bob", color: "magenta" }} description="工程部門">Bob Wang</MenuItem>
202
- <MenuItem checkbox checked={false} avatar={{ alt: "Carol", color: "green" }} description="行銷部門" disabled>Carol Lin(已離職)</MenuItem>
228
+ <MenuItem checkbox checked={selected.alice} onClick={() => toggle('alice')} avatar={{ src: avatarSrc('Alice Chen', 64), alt: "Alice", color: "indigo" }} description="設計部門">Alice Chen</MenuItem>
229
+ <MenuItem checkbox checked={selected.bob} onClick={() => toggle('bob')} avatar={{ src: avatarSrc('Bob Wang', 64), alt: "Bob", color: "magenta" }} description="工程部門">Bob Wang</MenuItem>
230
+ <MenuItem checkbox checked={false} avatar={{ src: avatarSrc('Carol Lin', 64), alt: "Carol", color: "green" }} description="行銷部門" disabled>Carol Lin(已離職)</MenuItem>
203
231
  </MenuGroup>
204
232
  <MenuFooter>
205
233
  <MenuItem checkbox checked={allState} onClick={toggleAll}>全部</MenuItem>