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

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 (622) 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-group.d.ts +47 -0
  53. package/dist/components/DataTable/data-table-filter-group.d.ts.map +1 -0
  54. package/dist/components/DataTable/data-table-filter-group.js +154 -0
  55. package/dist/components/DataTable/data-table-filter-group.js.map +1 -0
  56. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  57. package/dist/components/DataTable/data-table-filter-panel.js +285 -0
  58. package/dist/components/DataTable/data-table-filter-panel.js.map +1 -0
  59. package/dist/components/DataTable/data-table-filter-value-picker.d.ts +29 -0
  60. package/dist/components/DataTable/data-table-filter-value-picker.d.ts.map +1 -0
  61. package/dist/components/DataTable/data-table-filter-value-picker.js +187 -0
  62. package/dist/components/DataTable/data-table-filter-value-picker.js.map +1 -0
  63. package/dist/components/DataTable/data-table-interaction-layer.d.ts +11 -15
  64. package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
  65. package/dist/components/DataTable/data-table-interaction-layer.js +1 -3
  66. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  67. package/dist/components/DataTable/data-table-sort-manager.js +140 -0
  68. package/dist/components/DataTable/data-table-sort-manager.js.map +1 -0
  69. package/dist/components/DataTable/data-table.d.ts +5 -6
  70. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  71. package/dist/components/DataTable/data-table.js +36 -15
  72. package/dist/components/DataTable/data-table.js.map +1 -1
  73. package/dist/components/DataTable/filter-operators.js +140 -0
  74. package/dist/components/DataTable/filter-operators.js.map +1 -0
  75. package/dist/components/DataTable/filter-tree.js +312 -0
  76. package/dist/components/DataTable/filter-tree.js.map +1 -0
  77. package/dist/components/DataTable/index.d.ts +3 -0
  78. package/dist/components/DataTable/index.d.ts.map +1 -1
  79. package/dist/components/DataTable/index.js +12 -1
  80. package/dist/components/DataTable/index.js.map +1 -1
  81. package/dist/components/DataTable/lib/column-meta.js +25 -0
  82. package/dist/components/DataTable/lib/column-meta.js.map +1 -0
  83. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  84. package/dist/components/DateGrid/date-grid.js +3 -1
  85. package/dist/components/DateGrid/date-grid.js.map +1 -1
  86. package/dist/components/DatePicker/date-picker.d.ts +7 -1
  87. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  88. package/dist/components/DatePicker/date-picker.js +105 -74
  89. package/dist/components/DatePicker/date-picker.js.map +1 -1
  90. package/dist/components/Dialog/dialog.d.ts +20 -3
  91. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  92. package/dist/components/Dialog/dialog.js +38 -20
  93. package/dist/components/Dialog/dialog.js.map +1 -1
  94. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  95. package/dist/components/DropdownMenu/dropdown-menu.js +3 -1
  96. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  97. package/dist/components/Field/field-context.d.ts +45 -5
  98. package/dist/components/Field/field-context.d.ts.map +1 -1
  99. package/dist/components/Field/field-context.js +19 -0
  100. package/dist/components/Field/field-context.js.map +1 -1
  101. package/dist/components/Field/field-edit-keys.d.ts +21 -0
  102. package/dist/components/Field/field-edit-keys.d.ts.map +1 -0
  103. package/dist/components/Field/field-edit-keys.js +19 -0
  104. package/dist/components/Field/field-edit-keys.js.map +1 -0
  105. package/dist/components/Field/field-types.d.ts +20 -1
  106. package/dist/components/Field/field-types.d.ts.map +1 -1
  107. package/dist/components/Field/field-types.js.map +1 -1
  108. package/dist/components/Field/field-wrapper.d.ts +3 -2
  109. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  110. package/dist/components/Field/field-wrapper.js +38 -39
  111. package/dist/components/Field/field-wrapper.js.map +1 -1
  112. package/dist/components/Field/field.d.ts +2 -1
  113. package/dist/components/Field/field.d.ts.map +1 -1
  114. package/dist/components/Field/field.js.map +1 -1
  115. package/dist/components/Field/index.d.ts +1 -0
  116. package/dist/components/Field/index.d.ts.map +1 -1
  117. package/dist/components/Field/index.js +2 -0
  118. package/dist/components/Field/index.js.map +1 -1
  119. package/dist/components/FieldControlGroup/field-control-group.d.ts +1 -1
  120. package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
  121. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  122. package/dist/components/FileItem/file-item.js +1 -1
  123. package/dist/components/FileItem/file-item.js.map +1 -1
  124. package/dist/components/FileUpload/file-upload.d.ts +1 -1
  125. package/dist/components/FileUpload/file-upload.js.map +1 -1
  126. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  127. package/dist/components/FileViewer/file-viewer.js +7 -2
  128. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  129. package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
  130. package/dist/components/FileViewer/image-renderer.js +22 -1
  131. package/dist/components/FileViewer/image-renderer.js.map +1 -1
  132. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  133. package/dist/components/HoverCard/hover-card.js +5 -3
  134. package/dist/components/HoverCard/hover-card.js.map +1 -1
  135. package/dist/components/InlineEdit/index.d.ts +2 -0
  136. package/dist/components/InlineEdit/index.d.ts.map +1 -0
  137. package/dist/components/InlineEdit/index.js +5 -0
  138. package/dist/components/InlineEdit/index.js.map +1 -0
  139. package/dist/components/InlineEdit/inline-edit.d.ts +59 -0
  140. package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -0
  141. package/dist/components/InlineEdit/inline-edit.js +164 -0
  142. package/dist/components/InlineEdit/inline-edit.js.map +1 -0
  143. package/dist/components/Input/input.d.ts +6 -11
  144. package/dist/components/Input/input.d.ts.map +1 -1
  145. package/dist/components/Input/input.js +43 -13
  146. package/dist/components/Input/input.js.map +1 -1
  147. package/dist/components/LinkInput/link-input.d.ts +2 -2
  148. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  149. package/dist/components/LinkInput/link-input.js +6 -5
  150. package/dist/components/LinkInput/link-input.js.map +1 -1
  151. package/dist/components/Menu/menu-item.d.ts +14 -2
  152. package/dist/components/Menu/menu-item.d.ts.map +1 -1
  153. package/dist/components/Menu/menu-item.js +25 -3
  154. package/dist/components/Menu/menu-item.js.map +1 -1
  155. package/dist/components/NumberInput/number-input.d.ts +3 -3
  156. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  157. package/dist/components/NumberInput/number-input.js +6 -5
  158. package/dist/components/NumberInput/number-input.js.map +1 -1
  159. package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
  160. package/dist/components/OverflowIndicator/overflow-indicator.js +2 -6
  161. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  162. package/dist/components/Pagination/pagination.js +1 -1
  163. package/dist/components/Pagination/pagination.js.map +1 -1
  164. package/dist/components/PeoplePicker/index.js +7 -1
  165. package/dist/components/PeoplePicker/index.js.map +1 -1
  166. package/dist/components/PeoplePicker/people-picker.d.ts +9 -2
  167. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  168. package/dist/components/PeoplePicker/people-picker.js +23 -10
  169. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  170. package/dist/components/PeoplePicker/person-display.d.ts +6 -3
  171. package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
  172. package/dist/components/PeoplePicker/person-display.js +19 -14
  173. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  174. package/dist/components/Popover/popover.d.ts.map +1 -1
  175. package/dist/components/Popover/popover.js +3 -1
  176. package/dist/components/Popover/popover.js.map +1 -1
  177. package/dist/components/ProfileCard/profile-card.d.ts +2 -2
  178. package/dist/components/ProfileCard/profile-card.js +3 -3
  179. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  180. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  181. package/dist/components/RadioGroup/radio-group.js +15 -9
  182. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  183. package/dist/components/ScrollArea/scroll-area.d.ts +11 -1
  184. package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
  185. package/dist/components/ScrollArea/scroll-area.js +6 -3
  186. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  187. package/dist/components/Select/select.d.ts +46 -5
  188. package/dist/components/Select/select.d.ts.map +1 -1
  189. package/dist/components/Select/select.js +109 -28
  190. package/dist/components/Select/select.js.map +1 -1
  191. package/dist/components/SelectMenu/select-menu.d.ts +3 -1
  192. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  193. package/dist/components/SelectMenu/select-menu.js +8 -1
  194. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  195. package/dist/components/SelectionControl/boolean-value.d.ts +11 -0
  196. package/dist/components/SelectionControl/boolean-value.d.ts.map +1 -0
  197. package/dist/components/SelectionControl/boolean-value.js +23 -0
  198. package/dist/components/SelectionControl/boolean-value.js.map +1 -0
  199. package/dist/components/SelectionControl/index.d.ts +1 -0
  200. package/dist/components/SelectionControl/index.d.ts.map +1 -1
  201. package/dist/components/SelectionControl/index.js +2 -0
  202. package/dist/components/SelectionControl/index.js.map +1 -1
  203. package/dist/components/Sheet/index.js +2 -1
  204. package/dist/components/Sheet/sheet.d.ts +7 -1
  205. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  206. package/dist/components/Sheet/sheet.js +5 -2
  207. package/dist/components/Sheet/sheet.js.map +1 -1
  208. package/dist/components/Sidebar/sidebar.d.ts +9 -4
  209. package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
  210. package/dist/components/Sidebar/sidebar.js +8 -10
  211. package/dist/components/Sidebar/sidebar.js.map +1 -1
  212. package/dist/components/Slider/slider.d.ts.map +1 -1
  213. package/dist/components/Slider/slider.js.map +1 -1
  214. package/dist/components/Switch/switch.d.ts +6 -6
  215. package/dist/components/Switch/switch.d.ts.map +1 -1
  216. package/dist/components/Switch/switch.js +3 -2
  217. package/dist/components/Switch/switch.js.map +1 -1
  218. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  219. package/dist/components/Tabs/tabs.js +15 -1
  220. package/dist/components/Tabs/tabs.js.map +1 -1
  221. package/dist/components/Textarea/textarea.d.ts +2 -2
  222. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  223. package/dist/components/Textarea/textarea.js +50 -42
  224. package/dist/components/Textarea/textarea.js.map +1 -1
  225. package/dist/components/TimePicker/time-columns.d.ts.map +1 -1
  226. package/dist/components/TimePicker/time-columns.js +42 -38
  227. package/dist/components/TimePicker/time-columns.js.map +1 -1
  228. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  229. package/dist/components/TimePicker/time-picker.js +8 -6
  230. package/dist/components/TimePicker/time-picker.js.map +1 -1
  231. package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
  232. package/dist/components/Tooltip/tooltip.js +7 -5
  233. package/dist/components/Tooltip/tooltip.js.map +1 -1
  234. package/dist/components/TreeView/tree-view.d.ts +56 -1
  235. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  236. package/dist/components/TreeView/tree-view.js +211 -12
  237. package/dist/components/TreeView/tree-view.js.map +1 -1
  238. package/dist/index.d.ts +115 -58
  239. package/dist/index.d.ts.map +1 -1
  240. package/dist/index.js +95 -108
  241. package/dist/index.js.map +1 -1
  242. package/dist/patterns/element-anatomy/item-anatomy.d.ts +7 -6
  243. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  244. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  245. package/dist/tokens/categorical-color.js +15 -0
  246. package/dist/tokens/categorical-color.js.map +1 -1
  247. package/dist/tokens/motion/motion.d.ts +7 -6
  248. package/dist/tokens/motion/motion.d.ts.map +1 -1
  249. package/dist/tokens/motion/motion.js +6 -6
  250. package/dist/tokens/motion/motion.js.map +1 -1
  251. package/dist/tokens/motion/overlay-motion.d.ts +19 -0
  252. package/dist/tokens/motion/overlay-motion.d.ts.map +1 -0
  253. package/dist/tokens/motion/overlay-motion.js +9 -0
  254. package/dist/tokens/motion/overlay-motion.js.map +1 -0
  255. package/dist/tokens/uiSize/icon-size.js +5 -0
  256. package/dist/tokens/uiSize/icon-size.js.map +1 -0
  257. package/ds-canonical/fork/governance.lock +31 -19
  258. package/ds-canonical/fork/hooks/_log-fire.sh +46 -0
  259. package/ds-canonical/fork/hooks/block_prototype_imports.py +15 -4
  260. package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +145 -13
  261. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +2 -1
  262. package/ds-canonical/fork/hooks/check_dynamic_tailwind_class.sh +47 -0
  263. package/ds-canonical/fork/hooks/check_escape_marker_abuse.sh +27 -0
  264. package/ds-canonical/fork/hooks/check_field_family_invariants.sh +61 -1
  265. package/ds-canonical/fork/hooks/check_layout_space_magic_numbers.sh +28 -4
  266. package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +28 -0
  267. package/ds-canonical/fork/hooks/check_story_determinism.sh +2 -0
  268. package/ds-canonical/fork/hooks/check_story_invariants.sh +119 -0
  269. package/ds-canonical/fork/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
  270. package/ds-canonical/fork/manifest.json +15 -0
  271. package/ds-canonical/fork/preamble.md +20 -18
  272. package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +3 -3
  273. package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +3 -3
  274. package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +1 -1
  275. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +3 -3
  276. package/ds-canonical/fork/skills/propose-options/SKILL.md +3 -3
  277. package/ds-canonical/fork/skills/prototype/SKILL.md +1 -1
  278. package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +2 -2
  279. package/ds-canonical/fork/skills/visual-audit/SKILL.md +2 -2
  280. package/ds-canonical/hooks/README.md +1 -1
  281. package/ds-canonical/hooks/block_prototype_imports.py +15 -4
  282. package/ds-canonical/hooks/check_audit_post_report_validator.sh +65 -7
  283. package/ds-canonical/hooks/check_codex_brief_invariants.sh +28 -1
  284. package/ds-canonical/hooks/check_consumer_app_invariants.sh +145 -13
  285. package/ds-canonical/hooks/check_datatable_invariants.sh +6 -3
  286. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +2 -1
  287. package/ds-canonical/hooks/check_dynamic_tailwind_class.sh +47 -0
  288. package/ds-canonical/hooks/check_escape_marker_abuse.sh +27 -0
  289. package/ds-canonical/hooks/check_field_family_invariants.sh +61 -1
  290. package/ds-canonical/hooks/check_layout_space_magic_numbers.sh +28 -4
  291. package/ds-canonical/hooks/check_naming_and_abstraction.sh +1 -1
  292. package/ds-canonical/hooks/check_opacity_token_usage.sh +28 -0
  293. package/ds-canonical/hooks/check_pixel_quantified_audit.sh +3 -2
  294. package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +6 -1
  295. package/ds-canonical/hooks/check_ssot_header_declaration.sh +3 -1
  296. package/ds-canonical/hooks/check_story_determinism.sh +2 -0
  297. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +8 -7
  298. package/ds-canonical/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
  299. package/ds-canonical/hooks/lib/_story_compile_drift.sh +1 -1
  300. package/ds-canonical/hooks/lib/_token_hygiene.sh +10 -1
  301. package/ds-canonical/hooks/stop_self_audit.sh +31 -0
  302. package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +24 -0
  303. package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +44 -0
  304. package/ds-canonical/hooks/tests/test_check_codex_brief_invariants.sh +43 -2
  305. package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +112 -0
  306. package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +9 -2
  307. package/ds-canonical/hooks/tests/test_check_dynamic_tailwind_class.sh +76 -0
  308. package/ds-canonical/hooks/tests/test_check_escape_marker_abuse.sh +24 -0
  309. package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +27 -2
  310. package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +39 -0
  311. package/ds-canonical/hooks/tests/test_check_tabs_content_chrome_body_double_gap.sh +76 -0
  312. package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
  313. package/ds-canonical/references/build-ui-canonicals.md +2 -1
  314. package/ds-canonical/references/failure-class-registry.json +145 -0
  315. package/ds-canonical/references/governance-audit-coverage.md +33 -0
  316. package/ds-canonical/references/item-anatomy-recipe.md +2 -2
  317. package/ds-canonical/references/scenario-definition.md +6 -9
  318. package/ds-canonical/references/ssot-consultation.md +2 -2
  319. package/ds-canonical/references/ssot-index.md +5 -4
  320. package/ds-canonical/references/story-baseline-registry.json +8 -2
  321. package/ds-canonical/references/structural-token-retention.md +6 -9
  322. package/ds-canonical/rules/meta-patterns.md +8 -8
  323. package/ds-canonical/rules/self-verify.md +4 -3
  324. package/ds-canonical/rules/story-rules.md +2 -2
  325. package/ds-canonical/rules/ui-development.md +2 -2
  326. package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +3 -3
  327. package/ds-canonical/skills/code-quality-audit/SKILL.md +3 -3
  328. package/ds-canonical/skills/codex-collab/SKILL.md +9 -7
  329. package/ds-canonical/skills/codex-collab/references/brief-template.md +1 -1
  330. package/ds-canonical/skills/codify-principle/SKILL.md +2 -2
  331. package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
  332. package/ds-canonical/skills/component-quality-gate/references/checklist.md +2 -2
  333. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +51 -58
  334. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +29 -0
  335. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +29 -10
  336. package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +21 -0
  337. package/ds-canonical/skills/delivery-handoff/SKILL.md +1 -1
  338. package/ds-canonical/skills/design-system-audit/SKILL.md +26 -25
  339. package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +13 -1
  340. package/ds-canonical/skills/design-system-audit/references/rule-placement.md +1 -1
  341. package/ds-canonical/skills/governance-health/SKILL.md +1 -1
  342. package/ds-canonical/skills/knowledge-prune/SKILL.md +3 -0
  343. package/ds-canonical/skills/knowledge-prune/references/prune-dimensions-d5-d10.md +1 -0
  344. package/ds-canonical/skills/new-component/SKILL.md +5 -5
  345. package/ds-canonical/skills/new-component/references/new-component-checklist.md +1 -1
  346. package/ds-canonical/skills/product-ui-audit/SKILL.md +3 -3
  347. package/ds-canonical/skills/propose-options/SKILL.md +3 -3
  348. package/ds-canonical/skills/prototype/SKILL.md +1 -1
  349. package/ds-canonical/skills/scan-similar-bugs/SKILL.md +2 -2
  350. package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +3 -3
  351. package/ds-canonical/skills/story-writing/SKILL.md +3 -3
  352. package/ds-canonical/skills/visual-audit/SKILL.md +2 -2
  353. package/ds-story-manifest.json +29 -20
  354. package/llms-full.txt +22 -1
  355. package/llms.txt +5 -4
  356. package/package.json +7 -3
  357. package/src/components/Accordion/accordion.principles.stories.tsx +1 -1
  358. package/src/components/Accordion/accordion.stories.tsx +16 -1
  359. package/src/components/Alert/alert.principles.stories.tsx +1 -1
  360. package/src/components/Alert/alert.spec.md +8 -10
  361. package/src/components/Alert/alert.stories.tsx +28 -28
  362. package/src/components/Alert/alert.tsx +1 -1
  363. package/src/components/AppShell/_demo-helpers.tsx +6 -3
  364. package/src/components/AppShell/app-shell.spec.md +4 -4
  365. package/src/components/AppShell/app-shell.tsx +28 -2
  366. package/src/components/AspectRatio/aspect-ratio.spec.md +1 -1
  367. package/src/components/AspectRatio/aspect-ratio.stories.tsx +7 -89
  368. package/src/components/Avatar/avatar.anatomy.stories.tsx +7 -3
  369. package/src/components/Avatar/avatar.principles.stories.tsx +19 -15
  370. package/src/components/Avatar/avatar.spec.md +2 -2
  371. package/src/components/Avatar/avatar.stories.tsx +8 -6
  372. package/src/components/Avatar/avatar.tsx +13 -5
  373. package/src/components/Badge/badge.principles.stories.tsx +1 -1
  374. package/src/components/Badge/badge.spec.md +2 -0
  375. package/src/components/Badge/badge.stories.tsx +4 -2
  376. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +6 -4
  377. package/src/components/Breadcrumb/breadcrumb.stories.tsx +6 -5
  378. package/src/components/Breadcrumb/breadcrumb.tsx +3 -2
  379. package/src/components/BulkActionBar/bulk-action-bar.spec.md +4 -2
  380. package/src/components/Button/button.principles.stories.tsx +1 -1
  381. package/src/components/Button/button.spec.md +2 -2
  382. package/src/components/Button/button.tsx +18 -3
  383. package/src/components/Calendar/calendar.anatomy.stories.tsx +12 -10
  384. package/src/components/Calendar/calendar.principles.stories.tsx +14 -6
  385. package/src/components/Calendar/calendar.spec.md +5 -4
  386. package/src/components/Calendar/calendar.stories.tsx +7 -4
  387. package/src/components/Calendar/calendar.tsx +26 -7
  388. package/src/components/Carousel/carousel.anatomy.stories.tsx +5 -4
  389. package/src/components/Carousel/carousel.spec.md +2 -2
  390. package/src/components/Carousel/carousel.tsx +11 -3
  391. package/src/components/Chart/chart.anatomy.stories.tsx +7 -4
  392. package/src/components/Chart/chart.principles.stories.tsx +1 -1
  393. package/src/components/Chart/chart.stories.tsx +2 -8
  394. package/src/components/Chart/chart.tsx +3 -0
  395. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +28 -49
  396. package/src/components/Checkbox/checkbox.principles.stories.tsx +1 -1
  397. package/src/components/Checkbox/checkbox.spec.md +13 -26
  398. package/src/components/Checkbox/checkbox.stories.tsx +11 -8
  399. package/src/components/Checkbox/checkbox.tsx +18 -13
  400. package/src/components/Chip/chip.anatomy.stories.tsx +44 -24
  401. package/src/components/Chip/chip.principles.stories.tsx +1 -1
  402. package/src/components/Chip/chip.spec.md +5 -4
  403. package/src/components/Chip/chip.tsx +5 -1
  404. package/src/components/CircularProgress/circular-progress.principles.stories.tsx +7 -4
  405. package/src/components/CircularProgress/circular-progress.spec.md +3 -3
  406. package/src/components/CircularProgress/circular-progress.stories.tsx +2 -2
  407. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +2 -2
  408. package/src/components/Coachmark/coachmark.principles.stories.tsx +2 -1
  409. package/src/components/Coachmark/coachmark.spec.md +5 -5
  410. package/src/components/Coachmark/coachmark.stories.tsx +3 -1
  411. package/src/components/Coachmark/coachmark.tsx +10 -3
  412. package/src/components/Combobox/combobox.anatomy.stories.tsx +2 -2
  413. package/src/components/Combobox/combobox.tsx +53 -19
  414. package/src/components/Command/command.anatomy.stories.tsx +2 -2
  415. package/src/components/Command/command.principles.stories.tsx +1 -1
  416. package/src/components/Command/command.stories.tsx +42 -10
  417. package/src/components/DataTable/cell-registry.tsx +86 -63
  418. package/src/components/DataTable/column-types.ts +10 -1
  419. package/src/components/DataTable/data-table-column-visibility-panel.tsx +1 -1
  420. package/src/components/DataTable/data-table-filter-group.tsx +231 -0
  421. package/src/components/DataTable/data-table-filter-panel.tsx +16 -430
  422. package/src/components/DataTable/data-table-filter-value-picker.tsx +237 -0
  423. package/src/components/DataTable/data-table-interaction-layer.tsx +15 -19
  424. package/src/components/DataTable/data-table.anatomy.stories.tsx +15 -13
  425. package/src/components/DataTable/data-table.principles.stories.tsx +9 -9
  426. package/src/components/DataTable/data-table.spec.md +33 -13
  427. package/src/components/DataTable/data-table.stories.tsx +16 -17
  428. package/src/components/DataTable/data-table.tsx +107 -61
  429. package/src/components/DataTable/filter-operators.spec.md +3 -3
  430. package/src/components/DataTable/index.ts +3 -0
  431. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +4 -4
  432. package/src/components/DateGrid/date-grid.principles.stories.tsx +1 -1
  433. package/src/components/DateGrid/date-grid.spec.md +1 -1
  434. package/src/components/DateGrid/date-grid.tsx +3 -1
  435. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +8 -8
  436. package/src/components/DatePicker/date-picker.principles.stories.tsx +3 -3
  437. package/src/components/DatePicker/date-picker.spec.md +3 -3
  438. package/src/components/DatePicker/date-picker.stories.tsx +4 -5
  439. package/src/components/DatePicker/date-picker.tsx +99 -45
  440. package/src/components/DescriptionList/description-list.anatomy.stories.tsx +12 -10
  441. package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
  442. package/src/components/DescriptionList/description-list.spec.md +3 -3
  443. package/src/components/DescriptionList/description-list.stories.tsx +7 -6
  444. package/src/components/Dialog/dialog.anatomy.stories.tsx +1 -0
  445. package/src/components/Dialog/dialog.spec.md +9 -0
  446. package/src/components/Dialog/dialog.stories.tsx +66 -5
  447. package/src/components/Dialog/dialog.tsx +60 -25
  448. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
  449. package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +8 -6
  450. package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -5
  451. package/src/components/DropdownMenu/dropdown-menu.tsx +18 -9
  452. package/src/components/Empty/empty.anatomy.stories.tsx +1 -1
  453. package/src/components/Empty/empty.spec.md +2 -2
  454. package/src/components/Field/field-context.ts +69 -5
  455. package/src/components/Field/field-controls.spec.md +62 -8
  456. package/src/components/Field/field-edit-keys.ts +66 -0
  457. package/src/components/Field/field-types.ts +58 -9
  458. package/src/components/Field/field-wrapper.tsx +50 -42
  459. package/src/components/Field/field.anatomy.stories.tsx +9 -6
  460. package/src/components/Field/field.spec.md +1 -1
  461. package/src/components/Field/field.stories.tsx +1 -1
  462. package/src/components/Field/field.tsx +2 -1
  463. package/src/components/Field/form-validation.spec.md +3 -3
  464. package/src/components/Field/index.ts +1 -0
  465. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +6 -6
  466. package/src/components/FieldControlGroup/field-control-group.spec.md +9 -4
  467. package/src/components/FieldControlGroup/field-control-group.tsx +1 -1
  468. package/src/components/FileItem/file-item.anatomy.stories.tsx +1 -1
  469. package/src/components/FileItem/file-item.spec.md +5 -8
  470. package/src/components/FileItem/file-item.stories.tsx +36 -25
  471. package/src/components/FileItem/file-item.tsx +8 -6
  472. package/src/components/FileUpload/file-upload.anatomy.stories.tsx +6 -6
  473. package/src/components/FileUpload/file-upload.principles.stories.tsx +5 -5
  474. package/src/components/FileUpload/file-upload.spec.md +1 -1
  475. package/src/components/FileUpload/file-upload.tsx +1 -1
  476. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +4 -3
  477. package/src/components/FileViewer/file-viewer.principles.stories.tsx +3 -3
  478. package/src/components/FileViewer/file-viewer.spec.md +30 -12
  479. package/src/components/FileViewer/file-viewer.stories.tsx +1 -2
  480. package/src/components/FileViewer/file-viewer.tsx +12 -4
  481. package/src/components/FileViewer/image-renderer.tsx +35 -7
  482. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -2
  483. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -3
  484. package/src/components/HoverCard/hover-card.spec.md +5 -5
  485. package/src/components/HoverCard/hover-card.stories.tsx +16 -16
  486. package/src/components/HoverCard/hover-card.tsx +5 -3
  487. package/src/components/InlineEdit/index.ts +5 -0
  488. package/src/components/InlineEdit/inline-edit.spec.md +149 -0
  489. package/src/components/InlineEdit/inline-edit.stories.tsx +246 -0
  490. package/src/components/InlineEdit/inline-edit.tsx +287 -0
  491. package/src/components/Input/input.anatomy.stories.tsx +8 -7
  492. package/src/components/Input/input.principles.stories.tsx +2 -12
  493. package/src/components/Input/input.spec.md +13 -18
  494. package/src/components/Input/input.tsx +69 -22
  495. package/src/components/LinkInput/link-input.anatomy.stories.tsx +1 -1
  496. package/src/components/LinkInput/link-input.principles.stories.tsx +2 -2
  497. package/src/components/LinkInput/link-input.tsx +11 -10
  498. package/src/components/Menu/menu-item.anatomy.stories.tsx +3 -3
  499. package/src/components/Menu/menu-item.principles.stories.tsx +1 -1
  500. package/src/components/Menu/menu-item.spec.md +22 -15
  501. package/src/components/Menu/menu-item.stories.tsx +39 -11
  502. package/src/components/Menu/menu-item.tsx +38 -4
  503. package/src/components/Notice/notice.principles.stories.tsx +1 -1
  504. package/src/components/Notice/notice.spec.md +16 -3
  505. package/src/components/NumberInput/number-input.anatomy.stories.tsx +3 -3
  506. package/src/components/NumberInput/number-input.principles.stories.tsx +1 -1
  507. package/src/components/NumberInput/number-input.spec.md +4 -4
  508. package/src/components/NumberInput/number-input.tsx +19 -12
  509. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +10 -8
  510. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +3 -3
  511. package/src/components/OverflowIndicator/overflow-indicator.spec.md +4 -2
  512. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +71 -63
  513. package/src/components/OverflowIndicator/overflow-indicator.tsx +9 -8
  514. package/src/components/Pagination/pagination.spec.md +1 -1
  515. package/src/components/Pagination/pagination.stories.tsx +2 -6
  516. package/src/components/Pagination/pagination.tsx +1 -1
  517. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +4 -4
  518. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +2 -2
  519. package/src/components/PeoplePicker/people-picker.spec.md +6 -6
  520. package/src/components/PeoplePicker/people-picker.tsx +56 -16
  521. package/src/components/PeoplePicker/person-display.tsx +32 -16
  522. package/src/components/Popover/popover.anatomy.stories.tsx +2 -2
  523. package/src/components/Popover/popover.tsx +3 -1
  524. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +12 -23
  525. package/src/components/ProfileCard/profile-card.spec.md +3 -3
  526. package/src/components/ProfileCard/profile-card.tsx +6 -6
  527. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +3 -1
  528. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +1 -1
  529. package/src/components/ProgressBar/progress-bar.stories.tsx +50 -42
  530. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +10 -28
  531. package/src/components/RadioGroup/radio-group.stories.tsx +29 -32
  532. package/src/components/RadioGroup/radio-group.tsx +41 -15
  533. package/src/components/Rating/rating.spec.md +11 -3
  534. package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
  535. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
  536. package/src/components/ScrollArea/scroll-area.spec.md +7 -3
  537. package/src/components/ScrollArea/scroll-area.stories.tsx +1 -1
  538. package/src/components/ScrollArea/scroll-area.tsx +17 -4
  539. package/src/components/Select/select.anatomy.stories.tsx +3 -3
  540. package/src/components/Select/select.spec.md +29 -17
  541. package/src/components/Select/select.stories.tsx +33 -0
  542. package/src/components/Select/select.tsx +190 -36
  543. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +4 -4
  544. package/src/components/SelectMenu/select-menu.spec.md +15 -7
  545. package/src/components/SelectMenu/select-menu.tsx +16 -3
  546. package/src/components/SelectionControl/boolean-value.tsx +39 -0
  547. package/src/components/SelectionControl/index.ts +1 -0
  548. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +2 -2
  549. package/src/components/SelectionControl/selection-item.principles.stories.tsx +2 -2
  550. package/src/components/SelectionControl/selection-item.spec.md +3 -3
  551. package/src/components/SelectionControl/selection-item.stories.tsx +3 -3
  552. package/src/components/Separator/separator.spec.md +2 -2
  553. package/src/components/Separator/separator.stories.tsx +6 -27
  554. package/src/components/Sheet/sheet.anatomy.stories.tsx +1 -1
  555. package/src/components/Sheet/sheet.principles.stories.tsx +1 -1
  556. package/src/components/Sheet/sheet.spec.md +2 -2
  557. package/src/components/Sheet/sheet.tsx +9 -5
  558. package/src/components/Sidebar/sidebar.anatomy.stories.tsx +16 -12
  559. package/src/components/Sidebar/sidebar.principles.stories.tsx +10 -8
  560. package/src/components/Sidebar/sidebar.spec.md +13 -15
  561. package/src/components/Sidebar/sidebar.stories.tsx +8 -7
  562. package/src/components/Sidebar/sidebar.tsx +37 -25
  563. package/src/components/Skeleton/skeleton.stories.tsx +4 -11
  564. package/src/components/Slider/slider.anatomy.stories.tsx +3 -3
  565. package/src/components/Slider/slider.principles.stories.tsx +2 -2
  566. package/src/components/Slider/slider.spec.md +5 -5
  567. package/src/components/Slider/slider.tsx +3 -1
  568. package/src/components/Steps/steps.anatomy.stories.tsx +4 -4
  569. package/src/components/Steps/steps.principles.stories.tsx +1 -1
  570. package/src/components/Steps/steps.spec.md +1 -1
  571. package/src/components/Switch/switch.anatomy.stories.tsx +7 -7
  572. package/src/components/Switch/switch.principles.stories.tsx +4 -4
  573. package/src/components/Switch/switch.spec.md +8 -8
  574. package/src/components/Switch/switch.stories.tsx +9 -24
  575. package/src/components/Switch/switch.tsx +17 -13
  576. package/src/components/Tabs/tabs.anatomy.stories.tsx +10 -7
  577. package/src/components/Tabs/tabs.spec.md +1 -1
  578. package/src/components/Tabs/tabs.tsx +21 -1
  579. package/src/components/Textarea/textarea.anatomy.stories.tsx +1 -1
  580. package/src/components/Textarea/textarea.principles.stories.tsx +1 -1
  581. package/src/components/Textarea/textarea.spec.md +4 -4
  582. package/src/components/Textarea/textarea.tsx +72 -46
  583. package/src/components/TimePicker/time-columns.tsx +6 -2
  584. package/src/components/TimePicker/time-picker.principles.stories.tsx +1 -1
  585. package/src/components/TimePicker/time-picker.spec.md +3 -3
  586. package/src/components/TimePicker/time-picker.tsx +16 -10
  587. package/src/components/Toast/toast.stories.tsx +7 -4
  588. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +4 -4
  589. package/src/components/Tooltip/tooltip.principles.stories.tsx +3 -3
  590. package/src/components/Tooltip/tooltip.spec.md +1 -1
  591. package/src/components/Tooltip/tooltip.stories.tsx +4 -39
  592. package/src/components/Tooltip/tooltip.tsx +8 -6
  593. package/src/components/TreeView/tree-view.anatomy.stories.tsx +6 -3
  594. package/src/components/TreeView/tree-view.spec.md +42 -11
  595. package/src/components/TreeView/tree-view.stories.tsx +1 -0
  596. package/src/components/TreeView/tree-view.tsx +322 -17
  597. package/src/index.ts +588 -60
  598. package/src/patterns/action-bar/action-bar.spec.md +9 -0
  599. package/src/patterns/action-bar/action-bar.stories.tsx +6 -3
  600. package/src/patterns/element-anatomy/inline-action.spec.md +2 -0
  601. package/src/patterns/element-anatomy/item-anatomy.spec.md +14 -5
  602. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +6 -4
  603. package/src/patterns/element-anatomy/item-anatomy.tsx +7 -6
  604. package/src/patterns/header-canonical/header-canonical.spec.md +5 -4
  605. package/src/patterns/overlay-surface/overlay-surface.spec.md +22 -1
  606. package/src/styles/tokens.css +3 -0
  607. package/src/tokens/README.md +1 -1
  608. package/src/tokens/color/color.spec.md +4 -4
  609. package/src/tokens/color/color.stories.tsx +3 -3
  610. package/src/tokens/density/density.stories.tsx +16 -3
  611. package/src/tokens/layoutSpace/layoutSpace.spec.md +3 -1
  612. package/src/tokens/motion/motion.css +33 -21
  613. package/src/tokens/motion/motion.spec.md +32 -15
  614. package/src/tokens/motion/motion.stories.tsx +58 -5
  615. package/src/tokens/motion/motion.ts +7 -6
  616. package/src/tokens/motion/overlay-motion.ts +26 -0
  617. package/src/tokens/orphan-tokens.spec.md +4 -4
  618. package/src/tokens/token-system.spec.md +1 -0
  619. package/src/tokens/uiSize/uiSize.css +1 -1
  620. package/src/tokens/uiSize/uiSize.spec.md +1 -1
  621. package/src/tokens/uiSize/uiSize.stories.tsx +4 -2
  622. package/src/tokens/utility-registry.json +160 -23
@@ -172,14 +172,14 @@ const SidebarProvider = React.forwardRef<
172
172
  const setOpen = React.useCallback(
173
173
  (value: boolean | ((value: boolean) => boolean)) => {
174
174
  const openState = typeof value === "function" ? value(open) : value
175
- if (setOpenProp) {
176
- setOpenProp(openState)
177
- } else {
178
- _setOpen(openState)
179
- }
175
+ // 受控/非受控分岔依「有沒有傳 open prop」判定(同上方 activeId pattern),
176
+ // 不依「有沒有傳 onOpenChange」——否則 uncontrolled + onOpenChange 場景
177
+ // internal state 永不更新,sidebar 凍結(同 SelectMenu R2 已修 bug class)。
178
+ if (openProp === undefined) _setOpen(openState)
179
+ setOpenProp?.(openState)
180
180
  document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
181
181
  },
182
- [setOpenProp, open]
182
+ [openProp, setOpenProp, open]
183
183
  )
184
184
 
185
185
  const toggleSidebar = React.useCallback(() => {
@@ -306,6 +306,10 @@ const Sidebar = React.forwardRef<
306
306
  * 從該值起算(`top: viewportInsetTop / height: calc(100svh - viewportInsetTop)`),
307
307
  * 讓 global header 不被覆蓋(AppShell primary-header mode 必傳)。
308
308
  * 對齊 Mantine `layout="default"` navbar 高度扣 header 慣例。
309
+ *
310
+ * ⚠️ 作用範圍:**僅桌機 `offcanvas` / `icon` 分支**套用 insetStyle。
311
+ * `collapsible="none"`(於 isMobile 前提前 return 固定 div)與 mobile Sheet
312
+ * 兩分支不套此 inset —— 該兩模式下 global header 仍會被覆蓋(未接線)。
309
313
  */
310
314
  viewportInsetTop?: string
311
315
  }
@@ -471,13 +475,15 @@ SidebarInput.displayName = "SidebarInput"
471
475
 
472
476
  // ── Shell regions ──────────────────────────────────────────────────────────
473
477
 
474
- // SidebarHeader / SidebarFooter:
475
- // - 固定高度 `var(--chrome-header-height)`(md=48 / lg=56,density-responsive)
476
- // - 跨元件 chrome header 共享同一個 token,自動跟主內容 page header 對齊
478
+ // SidebarHeader(固定高度 chrome slot)vs SidebarFooter(content-based):
479
+ // - SidebarHeader:固定高度 `var(--chrome-header-height)`(md=48 / lg=56,density-responsive),
480
+ // 跨元件 chrome header 共享同一個 token,自動跟主內容 page header 對齊
481
+ // - SidebarFooter:高度由內容決定(`shrink-0 py-2 border-t`,不消費 chrome-header-height、
482
+ // 無自身固定高度),見下方 SidebarFooter 定義的 docblock + spec「Chrome 內容的約束」段
477
483
  // - 水平 padding 用 loose token(跟 items 的 px 對齊)
478
484
  // - 邊框是結構邊界(分隔 fixed/scroll 區),full-width 不內縮
479
485
  //
480
- // 為什麼 density-responsive:chrome 裡放的 button 綁定 field-height token,
486
+ // 為什麼 Header density-responsive:chrome 裡放的 button 綁定 field-height token,
481
487
  // 會隨 density 變大。Chrome 如果不跟著放大,lg density 下 padding 會被擠壓。
482
488
  const SidebarHeader = React.forwardRef<
483
489
  HTMLDivElement,
@@ -516,10 +522,11 @@ const SidebarHeader = React.forwardRef<
516
522
  // JS 端透過 WorkspaceBrand RowSizeProvider value="md"
517
523
  // + AVATAR_SIZE.inline.md spec-coupled 共識 sync。
518
524
  //
519
- // Numerical equivalence to v13 `loose-4` 公式(verified):
520
- // md density: (48-24)/2 = 12 = loose-4 = 12 ✓ identical
521
- // lg density: (64-24)/2 = 20 = loose-4 = 20 identical
522
- // v14 upgrade SSOT chain robustness,0 視覺改變。
525
+ // Collapsed pl 幾何值(rail-centered,--sidebar-width-icon = 2*loose + menu-icon):
526
+ // md density: (48-24)/2 = 12 · lg density: (68-24)/2 = 22
527
+ // (lg width = 2×24+20 = 68,非 stale 64;avatar-size = 24 both densities)
528
+ // 註:已撤回的 v13 `loose-4` 近似在 lg 20,與新 rail-幾何精確值 22 不同 —
529
+ // v14 以 SSOT chain 公式取代該近似,md 兩者恰好都是 12。
523
530
  "group-data-[collapsible=icon]:!pl-[calc((var(--sidebar-width-icon)-var(--chrome-header-avatar-size))/2)]",
524
531
  "group-data-[collapsible=icon]:!pr-0",
525
532
  "transition-[padding] duration-200 ease-linear motion-reduce:duration-0",
@@ -602,8 +609,9 @@ SidebarContent.displayName = "SidebarContent"
602
609
  // ── Group ──────────────────────────────────────────────────────────────────
603
610
 
604
611
  // SidebarGroupContext——讓 `SidebarGroupLabel` 和 `SidebarGroupContent` 知道當前
605
- // 所在的 group 是否 collapsible,以自動切換渲染模式(label trigger,content
606
- // `Collapsible.Content`)。沒有 context 就是舊行為(非 collapsible 的 plain div)。
612
+ // 所在的 group 是否 collapsible,以自動切換渲染模式(label 尾端 chevron button
613
+ // `Collapsible.Trigger`、content `Collapsible.Content`;label 本身維持
614
+ // `role="presentation"` div,不升格為 button)。沒有 context 就是舊行為(plain div)。
607
615
  type SidebarGroupContextValue = {
608
616
  collapsible: boolean
609
617
  }
@@ -617,13 +625,14 @@ function useSidebarGroup() {
617
625
  * SidebarGroup
618
626
  *
619
627
  * 預設是非互動的 plain group(div)。當 `collapsible` = true 時自動切換成 Radix
620
- * Collapsible:SidebarGroupLabel trigger、SidebarGroupContent Content、
621
- * 自動渲染 chevron label 尾端、chevron 依 open state 旋轉。
628
+ * Collapsible:SidebarGroupLabel 尾端自動渲染 chevron button 作為 `Collapsible.Trigger`
629
+ * (承載 aria-expanded / data-state,依 open state 旋轉),SidebarGroupContent 變 Content;
630
+ * label 本身維持 `role="presentation"` div,不升格為 button。
622
631
  *
623
632
  * ── API 設計決策 ──
624
633
  * 為什麼是 group 層級的 prop 而不是 label 層級?因為「group 是否可收合」是結構層
625
- * 的決定,影響 group 所有子 primitive 的渲染模式(label 變 button、content 變
626
- * animated container)。把 prop 放在 label 上會讓 content 不知道自己該不該被包,
634
+ * 的決定,影響 group primitive 的渲染模式(label 尾端 chevron trigger button、
635
+ * content 變 animated container)。把 prop 放在 label 上會讓 content 不知道自己該不該被包,
627
636
  * 形成跨元件的 prop drilling。放在 group 上用 context 傳遞是 React 的標準做法。
628
637
  */
629
638
  const SidebarGroup = React.forwardRef<
@@ -667,8 +676,10 @@ const SidebarGroup = React.forwardRef<
667
676
  open={open}
668
677
  onOpenChange={onOpenChange}
669
678
  className={baseClass}
670
- // Collapsible icon 模式下整個隱藏( TreeView 一樣——icon rail 沒空間放展開的 tree)
671
- // Consumer 若要在 icon 模式顯示整個 group,自行傳 className 覆寫
679
+ // 註:此 Collapsible.Root 本身**不**自帶 icon 模式隱藏 class(baseClass
680
+ // `group-data-[collapsible=icon]:hidden`),icon 模式下預設仍渲染。若要在 icon rail
681
+ // 隱藏整個可收合 group(icon rail 沒空間放展開的 tree),consumer 自行傳
682
+ // `className="group-data-[collapsible=icon]:hidden"` 覆寫。
672
683
  {...(props as React.ComponentProps<typeof CollapsiblePrimitive.Root>)}
673
684
  >
674
685
  {children}
@@ -872,9 +883,10 @@ SidebarMenuItem.displayName = "SidebarMenuItem"
872
883
  * - hover: bg-neutral-hover
873
884
  * - selected: bg-neutral-selected(data-active=true)
874
885
  *
875
- * Icon 模式:button 保持 w-full 填滿 sidebar icon rail,鎖高為 field-height-{size},
876
- * content justify-center 居中,label span sr-only 視覺隱藏(a11y tree 保留,
877
- * 2026-07-05 D4——SR 仍讀得到 accessible name)。
886
+ * Icon 模式:button 保持 w-full 填滿 sidebar icon rail,**無 height lock / 無 justify-center**
887
+ * (2026-05-21 v4 C* 撤回 `!h-[...]`——row height 永遠 stable,collapsed outer clip);
888
+ * prefix(icon / avatar)靠 `!pl-[calc((rail - prefix-width)/2)]` 公式置中對齊 rail,
889
+ * label span 以 sr-only 視覺隱藏(a11y tree 保留,2026-07-05 D4——SR 仍讀得到 accessible name)。
878
890
  */
879
891
  const sidebarMenuButtonVariants = cva(
880
892
  [
@@ -1,5 +1,5 @@
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
- // @story-baseline: packages/design-system/src/components/DataTable/data-table.stories.tsx#NumberAlignment
2
+ // @story-baseline: packages/design-system/src/components/DataTable/data-table.anatomy.stories.tsx#AlignmentRule
3
3
  import type { Meta, StoryObj } from '@storybook/react'
4
4
  import type { ColumnDef } from '@tanstack/react-table'
5
5
  import { Skeleton } from './skeleton'
@@ -21,16 +21,9 @@ const meta: Meta = {
21
21
  export default meta
22
22
  type Story = StoryObj
23
23
 
24
- export const Default: Story = {
25
- name: '預設',
26
- render: () => (
27
- <div className="flex flex-col gap-2 max-w-md">
28
- <Skeleton className="h-4 w-full" />
29
- <Skeleton className="h-4 w-full" />
30
- <Skeleton className="h-4 w-3/4" />
31
- </div>
32
- ),
33
- }
24
+ // Default retired 2026-07-14 per audit Dim 24(story 重複性):「預設」的三行文字骨架
25
+ // pattern 與 DocumentLoading 段落骨架完全同構,且抽象「預設」違「範例必真實業務場景」;
26
+ // UserProfileCard / TaskListLoading / DocumentLoading 等真實情境 story 覆蓋。
34
27
 
35
28
  export const UserProfileCard: Story = {
36
29
  name: '個人資料卡載入',
@@ -39,7 +39,7 @@ export const Overview: Story = {
39
39
  {[
40
40
  ['value / defaultValue', 'number[]', '—', '[single] 或 [min, max] range mode'],
41
41
  ['onValueChange', '(value: number[]) => void', '—', '拖曳中每次值變動'],
42
- ['onValueCommit', '(value: number[]) => void', '—', '放開滑鼠才觸發(防抖用)'],
42
+ ['onValueCommit', '(value: number[]) => void', '—', '互動結束觸發:滑鼠放開,或鍵盤 Arrow/Page/Home/End 每次有效變更(防抖用)'],
43
43
  ['min / max', 'number', '0 / 100', '值範圍'],
44
44
  ['step', 'number', '1', '步進'],
45
45
  ['size', "'sm' | 'md' | 'lg'", "'md'", '**只影響容器外高**,不影響 track/thumb 尺寸'],
@@ -175,8 +175,8 @@ export const ColorBindingRule: Story = {
175
175
  render: () => (
176
176
  <div className="flex flex-col gap-8">
177
177
  <div>
178
- <H3>Range 色和 Thumb border 色永遠同 token</H3>
179
- <Desc>Thumb 坐落在 range 的端點上,border 是 range 的視覺延續。兩者同色時看起來像「range 包住 thumb」——thumb 白底是「被 range 圍住的空心洞」,而不是「thumb 浮在 range 上面的異物」。</Desc>
178
+ <H3>Range 色和 Thumb border 色在 rest / disabled 兩態同 token</H3>
179
+ <Desc>Thumb 坐落在 range 的端點上,border 是 range 的視覺延續。兩者同色時看起來像「range 包住 thumb」——thumb 白底是「被 range 圍住的空心洞」,而不是「thumb 浮在 range 上面的異物」。<strong>互動態(hover / active / focus)只動 thumb border 色階</strong>(hover / focus 升 primary-hover、active 深至 primary-active),Range 填色恆 primary — 下表只列 rest / disabled 兩態的綁定。</Desc>
180
180
  <div className="overflow-x-auto">
181
181
  <table className="text-caption border-collapse">
182
182
  <thead><tr><Th>State</Th><Th>Range bg</Th><Th>Thumb border</Th><Th>共享 token</Th></tr></thead>
@@ -148,8 +148,8 @@ export const PositionAsStateRule: Story = {
148
148
  </Rule>
149
149
 
150
150
  <Rule
151
- title="對照 Switch:必須用 opacity(顏色是唯一 state 載體)"
152
- note="Switch 的 on/off 視覺差異唯一靠顏色(track bg-primary vs bg-border)——形狀完全一樣。若灰階 swap,disabled 的 ON 和 OFF 會看起來一模一樣,使用者無法分辨。必須 opacity 保留顏色"
151
+ title="對照 Switch:必須用 opacity(顏色是主要 state 載體)"
152
+ note="Switch 的 on/off 主要靠顏色(track bg-primary vs bg-border)承載——thumb 位移與 checked 勾號只是輔助訊號,單獨一顆、無左右對照時不易讀。若灰階 swap,disabled 的 ON 和 OFF track 同灰、主通道消失,使用者難以分辨。必須 opacity 保留顏色"
153
153
  >
154
154
  <div className="flex items-center gap-4">
155
155
  <div className="flex items-center gap-2">
@@ -123,7 +123,7 @@ Slider 不是 button——它是「當前位置指示器」,底色不該動(動
123
123
  value={number[]} // controlled
124
124
  defaultValue={number[]} // uncontrolled
125
125
  onValueChange={(value) => void}
126
- onValueCommit={(value) => void} // 放開滑鼠時才觸發
126
+ onValueCommit={(value) => void} // 互動結束觸發:滑鼠放開,或鍵盤 Arrow/Page/Home/End 每次有效變更
127
127
  min={number} // 預設 0
128
128
  max={number} // 預設 100
129
129
  step={number} // 預設 1
@@ -186,18 +186,18 @@ Radix Slider 原生支援多 thumb——只要 `value` / `defaultValue` 傳長
186
186
 
187
187
  **Slider 用灰階不用 opacity**。這是系統內重要的判準,反覆確認後的結論——系統有兩個 disabled 處理派別,Slider 屬於**灰階派**(跟 Button / Checkbox 同家族)。判準比「多色彩 vs 單色彩」更精準,是:
188
188
 
189
- ### 判準:**顏色是否是 semantic state 的唯一載體?**
189
+ ### 判準:**顏色是否是 semantic state 的主載體?**
190
190
 
191
191
  | 元件 | 顏色的角色 | State 載體 | Disabled 策略 |
192
192
  |---|---|---|---|
193
- | **Switch** | `bg-primary` vs `bg-border` 是 on/off 的**唯一視覺差異**(track thumb 形狀在 on/off 之間完全相同,只有顏色變)| **顏色本身就是 state** | **`opacity-disabled`** — 必須保留色彩身分,否則灰階後失去狀態區辨 |
193
+ | **Switch** | `bg-primary` vs `bg-border` 是 on/off 的**最強視覺差異**(thumb 位移與 check icon 只是弱輔助線索,灰階低對比下難辨——見 switch.spec.md「Disabled 用 opacity」)| **顏色是 state 主載體** | **`opacity-disabled`** — 必須保留色彩身分,否則灰階後狀態區辨大幅下降 |
194
194
  | **Checkbox** | 勾了有 checkmark,沒勾沒 checkmark——**形狀**決定 state,顏色只是美學 | **形狀(checkmark)** | **灰階 swap**(`bg-disabled`)— 灰色框裡的 checkmark 仍清楚可辨 |
195
195
  | **Button** | Primary 色是品牌美學,不是 state | — | **灰階 swap** |
196
196
  | **Slider** | Range 藍色是「已選長度」的美學視覺,**位置 + 長度是 state**,顏色只是裝飾 | **位置(thumb)+ 長度(range 佔比)** | **灰階 swap** — 灰色 range 跟灰色 thumb 的位置/長度仍然完全可辨 |
197
197
 
198
198
  ### 論證摘要
199
199
 
200
- Disabled slider 要傳達的兩件事——thumb 位置、range 長度——**完全不依賴顏色**(灰階後 x 座標與填滿佔比一模一樣),失去藍色零資訊損失,故走灰階派(同 Checkbox:state 由形狀 / 位置承載、顏色是裝飾)。Switch 是唯一 opacity 特例:on/off 無任何形狀差異、顏色本身就是 state,灰階會讓兩態無法區分——此 rationale 不適用 Slider。
200
+ Disabled slider 要傳達的兩件事——thumb 位置、range 長度——**完全不依賴顏色**(灰階後 x 座標與填滿佔比一模一樣),失去藍色零資訊損失,故走灰階派(同 Checkbox:state 由形狀 / 位置承載、顏色是裝飾)。Switch 是唯一 opacity 特例:on/off 的主載體是顏色(thumb 位移 / check icon 只是弱輔助線索),灰階會讓兩態近乎無法區分——此 rationale 不適用 Slider。
201
201
 
202
202
  ### 為什麼 shadcn / Material 3 用 opacity 仍然不是理由
203
203
 
@@ -236,7 +236,7 @@ Slider 無獨立 `readOnly` prop;但在 `<Field mode="readonly">` 內(2026-06-12
236
236
 
237
237
  - 「Slider 可以當計數器」——錯。Slider 傳達 position(在哪裡),計數(多少)用 NumberInput ±step(見「何時不用」)
238
238
  - 「兩個選項可以用 Range mode」——錯。Range 是連續值區間(value = [start, end]),二元 / 布林選擇用 Switch / SegmentedControl
239
- - 「focus 要加 ring」——本元件 documented 例外:focus 用 border 加深( hover 視覺),不加 ring / halo(見「視覺規格」表)
239
+ - 「focus 要加 ring」——本元件 documented 例外:focus 用 border 升 hover 階(`primary-hover`,淺一階 lift;同 hover 視覺),不加 ring / halo(見「視覺規格」表,對齊上方「為什麼 hover / active 用陰影不用色變」)
240
240
 
241
241
  ---
242
242
 
@@ -107,7 +107,9 @@ const Slider = React.forwardRef<
107
107
  Range 是「填充視覺」,Thumb border 是「thumb 的輪廓線」——視覺上 thumb
108
108
  的 border 剛好是 range 的延續(thumb 坐落在 range 的端點上,border 跟
109
109
  range 在色彩上融為一體,看起來像「range 包住 thumb」而不是「thumb 浮在
110
- range 上」)。兩個 token 綁在一起,不論什麼 state 都一致。
110
+ range 上」)。兩個 token 綁在一起 —— **同色綁定 scope = rest 與 disabled 兩態**;
111
+ 互動態(hover/active/focus)只動 thumb border 色階,range 填色恆 primary
112
+ (詳下方 Thumb 註解 2026-07-06 retract)。
111
113
  */}
112
114
  <SliderPrimitive.Range
113
115
  className={cn(
@@ -39,7 +39,7 @@ export const Overview: Story = {
39
39
  <tr><Td>completed</Td><Td><span className="inline-flex items-center gap-1.5"><Swatch value="--info" size="sm" /><span>filled info + white check icon</span></span></Td><Td><TokenCell token="--fg-secondary" display="fg-secondary" /></Td><Td><TokenCell token="--info" display="bg-info" /></Td></tr>
40
40
  <tr><Td>current(= value)</Td><Td><span className="inline-flex items-center gap-1.5"><Swatch value="--info" size="sm" /><span>filled info + white number + ring</span></span></Td><Td><TokenCell token="--foreground" display="foreground" /></Td><Td><TokenCell token="--border" display="bg-border" /></Td></tr>
41
41
  <tr><Td>upcoming</Td><Td><span className="inline-flex items-center gap-1.5"><Swatch value="--muted" size="sm" /><span>filled muted + fg-disabled number</span></span></Td><Td><TokenCell token="--fg-secondary" display="fg-secondary" /></Td><Td><TokenCell token="--border" display="bg-border" /></Td></tr>
42
- <tr><Td>error</Td><Td><span className="inline-flex items-center gap-1.5"><Swatch value="--error" size="sm" /><span>bg-error + white icon</span></span></Td><Td><TokenCell token="--error-text" display="error-text" /></Td><Td>—</Td></tr>
42
+ <tr><Td>error</Td><Td><span className="inline-flex items-center gap-1.5"><Swatch value="--error" size="sm" /><span>bg-error + white icon</span></span></Td><Td><TokenCell token="--error-text" display="error-text" /></Td><Td><TokenCell token="--border" display="bg-border" /></Td></tr>
43
43
  </tbody>
44
44
  </table>
45
45
  </div>
@@ -185,7 +185,7 @@ export const ColorMatrix: Story = {
185
185
  <Td mono>completed</Td>
186
186
  <Td><TokenCell token="--info" /></Td>
187
187
  <Td>—(同 bg)</Td>
188
- <Td><TokenCell token="white" display="白色 Check icon" /></Td>
188
+ <Td><TokenCell token="--on-emphasis" display="白色 Check icon" /></Td>
189
189
  <Td><TokenCell token="--fg-secondary" /></Td>
190
190
  <Td><TokenCell token="--info" display="bg-info" /></Td>
191
191
  </tr>
@@ -209,9 +209,9 @@ export const ColorMatrix: Story = {
209
209
  <Td mono>error</Td>
210
210
  <Td><TokenCell token="--error" /></Td>
211
211
  <Td>—(同 bg)</Td>
212
- <Td><TokenCell token="white" display="白色 X icon" /></Td>
212
+ <Td><TokenCell token="--on-emphasis" display="白色 X icon" /></Td>
213
213
  <Td><TokenCell token="--error-text" /></Td>
214
- <Td>—</Td>
214
+ <Td><TokenCell token="--border" display="bg-border" /></Td>
215
215
  </tr>
216
216
  </tbody>
217
217
  </table>
@@ -263,7 +263,7 @@ export const LinearBackClickDoesNotMutate: Story = {
263
263
  return (
264
264
  <Section
265
265
  title="點 completed step 只改 focus(value),不會自動取消完成狀態"
266
- subtitle="Steps 是純 controlled 元件,從不偷偷 mutate parent state。若應用層需要「使用者改錯了就 block 後續」,應由應用層自己從 completedValues 移除,Steps 內部絕不 auto-remove。"
266
+ subtitle="Steps 的進度狀態由 parent own(value 為 controlled / uncontrolled defaultValue 雙模),從不偷偷 mutate parent 的 completedValues。若應用層需要「使用者改錯了就 block 後續」,應由應用層自己從 completedValues 移除,Steps 內部絕不 auto-remove。"
267
267
  >
268
268
  <div className="flex flex-col gap-3 w-[360px]">
269
269
  <p className="text-caption text-fg-secondary">
@@ -284,7 +284,7 @@ disabled > error > focused > default
284
284
 
285
285
  ### 為什麼 error label 要跟 indicator 同色
286
286
 
287
- Steps 的 step 本身是「狀態載體」,跟 Field 的「label 只是欄位名,error 靠 help text 表達」不同。Steps 的 label 在視覺上屬於 indicator 的延伸資訊,兩者應該一起講故事:紅 ✕ indicator + 紅 label + 紅 description(如有)形成一致的 error visual language,讓使用者一眼看出這步出了什麼錯。
287
+ Steps 的 step 本身是「狀態載體」,跟 Field 的「label 只是欄位名,error 靠 help text 表達」不同。Steps 的 label 在視覺上屬於 indicator 的延伸資訊,兩者應該一起講故事:紅 ✕ indicator + 紅 label 形成一致的 error visual language,讓使用者一眼看出這步出了什麼錯(description 不變色,見下段)。
288
288
 
289
289
  這跟 `CLAUDE.md` 的「Label 永遠 foreground」原則不衝突——那是 Field 家族的規則(edit / readonly / disabled 三態的欄位容器)。Steps 是**進度指示器**不是**輸入容器**,label 色彩跟著 step state 走是正確做法。
290
290
 
@@ -38,14 +38,14 @@ export const Overview: Story = {
38
38
  <thead><tr><Th>Prop</Th><Th>Type</Th><Th>Default</Th><Th>說明</Th></tr></thead>
39
39
  <tbody>
40
40
  {[
41
- ['mode', "'edit' | 'display' | 'readonly' | 'disabled'", "'edit'", 'Field mode(默認 inherit Field context 或 edit);display 渲純展示 ✓/—,語意由 context 提供'],
41
+ ['mode', "'edit' | 'display' | 'readonly' | 'disabled'", "'edit'", 'Field mode(默認 inherit Field context 或 edit);display 渲純展示 勾/叉 icon,語意由 context 提供'],
42
42
  ['size', "'sm' | 'md' | 'lg'", "'md'", '對齊 field size tier(sm 跟 md 視覺相同)'],
43
43
  ['checked / defaultChecked', 'boolean', '—', 'ON/OFF 狀態(受控 / 非受控)'],
44
44
  ['onCheckedChange', '(checked: boolean) => void', '—', '切換 callback'],
45
45
  ['label', 'ReactNode', '—', 'inline label(Field context 內會被忽略)'],
46
46
  ['description', 'ReactNode', '—', 'inline description(與 label 搭配)'],
47
47
  ['disabled', 'boolean', 'false', '停用(opacity-disabled 保留顏色,見 spec)'],
48
- ['readOnly', 'boolean', 'false', 'standalone:鎖定互動視覺正常;Field 內:灰框 + ✓/—(2026-06-12 拍板)'],
48
+ ['readOnly', 'boolean', 'false', 'standalone:鎖定互動視覺正常;Field 內:灰框 + 勾/叉 icon(2026-06-12 拍板)'],
49
49
  ].map(([p, t, d, desc]) => (
50
50
  <tr key={p}><Td mono>{p}</Td><Td mono>{t}</Td><Td mono>{d}</Td><Td>{desc}</Td></tr>
51
51
  ))}
@@ -78,7 +78,7 @@ export const Inspector: InspectorStory = {
78
78
  mode: {
79
79
  control: 'radio',
80
80
  options: ['edit', 'display', 'readonly', 'disabled'],
81
- description: 'Field mode;display 渲純展示 ✓/—(語意由 context 提供)',
81
+ description: 'Field mode;display 渲純展示 勾/叉 icon(語意由 context 提供)',
82
82
  },
83
83
  size: {
84
84
  control: 'radio',
@@ -87,7 +87,7 @@ export const Inspector: InspectorStory = {
87
87
  },
88
88
  defaultChecked: { control: 'boolean', description: '預設 ON / OFF' },
89
89
  disabled: { control: 'boolean', description: '停用(opacity-disabled 保留顏色)' },
90
- readOnly: { control: 'boolean', description: '唯讀(standalone 視覺正常鎖互動;Field 內灰框 + ✓/—)' },
90
+ readOnly: { control: 'boolean', description: '唯讀(standalone 視覺正常鎖互動;Field 內灰框 + 勾/叉 icon)' },
91
91
  label: { control: 'text', description: 'inline label(留空 → 只渲染 switch 本體)' },
92
92
  description: { control: 'text', description: 'inline description(需搭配 label)' },
93
93
  },
@@ -135,7 +135,7 @@ export const StateBehavior: Story = {
135
135
  <div className="flex flex-col gap-8">
136
136
  <div>
137
137
  <H3>視覺狀態對照</H3>
138
- <Desc>disabled 用 opacity-disabled 保留顏色(Switch 特例,見下「Disabled 策略」)。readonly:standalone 視覺正常鎖互動;Field 內灰框 + ✓/—。</Desc>
138
+ <Desc>disabled 用 opacity-disabled 保留顏色(Switch 特例,見下「Disabled 策略」)。readonly:standalone 視覺正常鎖互動;Field 內灰框 + 勾/叉 icon。</Desc>
139
139
  <div className="overflow-x-auto mb-4">
140
140
  <table className="text-caption border-collapse">
141
141
  <thead><tr><Th>State</Th><Th>Track</Th><Th>Thumb</Th><Th>Check icon</Th></tr></thead>
@@ -178,8 +178,8 @@ export const StateBehavior: Story = {
178
178
 
179
179
  <div>
180
180
  <H3>Disabled 策略:opacity 而非灰階 swap</H3>
181
- <Desc>Switch 的 on/off 視覺差異**唯一載體是顏色**(track bg-primary vs bg-border)——track thumb on/off 之間形狀完全相同。若用灰階 swap(把 primary 換成 border),disabled 的 ON 和 OFF 會看起來一模一樣,使用者無法分辨當前狀態。必須保留顏色。</Desc>
182
- <p className="text-footnote text-fg-muted">對照 Checkbox / Slider 用灰階弱化(它們的形狀或位置本身就承載開 / 關狀態,不靠顏色),所以灰掉也看得出狀態;Switch 只靠顏色,只能降透明度保留顏色。</p>
181
+ <Desc>Switch 的 on/off 視覺差異**最強載體是顏色**(track bg-primary vs bg-border)——thumb 位移與 check icon 只是弱輔助線索。若用灰階 swap(把 primary 換成 border),disabled 的 ON 和 OFF 只剩灰階低對比下的位置與小 icon 差異,難以一眼分辨當前狀態。必須保留顏色。</Desc>
182
+ <p className="text-footnote text-fg-muted">對照 Checkbox / Slider 用灰階弱化(它們的形狀或位置本身就承載開 / 關狀態,不靠顏色),所以灰掉也看得出狀態;Switch 的主載體是顏色,只能降透明度保留顏色。</p>
183
183
  </div>
184
184
  </div>
185
185
  ),
@@ -116,7 +116,7 @@ export const UsageGuidance: Story = {
116
116
 
117
117
  <Rule
118
118
  title="使用場景對照"
119
- note="Readonly(standalone settings list):值重要、視覺不能弱化。表單 readonly = Field 內灰框 + ✓/—(2026-06-12 拍板);DataTable cell 非編輯態 = mode display。Disabled:外部條件造成暫時不可用(方案限制、權限不足)——傳達「現在用不到」"
119
+ note="Readonly(standalone settings list):值重要、視覺不能弱化。表單 readonly = Field 內灰框 + 勾/叉 icon(2026-06-12 拍板);DataTable cell 非編輯態 = mode display。Disabled:外部條件造成暫時不可用(方案限制、權限不足)——傳達「現在用不到」"
120
120
  >
121
121
  <Label>兩者都不可切換、不在 tab order 內(standalone),機制不同:readonly 用 `pointer-events-none`(視覺正常);disabled 走 native `disabled` + `cursor-not-allowed`(降透明度)。Field 內 readonly 灰框則可聚焦(tabIndex=0)</Label>
122
122
  </Rule>
@@ -132,7 +132,7 @@ export const DisabledOpacityRule: Story = {
132
132
  <div>
133
133
  <Rule
134
134
  title="Switch disabled 用 opacity,不用灰階 swap"
135
- note="Switch 的 on/off 視覺差異**唯一載體是顏色**(track bg-primary vs bg-border)——track thumb on/off 之間形狀完全相同,只有顏色變。若灰階 swap(primary → border),disabled 的 ON OFF 會看起來一模一樣,使用者無法分辨當前狀態"
135
+ note="Switch 的 on/off 最強視覺載體是顏色(track bg-primary vs bg-border);thumb 位移與 checked check icon 是輔助線索。若灰階 swap(primary → border),disabled 的 ON/OFF 只剩灰階低對比下的位置與小 icon 差異,難以一眼分辨當前狀態"
136
136
  >
137
137
  <div className="grid grid-cols-2 gap-4">
138
138
  <div className="border border-dashed border-divider rounded-md p-3">
@@ -164,9 +164,9 @@ export const DisabledOpacityRule: Story = {
164
164
 
165
165
  <Rule
166
166
  title="❌ 若 Switch 改用灰階 swap"
167
- note="disabled ON 和 disabled OFF 都會變成「灰 track + 灰 thumb」,視覺一模一樣——使用者不知道這個被停用的開關當前是開還是關"
167
+ note="disabled ON 和 disabled OFF 都會退成「灰 track + 灰 thumb」——殘餘線索只剩 thumb 位置與 thumb 內小 check icon,在灰階低對比下辨識度大幅下降,使用者難以一眼判斷被停用的開關當前是開還是關"
168
168
  >
169
- <Label warn>設計上禁止——Switch 是系統內唯一靠顏色承載 state 的元件,必須保留顏色</Label>
169
+ <Label warn>設計上禁止——Switch on/off 最強視覺載體是顏色(thumb 位置 / check icon 只是輔助線索),disabled 必須保留顏色</Label>
170
170
  </Rule>
171
171
  </div>
172
172
  ),
@@ -104,22 +104,22 @@ sm 和 md 視覺相同(純粹命名 mapping,讓消費者可直接傳同一
104
104
  | ON | `bg-primary` | 白色 + 2px primary border | primary check |
105
105
  | Disabled | 套 `opacity-disabled`(整體透明度降級) | 同 ON/OFF | 同 ON/OFF |
106
106
  | Readonly(standalone)| 視覺同一般態 | 但 `pointer-events-none` + click guard + `aria-readonly` | — |
107
- | Readonly(Field 內,2026-06-12 user 拍板)| 不渲染 toggle — 改渲染 `fieldWrapperStyles` readonly 灰框(= Input readonly 同源)+ ✓/—(display 同款值語言) | role="switch" + aria-checked + aria-readonly + 可 focus | — |
107
+ | Readonly(Field 內,2026-06-12 user 拍板)| 不渲染 toggle — 改渲染 `fieldWrapperStyles` readonly 灰框(= Input readonly 同源)+ 勾/叉 icon(display 同款值語言) | role="switch" + aria-checked + aria-readonly + 可 focus | — |
108
108
 
109
109
  ### Disabled 用 `opacity`
110
110
 
111
111
  **不用灰階 swap**——這是 Switch 的**特例**,跟 Checkbox / Button / Slider 的灰階策略不同。
112
112
 
113
- **理由**:Switch 的 on/off 視覺差異**唯一載體是顏色**(track `bg-primary` vs `bg-border`)——track thumb on/off 之間形狀完全相同,只有顏色變。若用灰階 swap(把 primary 換成 border),disabled 的 ON 跟 OFF 會合併成同一視覺態,使用者無法分辨當前狀態(螢幕閱讀器只能靠 `aria-checked` 區分)。
113
+ **理由**:Switch 的 on/off 視覺差異**最強載體是顏色**(track `bg-primary` vs `bg-border`)——thumb 位置(translate-x)與 thumb check icon 雖有差異,但屬弱輔助線索。若用灰階 swap(把 primary 換成 border),disabled 的 ON 跟 OFF 只剩灰階下低對比的位置/小 icon 差異,辨識度大幅下降,使用者難以一眼分辨當前狀態。
114
114
 
115
115
  **對照**:
116
116
  - Checkbox disabled 可以灰階 swap——checkmark 形狀承載 state,顏色只是裝飾
117
117
  - Slider disabled 可以灰階 swap——thumb 位置和 range 長度承載 state
118
- - Switch disabled **必須保留顏色**——沒有形狀差異,灰階後 state 失傳
118
+ - Switch disabled **必須保留顏色**——主載體是顏色,灰階後殘餘線索太弱,state 近乎失傳
119
119
 
120
120
  詳細對照見 `../Slider/slider.spec.md`「Disabled 策略」節。
121
121
 
122
- ### Readonly vs Disabled(standalone;Field 內 readonly = 灰框 + ✓/—,見上方狀態表)
122
+ ### Readonly vs Disabled(standalone;Field 內 readonly = 灰框 + 勾/叉 icon,見上方狀態表)
123
123
 
124
124
  | | Readonly(standalone) | Disabled |
125
125
  |---|---|---|
@@ -129,14 +129,14 @@ sm 和 md 視覺相同(純粹命名 mapping,讓消費者可直接傳同一
129
129
  | Tab 焦點 | 不在 tab order | 不在 tab order |
130
130
  | 用途 | settings list 鎖定呈現 | 外部條件造成不可操作 |
131
131
 
132
- 表單 readonly 呈現 = Field 內灰框 + ✓/—(2026-06-12 拍板);DataTable cell 非編輯態 = `mode="display"`(✓/—),皆非本表 standalone readonly。
132
+ 表單 readonly 呈現 = Field 內灰框 + 勾/叉 icon(2026-06-12 拍板);DataTable cell 非編輯態 = `mode="display"`(勾/叉 icon),皆非本表 standalone readonly。
133
133
 
134
134
  ### `mode` prop(Field mode,正交於 size)
135
135
 
136
136
  `mode?: 'edit' | 'display' | 'readonly' | 'disabled'`(默認 inherit Field context 或 `'edit'`),對齊 `field-types.ts` FieldMode:
137
137
  - `edit`(預設)— 可切換的 Switch。
138
- - `display` — **純展示**:渲染 / —(非互動 Switch),語意由 context(如 DataTable boolean cell 的行/列 header)提供,對齊 Carbon read-only / Input·Select·Textarea display mode 一致。
139
- - `readonly` — **Field 內** = 灰框 + ✓/—(消費 `fieldWrapperStyles` readonly,與 Input readonly 同一視覺語言;世界級:Salesforce output ✓ glyph / SAP 靜態文字 — readonly 下 boolean 是資料 value 非控件);**standalone** = 正常色鎖互動(settings list 場景)。
138
+ - `display` — **純展示**:渲染 勾/叉 icon(非互動 Switch;true=Check / false=X,中性 foreground 色,SSOT = `../SelectionControl/boolean-value.tsx`),語意由 context(如 DataTable boolean cell 的行/列 header)提供,對齊 Carbon read-only / Input·Select·Textarea display mode 一致。<!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
139
+ - `readonly` — **Field 內** = 灰框 + 勾/叉 icon(消費 `fieldWrapperStyles` readonly,與 Input readonly 同一視覺語言;世界級:Salesforce output ✓ glyph / SAP 靜態文字 — readonly 下 boolean 是資料 value 非控件);**standalone** = 正常色鎖互動(settings list 場景)。
140
140
  - `disabled` — 同上表「Readonly vs Disabled」(opacity 弱化);`mode='disabled'` 直傳與 `disabled` prop 等效(effectiveDisabled,2026-06-12)。
141
141
 
142
142
  ---
@@ -194,7 +194,7 @@ Switch 可透過 `label` / `description` props 內部直接渲染緊鄰文字:
194
194
 
195
195
  - ❌ 用 Switch 做 form 內的同意 / 勾選——「我同意條款」是書面成立行為,用 Checkbox
196
196
  - ❌ 把 Switch 當三態使用(例:「on / off / auto」)——用 RadioGroup / SegmentedControl
197
- - ❌ 對 disabled 狀態用灰階 swap 而非 opacity——會讓 on/off 視覺無法區分(見「Disabled 用 opacity」段)
197
+ - ❌ 對 disabled 狀態用灰階 swap 而非 opacity——會讓 on/off 視覺近乎無法區分(見「Disabled 用 opacity」段)
198
198
  - ❌ Switch 的 on/off 顯示「正在處理中」進度——用 Button + loading state
199
199
  - ❌ 直接改動 track 顏色承載額外語意(例如 error 時改紅)——Switch 是單純布林,錯誤訊息放外部 help text
200
200
 
@@ -1,7 +1,6 @@
1
1
  // @story-trait-rationale: hasSizes 由 anatomy.stories.tsx SizeMatrix / 狀態(States)story 的 md/lg 對照 owns size showcase;Default scenario 由 四模式(Modes) / 狀態(States) / 搭配標籤(WithLabel)等真實業務情境 story 覆蓋,不另開抽象 Default/AllSizes。
2
2
  import type { Meta, StoryObj } from '@storybook/react'
3
3
  import { Switch } from './switch'
4
- import { SelectionItem } from '@/design-system/components/SelectionControl/selection-item'
5
4
 
6
5
  const meta: Meta<typeof Switch> = {
7
6
  title: 'Design System/Components/Switch/展示',
@@ -23,7 +22,7 @@ export const Modes: Story = {
23
22
  <div>
24
23
  <h3 className="text-body font-bold text-foreground mb-2">display</h3>
25
24
  <Switch mode="display" checked />
26
- <p className="text-caption text-fg-muted mt-1">純視覺 / —;語意由 context(如 DataTable 表頭 + 行標籤)提供,display 不暴露獨立 aria-label。需螢幕報讀器可讀請用 edit / readonly。</p>
25
+ <p className="text-caption text-fg-muted mt-1">純視覺 勾/叉 icon;語意由 context(如 DataTable 表頭 + 行標籤)提供,display 不暴露獨立 aria-label。需螢幕報讀器可讀請用 edit / readonly。</p>
27
26
  </div>
28
27
  <div>
29
28
  <h3 className="text-body font-bold text-foreground mb-2">readonly</h3>
@@ -70,7 +69,10 @@ export const States: Story = {
70
69
  ),
71
70
  }
72
71
 
73
- /* ── 搭配 SelectionItem ── */
72
+ /* ── 搭配標籤 ── */
73
+ // 2026-07-14 dim-68 修:原直用 internal <SelectionItem control={...}>(selection-item.spec.md
74
+ // 明定僅 Checkbox / RadioGroup 消費路徑;Switch 不在其中)→ 改公開 `<Switch label description>`
75
+ // (switch.spec.md「label / description 整合」canonical:label 左 / switch 右,自動 wire htmlFor)。
74
76
  export const WithLabel: Story = {
75
77
  name: '搭配標籤',
76
78
  render: () => (
@@ -78,27 +80,10 @@ export const WithLabel: Story = {
78
80
  {(['sm', 'md', 'lg'] as const).map(size => (
79
81
  <div key={size}>
80
82
  <p className="text-caption text-fg-muted mb-1">size="{size}"</p>
81
- <div className="grid">
82
- <SelectionItem
83
- size={size}
84
- control={<Switch id={`sw-${size}-a`} size={size} defaultChecked />}
85
- label="啟用通知"
86
- description="接收電子郵件和推播通知"
87
- htmlFor={`sw-${size}-a`}
88
- />
89
- <SelectionItem
90
- size={size}
91
- control={<Switch id={`sw-${size}-b`} size={size} />}
92
- label="自動更新"
93
- htmlFor={`sw-${size}-b`}
94
- />
95
- <SelectionItem
96
- size={size}
97
- control={<Switch id={`sw-${size}-c`} size={size} disabled />}
98
- label="維護模式(管理員限定)"
99
- htmlFor={`sw-${size}-c`}
100
- disabled
101
- />
83
+ <div className="grid gap-2">
84
+ <Switch size={size} defaultChecked label="啟用通知" description="接收電子郵件和推播通知" />
85
+ <Switch size={size} label="自動更新" />
86
+ <Switch size={size} disabled label="維護模式(管理員限定)" />
102
87
  </div>
103
88
  </div>
104
89
  ))}
@@ -7,6 +7,7 @@ import { cn } from '@/lib/utils'
7
7
  import type { FieldMode, FieldVariant } from '@/design-system/components/Field/field-types'
8
8
  import { useFieldContext, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldSize } from '@/design-system/components/Field/field-context'
9
9
  import { fieldWrapperStyles } from '@/design-system/components/Field/field-wrapper'
10
+ import { BooleanValueIcon } from '@/design-system/components/SelectionControl/boolean-value'
10
11
 
11
12
  /**
12
13
  * Switch — 開關控件
@@ -24,7 +25,7 @@ import { fieldWrapperStyles } from '@/design-system/components/Field/field-wrapp
24
25
  * ON: track primary, thumb 白色 + 2px primary border + primary check icon
25
26
  * disabled: opacity-disabled(整體透明度)
26
27
  * readOnly(standalone): 視覺同一般態,但 pointer-events-none + aria-readonly
27
- * readOnly(Field 內): 渲染 readonly 灰框 + ✓/—(2026-06-12 拍板,= Input readonly 同視覺語言)
28
+ * readOnly(Field 內): 渲染 readonly 灰框 + 勾/叉 icon(2026-06-12 拍板,= Input readonly 同視覺語言)
28
29
  *
29
30
  * ── label / description / readOnly ──
30
31
  * Switch 可以透過 `label` 和 `description` props 在元件內直接渲染緊鄰的文字,
@@ -45,7 +46,7 @@ import { fieldWrapperStyles } from '@/design-system/components/Field/field-wrapp
45
46
  * readOnly 模式:
46
47
  * <Switch readOnly checked={true} label="..." />
47
48
  * standalone:視覺維持 ON/OFF 正確狀態,但無法互動、不在 tab order 內、寫入 aria-readonly。
48
- * Field 內:渲染 readonly 灰框 + ✓/—(不渲染 toggle;見 forwardRef 內 readonly 分支)。
49
+ * Field 內:渲染 readonly 灰框 + 勾/叉 icon(不渲染 toggle;見 forwardRef 內 readonly 分支)。
49
50
  * 與 disabled 的差異:readonly 不降色(可讀),disabled 降色(弱化)。
50
51
  */
51
52
 
@@ -110,18 +111,18 @@ export interface SwitchProps
110
111
  */
111
112
  description?: React.ReactNode
112
113
  /**
113
- * readonly 模式:standalone = 鎖定互動但維持 ON/OFF 視覺;Field 內 = 灰框 + ✓/—。
114
+ * readonly 模式:standalone = 鎖定互動但維持 ON/OFF 視覺;Field 內 = 灰框 + 勾/叉 icon。
114
115
  * 與 disabled 的差異:readonly 不降色(可讀),disabled 降色(弱化)。
115
- * DataTable cell 非編輯態用 mode="display"(✓/—),非 readOnly。
116
+ * DataTable cell 非編輯態用 mode="display"(勾/叉 icon),非 readOnly。
116
117
  */
117
118
  readOnly?: boolean
118
119
  /**
119
120
  * Field mode(2026-05-05 Phase B3 align):
120
121
  * edit — 一般可互動 Switch(預設)
121
- * display — **純展示**:渲染 / —(無互動 primitive、無 input chrome);
122
+ * display — **純展示**:渲染 勾/叉 icon(無互動 primitive、無 input chrome);
122
123
  * 對齊 Carbon read-only / DataTable boolean cell。
123
124
  * `display` 完全無 toggle 形體;`readonly` 視場景(field-types.ts)。
124
- * readonly — standalone 同 readOnly prop(保留視覺鎖互動);Field 內 = 灰框 + ✓/—
125
+ * readonly — standalone 同 readOnly prop(保留視覺鎖互動);Field 內 = 灰框 + 勾/叉 icon
125
126
  * disabled — 同 disabled prop
126
127
  */
127
128
  mode?: FieldMode
@@ -200,17 +201,20 @@ const Switch = React.forwardRef<
200
201
  } = props
201
202
 
202
203
  // ── mode='display'(下移至所有 hooks 之後,per #35 Rules of Hooks)──────────
203
- // 純展示模式:無互動 toggle、渲染 / —。與 Checkbox display 對齊(同 boolean primitive)。
204
+ // 純展示模式:無互動 toggle、渲染 Check / X icon。與 Checkbox display 對齊(同 boolean primitive,
205
+ // 共用 SelectionControl/boolean-value.tsx SSOT — 勾/叉 icon + 中性 foreground 色)。
204
206
  if (resolvedMode === 'display') {
205
207
  const isChecked = checkedProp === true
206
- return isChecked
207
- ? <span {...restDomProps} ref={ref as React.Ref<HTMLSpanElement>} className="text-foreground">✓</span>
208
- : <span {...restDomProps} ref={ref as React.Ref<HTMLSpanElement>} className="text-fg-muted">—</span>
208
+ return (
209
+ <span {...restDomProps} ref={ref as React.Ref<HTMLSpanElement>} className="inline-flex">
210
+ <BooleanValueIcon checked={isChecked} size={resolvedBoxSize} />
211
+ </span>
212
+ )
209
213
  }
210
214
 
211
- // ── mode='readonly' in Field(2026-06-12 user 拍板「灰框 + ✓/—」,與 Checkbox 同款)──
215
+ // ── mode='readonly' in Field(2026-06-12 拍板「灰框 + 勾/叉」,與 Checkbox 同款;2026-07-09 glyph → Check/X icon)──
212
216
  // Field 內 readonly boolean = fieldWrapperStyles readonly 灰框(= Input readonly 同源)
213
- // + ✓/— 值語言;standalone readOnly(settings list)維持原樣鎖互動。詳 checkbox.tsx 同段註解。
217
+ // + 勾/叉 icon 值語言;standalone readOnly(settings list)維持原樣鎖互動。詳 checkbox.tsx 同段註解。
214
218
  if (effectiveReadOnly && insideField) {
215
219
  const isChecked = (checkedProp ?? defaultCheckedProp) === true
216
220
  const boxSize = resolvedBoxSize
@@ -231,7 +235,7 @@ const Switch = React.forwardRef<
231
235
  className,
232
236
  )}
233
237
  >
234
- {isChecked ? <span className="text-foreground">✓</span> : <span className="text-fg-muted">—</span>}
238
+ <BooleanValueIcon checked={isChecked} size={boxSize} />
235
239
  </div>
236
240
  )
237
241
  }