@qijenchen/design-system 0.1.0-beta.61 → 0.1.0-beta.63

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 (507) hide show
  1. package/CLAUDE.md +6 -6
  2. package/dist/components/Accordion/accordion.d.ts +1 -1
  3. package/dist/components/Accordion/accordion.js +1 -1
  4. package/dist/components/Accordion/accordion.js.map +1 -1
  5. package/dist/components/Alert/alert.d.ts +2 -2
  6. package/dist/components/Alert/alert.js +4 -3
  7. package/dist/components/Alert/alert.js.map +1 -1
  8. package/dist/components/AppShell/app-shell.d.ts.map +1 -1
  9. package/dist/components/AppShell/app-shell.js +4 -3
  10. package/dist/components/AppShell/app-shell.js.map +1 -1
  11. package/dist/components/Avatar/avatar.d.ts +1 -1
  12. package/dist/components/Avatar/avatar.js +2 -1
  13. package/dist/components/Avatar/avatar.js.map +1 -1
  14. package/dist/components/Badge/badge.d.ts +1 -1
  15. package/dist/components/Badge/badge.js +2 -1
  16. package/dist/components/Badge/badge.js.map +1 -1
  17. package/dist/components/Breadcrumb/breadcrumb.d.ts +1 -1
  18. package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
  19. package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
  20. package/dist/components/Button/button.d.ts +5 -5
  21. package/dist/components/Button/button.d.ts.map +1 -1
  22. package/dist/components/Button/button.js +4 -3
  23. package/dist/components/Button/button.js.map +1 -1
  24. package/dist/components/Calendar/calendar.d.ts +1 -1
  25. package/dist/components/Calendar/calendar.d.ts.map +1 -1
  26. package/dist/components/Calendar/calendar.js +80 -69
  27. package/dist/components/Calendar/calendar.js.map +1 -1
  28. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  29. package/dist/components/Carousel/carousel.js +5 -3
  30. package/dist/components/Carousel/carousel.js.map +1 -1
  31. package/dist/components/Checkbox/checkbox-group.d.ts +2 -1
  32. package/dist/components/Checkbox/checkbox-group.d.ts.map +1 -1
  33. package/dist/components/Checkbox/checkbox-group.js.map +1 -1
  34. package/dist/components/Checkbox/checkbox.d.ts +9 -3
  35. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  36. package/dist/components/Checkbox/checkbox.js +8 -3
  37. package/dist/components/Checkbox/checkbox.js.map +1 -1
  38. package/dist/components/Chip/chip.d.ts +2 -2
  39. package/dist/components/Chip/chip.js +3 -2
  40. package/dist/components/Chip/chip.js.map +1 -1
  41. package/dist/components/CircularProgress/circular-progress.d.ts +1 -1
  42. package/dist/components/CircularProgress/circular-progress.js +2 -1
  43. package/dist/components/CircularProgress/circular-progress.js.map +1 -1
  44. package/dist/components/Coachmark/coachmark.d.ts +4 -4
  45. package/dist/components/Coachmark/coachmark.js +2 -1
  46. package/dist/components/Coachmark/coachmark.js.map +1 -1
  47. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  48. package/dist/components/Combobox/combobox.js +17 -13
  49. package/dist/components/Combobox/combobox.js.map +1 -1
  50. package/dist/components/Command/command.d.ts +4 -0
  51. package/dist/components/Command/command.d.ts.map +1 -1
  52. package/dist/components/Command/command.js.map +1 -1
  53. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  54. package/dist/components/DataTable/data-table.js.map +1 -1
  55. package/dist/components/DateGrid/date-grid.d.ts +8 -4
  56. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  57. package/dist/components/DateGrid/date-grid.js +2 -2
  58. package/dist/components/DateGrid/date-grid.js.map +1 -1
  59. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  60. package/dist/components/DatePicker/date-picker.js +6 -4
  61. package/dist/components/DatePicker/date-picker.js.map +1 -1
  62. package/dist/components/DescriptionList/description-list.js +1 -1
  63. package/dist/components/DescriptionList/description-list.js.map +1 -1
  64. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  65. package/dist/components/Dialog/dialog.js.map +1 -1
  66. package/dist/components/DropdownMenu/dropdown-menu.d.ts +1 -1
  67. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  68. package/dist/components/DropdownMenu/dropdown-menu.js +0 -1
  69. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  70. package/dist/components/Empty/empty.d.ts +1 -1
  71. package/dist/components/Empty/empty.js.map +1 -1
  72. package/dist/components/Field/field-wrapper.js +1 -1
  73. package/dist/components/Field/field-wrapper.js.map +1 -1
  74. package/dist/components/Field/field.d.ts.map +1 -1
  75. package/dist/components/Field/field.js +1 -0
  76. package/dist/components/Field/field.js.map +1 -1
  77. package/dist/components/FieldControlGroup/field-control-group.d.ts.map +1 -1
  78. package/dist/components/FieldControlGroup/field-control-group.js +1 -1
  79. package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
  80. package/dist/components/FileUpload/file-upload.d.ts +8 -1
  81. package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
  82. package/dist/components/FileUpload/file-upload.js +7 -3
  83. package/dist/components/FileUpload/file-upload.js.map +1 -1
  84. package/dist/components/FileViewer/file-viewer.d.ts +1 -1
  85. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  86. package/dist/components/FileViewer/file-viewer.js +25 -14
  87. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  88. package/dist/components/HoverCard/hover-card.d.ts +5 -1
  89. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  90. package/dist/components/HoverCard/hover-card.js +2 -1
  91. package/dist/components/HoverCard/hover-card.js.map +1 -1
  92. package/dist/components/Menu/menu-item.d.ts +6 -2
  93. package/dist/components/Menu/menu-item.d.ts.map +1 -1
  94. package/dist/components/Menu/menu-item.js.map +1 -1
  95. package/dist/components/Notice/notice.d.ts +5 -1
  96. package/dist/components/Notice/notice.d.ts.map +1 -1
  97. package/dist/components/Notice/notice.js +2 -1
  98. package/dist/components/Notice/notice.js.map +1 -1
  99. package/dist/components/OverflowIndicator/overflow-indicator.d.ts +4 -0
  100. package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
  101. package/dist/components/OverflowIndicator/overflow-indicator.js +2 -2
  102. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  103. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  104. package/dist/components/PeoplePicker/people-picker.js +6 -2
  105. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  106. package/dist/components/Popover/popover.d.ts +1 -1
  107. package/dist/components/Popover/popover.js +2 -1
  108. package/dist/components/Popover/popover.js.map +1 -1
  109. package/dist/components/ProfileCard/profile-card.d.ts +9 -4
  110. package/dist/components/ProfileCard/profile-card.d.ts.map +1 -1
  111. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  112. package/dist/components/RadioGroup/radio-group.d.ts +6 -0
  113. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  114. package/dist/components/RadioGroup/radio-group.js +4 -0
  115. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  116. package/dist/components/ScrollArea/scroll-area.js +1 -1
  117. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  118. package/dist/components/Select/select.d.ts.map +1 -1
  119. package/dist/components/Select/select.js +14 -4
  120. package/dist/components/Select/select.js.map +1 -1
  121. package/dist/components/SelectMenu/select-menu.d.ts +4 -0
  122. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  123. package/dist/components/SelectMenu/select-menu.js +6 -3
  124. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  125. package/dist/components/SelectionControl/selection-item.d.ts +5 -1
  126. package/dist/components/SelectionControl/selection-item.d.ts.map +1 -1
  127. package/dist/components/SelectionControl/selection-item.js +3 -1
  128. package/dist/components/SelectionControl/selection-item.js.map +1 -1
  129. package/dist/components/Sheet/sheet.d.ts +6 -5
  130. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  131. package/dist/components/Sheet/sheet.js.map +1 -1
  132. package/dist/components/Sidebar/sidebar.d.ts +1 -1
  133. package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
  134. package/dist/components/Sidebar/sidebar.js +3 -2
  135. package/dist/components/Sidebar/sidebar.js.map +1 -1
  136. package/dist/components/Skeleton/skeleton.d.ts +1 -1
  137. package/dist/components/Skeleton/skeleton.js +2 -1
  138. package/dist/components/Skeleton/skeleton.js.map +1 -1
  139. package/dist/components/Slider/slider.d.ts +1 -1
  140. package/dist/components/Slider/slider.d.ts.map +1 -1
  141. package/dist/components/Slider/slider.js +7 -3
  142. package/dist/components/Slider/slider.js.map +1 -1
  143. package/dist/components/Steps/steps.d.ts +1 -1
  144. package/dist/components/Steps/steps.d.ts.map +1 -1
  145. package/dist/components/Steps/steps.js +9 -6
  146. package/dist/components/Steps/steps.js.map +1 -1
  147. package/dist/components/Switch/switch.d.ts +5 -5
  148. package/dist/components/Switch/switch.js +3 -3
  149. package/dist/components/Switch/switch.js.map +1 -1
  150. package/dist/components/Tabs/tabs.js +1 -1
  151. package/dist/components/Tabs/tabs.js.map +1 -1
  152. package/dist/components/Textarea/textarea.js +1 -1
  153. package/dist/components/Textarea/textarea.js.map +1 -1
  154. package/dist/components/TimePicker/time-columns.js +1 -1
  155. package/dist/components/TimePicker/time-columns.js.map +1 -1
  156. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  157. package/dist/components/TimePicker/time-picker.js +2 -1
  158. package/dist/components/TimePicker/time-picker.js.map +1 -1
  159. package/dist/components/Toast/toast.js.map +1 -1
  160. package/dist/components/Tooltip/tooltip.d.ts +1 -1
  161. package/dist/components/Tooltip/tooltip.js +2 -1
  162. package/dist/components/Tooltip/tooltip.js.map +1 -1
  163. package/dist/components/TreeView/tree-view.d.ts +4 -4
  164. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  165. package/dist/components/TreeView/tree-view.js +3 -3
  166. package/dist/components/TreeView/tree-view.js.map +1 -1
  167. package/dist/patterns/element-anatomy/item-anatomy.d.ts +3 -3
  168. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  169. package/dist/patterns/header-canonical/chrome-header.d.ts +3 -2
  170. package/dist/patterns/header-canonical/chrome-header.d.ts.map +1 -1
  171. package/dist/patterns/header-canonical/chrome-header.js.map +1 -1
  172. package/dist/patterns/overlay-surface/overlay-surface.d.ts +3 -2
  173. package/dist/patterns/overlay-surface/overlay-surface.d.ts.map +1 -1
  174. package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
  175. package/ds-canonical/hooks/_log-fire.sh +3 -1
  176. package/ds-canonical/hooks/check_audit_post_report_validator.sh +21 -1
  177. package/ds-canonical/hooks/check_audit_sample_escape.sh +6 -3
  178. package/ds-canonical/hooks/check_chrome_header_avatar_canonical.sh +8 -0
  179. package/ds-canonical/hooks/check_consumer_app_invariants.sh +321 -0
  180. package/ds-canonical/hooks/check_datatable_invariants.sh +75 -12
  181. package/ds-canonical/hooks/check_escape_marker_abuse.sh +15 -1
  182. package/ds-canonical/hooks/check_field_family_invariants.sh +12 -2
  183. package/ds-canonical/hooks/check_plugin_fork_health.sh +137 -0
  184. package/ds-canonical/hooks/check_propose_discipline.sh +147 -0
  185. package/ds-canonical/hooks/check_solo_workflow.sh +11 -1
  186. package/ds-canonical/hooks/check_story_invariants.sh +63 -9
  187. package/ds-canonical/hooks/check_storybook_addon_packaging.sh +151 -0
  188. package/ds-canonical/hooks/check_tailwind_wildcard_in_docs.sh +8 -2
  189. package/ds-canonical/hooks/lib/_overlay_handcraft.sh +25 -4
  190. package/ds-canonical/hooks/lib/_token_hygiene.sh +9 -1
  191. package/ds-canonical/hooks/session_start_governance_check.sh +7 -2
  192. package/ds-canonical/hooks/stop_self_audit.sh +2 -2
  193. package/ds-canonical/hooks/tests/test_check_addon_subdir_ship.sh +3 -2
  194. package/ds-canonical/hooks/tests/test_check_consumer_app_invariants.sh +12 -0
  195. package/ds-canonical/hooks/tests/test_check_consumer_app_story_title.sh +3 -2
  196. package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +6 -4
  197. package/ds-canonical/hooks/tests/test_check_consumer_no_ds_catalog.sh +7 -4
  198. package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +6 -4
  199. package/ds-canonical/hooks/tests/test_check_data_table_size_num_to_meta_width.sh +3 -2
  200. package/ds-canonical/hooks/tests/test_check_fork_user_plugin_install.sh +3 -2
  201. package/ds-canonical/hooks/tests/test_check_plugin_fork_health.sh +9 -0
  202. package/ds-canonical/hooks/tests/test_check_propose_cite_required.sh +3 -2
  203. package/ds-canonical/hooks/tests/test_check_propose_discipline.sh +10 -0
  204. package/ds-canonical/hooks/tests/test_check_propose_plain_chinese.sh +3 -2
  205. package/ds-canonical/hooks/tests/test_check_storybook_addon_packaging.sh +10 -0
  206. package/ds-canonical/hooks/tests/test_check_storybook_addon_preset_cjs.sh +3 -2
  207. package/ds-canonical/references/build-ui-canonicals.md +2 -2
  208. package/ds-canonical/references/composition-fidelity.md +3 -3
  209. package/ds-canonical/references/naming-conventions.md +1 -0
  210. package/ds-canonical/references/ssot-consultation.md +1 -1
  211. package/ds-canonical/references/ssot-index.md +7 -7
  212. package/ds-canonical/rules/meta-patterns.md +5 -5
  213. package/ds-canonical/rules/self-verify.md +1 -1
  214. package/ds-canonical/rules/story-rules.md +2 -0
  215. package/ds-canonical/rules/ui-development.md +1 -1
  216. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +11 -2
  217. package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +1 -1
  218. package/ds-canonical/skills/design-system-audit/SKILL.md +15 -15
  219. package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +4 -3
  220. package/ds-story-manifest.json +15 -21
  221. package/llms-full.txt +9 -5
  222. package/llms.txt +2 -2
  223. package/package.json +1 -1
  224. package/src/components/Accordion/accordion.principles.stories.tsx +1 -1
  225. package/src/components/Accordion/accordion.spec.md +22 -5
  226. package/src/components/Accordion/accordion.tsx +1 -1
  227. package/src/components/Alert/alert.anatomy.stories.tsx +4 -4
  228. package/src/components/Alert/alert.principles.stories.tsx +5 -5
  229. package/src/components/Alert/alert.spec.md +16 -11
  230. package/src/components/Alert/alert.stories.tsx +5 -5
  231. package/src/components/Alert/alert.tsx +2 -2
  232. package/src/components/AppShell/app-shell.principles.stories.tsx +68 -21
  233. package/src/components/AppShell/app-shell.spec.md +17 -24
  234. package/src/components/AppShell/app-shell.tsx +4 -3
  235. package/src/components/AspectRatio/aspect-ratio.anatomy.stories.tsx +3 -3
  236. package/src/components/AspectRatio/aspect-ratio.spec.md +15 -1
  237. package/src/components/Avatar/avatar.anatomy.stories.tsx +1 -1
  238. package/src/components/Avatar/avatar.principles.stories.tsx +8 -8
  239. package/src/components/Avatar/avatar.spec.md +25 -54
  240. package/src/components/Avatar/avatar.tsx +2 -2
  241. package/src/components/Badge/badge.principles.stories.tsx +2 -2
  242. package/src/components/Badge/badge.spec.md +22 -13
  243. package/src/components/Badge/badge.tsx +1 -1
  244. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +5 -2
  245. package/src/components/Breadcrumb/breadcrumb.spec.md +25 -25
  246. package/src/components/Breadcrumb/breadcrumb.stories.tsx +7 -32
  247. package/src/components/Breadcrumb/breadcrumb.tsx +1 -1
  248. package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +2 -2
  249. package/src/components/BulkActionBar/bulk-action-bar.spec.md +20 -6
  250. package/src/components/BulkActionBar/bulk-action-bar.tsx +2 -2
  251. package/src/components/Button/button.anatomy.stories.tsx +2 -2
  252. package/src/components/Button/button.spec.md +19 -17
  253. package/src/components/Button/button.stories.tsx +1 -1
  254. package/src/components/Button/button.tsx +10 -9
  255. package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
  256. package/src/components/Calendar/calendar.spec.md +37 -17
  257. package/src/components/Calendar/calendar.tsx +26 -22
  258. package/src/components/Carousel/carousel.anatomy.stories.tsx +6 -6
  259. package/src/components/Carousel/carousel.principles.stories.tsx +1 -1
  260. package/src/components/Carousel/carousel.spec.md +23 -3
  261. package/src/components/Carousel/carousel.tsx +19 -4
  262. package/src/components/Chart/chart.anatomy.stories.tsx +1 -1
  263. package/src/components/Chart/chart.spec.md +20 -1
  264. package/src/components/Checkbox/checkbox-group.tsx +2 -1
  265. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +4 -4
  266. package/src/components/Checkbox/checkbox.principles.stories.tsx +2 -2
  267. package/src/components/Checkbox/checkbox.spec.md +35 -15
  268. package/src/components/Checkbox/checkbox.stories.tsx +1 -1
  269. package/src/components/Checkbox/checkbox.tsx +14 -3
  270. package/src/components/Chip/chip.anatomy.stories.tsx +5 -5
  271. package/src/components/Chip/chip.principles.stories.tsx +9 -3
  272. package/src/components/Chip/chip.spec.md +8 -9
  273. package/src/components/Chip/chip.tsx +2 -2
  274. package/src/components/CircularProgress/circular-progress.anatomy.stories.tsx +6 -6
  275. package/src/components/CircularProgress/circular-progress.spec.md +10 -11
  276. package/src/components/CircularProgress/circular-progress.stories.tsx +36 -43
  277. package/src/components/CircularProgress/circular-progress.tsx +1 -1
  278. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +1 -1
  279. package/src/components/Coachmark/coachmark.principles.stories.tsx +14 -29
  280. package/src/components/Coachmark/coachmark.spec.md +21 -13
  281. package/src/components/Coachmark/coachmark.tsx +4 -4
  282. package/src/components/Combobox/combobox.anatomy.stories.tsx +28 -28
  283. package/src/components/Combobox/combobox.principles.stories.tsx +1 -1
  284. package/src/components/Combobox/combobox.spec.md +25 -7
  285. package/src/components/Combobox/combobox.tsx +12 -3
  286. package/src/components/Command/command.anatomy.stories.tsx +1 -1
  287. package/src/components/Command/command.principles.stories.tsx +8 -8
  288. package/src/components/Command/command.spec.md +18 -8
  289. package/src/components/Command/command.tsx +8 -3
  290. package/src/components/DataTable/data-table-sort-manager.tsx +1 -1
  291. package/src/components/DataTable/data-table.anatomy.stories.tsx +9 -7
  292. package/src/components/DataTable/data-table.principles.stories.tsx +3 -3
  293. package/src/components/DataTable/data-table.spec.md +15 -11
  294. package/src/components/DataTable/data-table.stories.tsx +58 -13
  295. package/src/components/DataTable/data-table.tsx +13 -0
  296. package/src/components/DataTable/filter-operators.spec.md +19 -27
  297. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +8 -8
  298. package/src/components/DateGrid/date-grid.principles.stories.tsx +7 -7
  299. package/src/components/DateGrid/date-grid.spec.md +17 -14
  300. package/src/components/DateGrid/date-grid.tsx +8 -4
  301. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +11 -15
  302. package/src/components/DatePicker/date-picker.principles.stories.tsx +8 -8
  303. package/src/components/DatePicker/date-picker.spec.md +40 -36
  304. package/src/components/DatePicker/date-picker.stories.tsx +23 -53
  305. package/src/components/DatePicker/date-picker.tsx +12 -6
  306. package/src/components/DescriptionList/description-list.anatomy.stories.tsx +2 -2
  307. package/src/components/DescriptionList/description-list.principles.stories.tsx +4 -18
  308. package/src/components/DescriptionList/description-list.spec.md +9 -3
  309. package/src/components/DescriptionList/description-list.stories.tsx +2 -18
  310. package/src/components/DescriptionList/description-list.tsx +1 -1
  311. package/src/components/Dialog/dialog.principles.stories.tsx +1 -1
  312. package/src/components/Dialog/dialog.spec.md +26 -11
  313. package/src/components/Dialog/dialog.stories.tsx +159 -184
  314. package/src/components/Dialog/dialog.tsx +2 -1
  315. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +2 -2
  316. package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +16 -17
  317. package/src/components/DropdownMenu/dropdown-menu.spec.md +10 -15
  318. package/src/components/DropdownMenu/dropdown-menu.stories.tsx +13 -15
  319. package/src/components/DropdownMenu/dropdown-menu.tsx +12 -2
  320. package/src/components/Empty/empty.spec.md +19 -18
  321. package/src/components/Empty/empty.tsx +1 -1
  322. package/src/components/Field/field-controls.spec.md +52 -41
  323. package/src/components/Field/field-wrapper.tsx +2 -2
  324. package/src/components/Field/field.anatomy.stories.tsx +6 -5
  325. package/src/components/Field/field.principles.stories.tsx +8 -7
  326. package/src/components/Field/field.spec.md +42 -20
  327. package/src/components/Field/field.stories.tsx +9 -7
  328. package/src/components/Field/field.tsx +6 -3
  329. package/src/components/Field/form-validation.spec.md +20 -19
  330. package/src/components/FieldControlGroup/field-control-group.anatomy.stories.tsx +16 -15
  331. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +5 -1
  332. package/src/components/FieldControlGroup/field-control-group.spec.md +17 -1
  333. package/src/components/FieldControlGroup/field-control-group.stories.tsx +9 -9
  334. package/src/components/FieldControlGroup/field-control-group.tsx +4 -3
  335. package/src/components/FileItem/file-item.anatomy.stories.tsx +7 -17
  336. package/src/components/FileItem/file-item.principles.stories.tsx +7 -7
  337. package/src/components/FileItem/file-item.spec.md +24 -24
  338. package/src/components/FileUpload/file-upload.anatomy.stories.tsx +4 -2
  339. package/src/components/FileUpload/file-upload.principles.stories.tsx +10 -12
  340. package/src/components/FileUpload/file-upload.spec.md +37 -8
  341. package/src/components/FileUpload/file-upload.tsx +5 -3
  342. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +16 -11
  343. package/src/components/FileViewer/file-viewer.principles.stories.tsx +4 -4
  344. package/src/components/FileViewer/file-viewer.spec.md +7 -19
  345. package/src/components/FileViewer/file-viewer.stories.tsx +3 -3
  346. package/src/components/FileViewer/file-viewer.tsx +42 -16
  347. package/src/components/HoverCard/hover-card.spec.md +20 -10
  348. package/src/components/HoverCard/hover-card.stories.tsx +1 -1
  349. package/src/components/HoverCard/hover-card.tsx +12 -1
  350. package/src/components/Input/input.anatomy.stories.tsx +4 -4
  351. package/src/components/Input/input.principles.stories.tsx +4 -31
  352. package/src/components/Input/input.spec.md +16 -4
  353. package/src/components/LinkInput/link-input.anatomy.stories.tsx +1 -1
  354. package/src/components/LinkInput/link-input.principles.stories.tsx +2 -2
  355. package/src/components/LinkInput/link-input.spec.md +14 -1
  356. package/src/components/Menu/menu-item.principles.stories.tsx +5 -5
  357. package/src/components/Menu/menu-item.spec.md +24 -12
  358. package/src/components/Menu/menu-item.tsx +7 -3
  359. package/src/components/Notice/notice.principles.stories.tsx +7 -7
  360. package/src/components/Notice/notice.spec.md +7 -5
  361. package/src/components/Notice/notice.tsx +5 -1
  362. package/src/components/NumberInput/number-input.anatomy.stories.tsx +4 -4
  363. package/src/components/NumberInput/number-input.principles.stories.tsx +13 -35
  364. package/src/components/NumberInput/number-input.spec.md +19 -0
  365. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +4 -3
  366. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +2 -5
  367. package/src/components/OverflowIndicator/overflow-indicator.spec.md +9 -1
  368. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -42
  369. package/src/components/OverflowIndicator/overflow-indicator.tsx +9 -3
  370. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +11 -2
  371. package/src/components/PeoplePicker/people-picker.spec.md +26 -12
  372. package/src/components/PeoplePicker/people-picker.stories.tsx +14 -15
  373. package/src/components/PeoplePicker/people-picker.tsx +12 -4
  374. package/src/components/Popover/popover.spec.md +20 -11
  375. package/src/components/Popover/popover.tsx +2 -2
  376. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
  377. package/src/components/ProfileCard/profile-card.principles.stories.tsx +29 -25
  378. package/src/components/ProfileCard/profile-card.spec.md +30 -10
  379. package/src/components/ProfileCard/profile-card.tsx +12 -7
  380. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +1 -1
  381. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +7 -5
  382. package/src/components/ProgressBar/progress-bar.spec.md +9 -1
  383. package/src/components/ProgressBar/progress-bar.stories.tsx +1 -1
  384. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +6 -3
  385. package/src/components/RadioGroup/radio-group.principles.stories.tsx +6 -13
  386. package/src/components/RadioGroup/radio-group.spec.md +7 -1
  387. package/src/components/RadioGroup/radio-group.tsx +10 -0
  388. package/src/components/Rating/rating.anatomy.stories.tsx +1 -0
  389. package/src/components/Rating/rating.principles.stories.tsx +9 -6
  390. package/src/components/Rating/rating.spec.md +13 -7
  391. package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +44 -24
  392. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +51 -57
  393. package/src/components/ScrollArea/scroll-area.spec.md +5 -7
  394. package/src/components/ScrollArea/scroll-area.stories.tsx +110 -60
  395. package/src/components/ScrollArea/scroll-area.tsx +1 -1
  396. package/src/components/SegmentedControl/segmented-control.anatomy.stories.tsx +1 -1
  397. package/src/components/SegmentedControl/segmented-control.spec.md +15 -14
  398. package/src/components/SegmentedControl/segmented-control.stories.tsx +1 -1
  399. package/src/components/Select/select.anatomy.stories.tsx +12 -11
  400. package/src/components/Select/select.principles.stories.tsx +3 -3
  401. package/src/components/Select/select.spec.md +34 -8
  402. package/src/components/Select/select.stories.tsx +2 -2
  403. package/src/components/Select/select.tsx +18 -3
  404. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +2 -2
  405. package/src/components/SelectMenu/select-menu.principles.stories.tsx +5 -5
  406. package/src/components/SelectMenu/select-menu.spec.md +19 -12
  407. package/src/components/SelectMenu/select-menu.stories.tsx +34 -27
  408. package/src/components/SelectMenu/select-menu.tsx +14 -3
  409. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
  410. package/src/components/SelectionControl/selection-item.spec.md +13 -2
  411. package/src/components/SelectionControl/selection-item.stories.tsx +1 -1
  412. package/src/components/SelectionControl/selection-item.tsx +8 -2
  413. package/src/components/Separator/separator.principles.stories.tsx +2 -2
  414. package/src/components/Separator/separator.spec.md +11 -5
  415. package/src/components/Sheet/sheet.anatomy.stories.tsx +7 -6
  416. package/src/components/Sheet/sheet.principles.stories.tsx +18 -38
  417. package/src/components/Sheet/sheet.spec.md +9 -11
  418. package/src/components/Sheet/sheet.stories.tsx +2 -2
  419. package/src/components/Sheet/sheet.tsx +6 -5
  420. package/src/components/Sidebar/sidebar.anatomy.stories.tsx +42 -33
  421. package/src/components/Sidebar/sidebar.principles.stories.tsx +4 -4
  422. package/src/components/Sidebar/sidebar.spec.md +22 -17
  423. package/src/components/Sidebar/sidebar.stories.tsx +16 -0
  424. package/src/components/Sidebar/sidebar.tsx +15 -7
  425. package/src/components/Skeleton/skeleton.anatomy.stories.tsx +2 -2
  426. package/src/components/Skeleton/skeleton.principles.stories.tsx +20 -7
  427. package/src/components/Skeleton/skeleton.spec.md +6 -4
  428. package/src/components/Skeleton/skeleton.stories.tsx +34 -26
  429. package/src/components/Skeleton/skeleton.tsx +1 -1
  430. package/src/components/Slider/slider.principles.stories.tsx +3 -3
  431. package/src/components/Slider/slider.spec.md +16 -12
  432. package/src/components/Slider/slider.stories.tsx +2 -18
  433. package/src/components/Slider/slider.tsx +6 -2
  434. package/src/components/Steps/steps.anatomy.stories.tsx +3 -3
  435. package/src/components/Steps/steps.principles.stories.tsx +9 -12
  436. package/src/components/Steps/steps.spec.md +31 -25
  437. package/src/components/Steps/steps.stories.tsx +5 -40
  438. package/src/components/Steps/steps.tsx +17 -12
  439. package/src/components/Switch/switch.principles.stories.tsx +1 -1
  440. package/src/components/Switch/switch.spec.md +13 -0
  441. package/src/components/Switch/switch.tsx +3 -3
  442. package/src/components/Tabs/tabs.anatomy.stories.tsx +4 -4
  443. package/src/components/Tabs/tabs.principles.stories.tsx +1 -1
  444. package/src/components/Tabs/tabs.spec.md +13 -6
  445. package/src/components/Tabs/tabs.stories.tsx +3 -3
  446. package/src/components/Tabs/tabs.tsx +2 -2
  447. package/src/components/Tag/tag.anatomy.stories.tsx +15 -15
  448. package/src/components/Tag/tag.principles.stories.tsx +1 -1
  449. package/src/components/Tag/tag.spec.md +25 -5
  450. package/src/components/Textarea/textarea.anatomy.stories.tsx +3 -3
  451. package/src/components/Textarea/textarea.principles.stories.tsx +7 -7
  452. package/src/components/Textarea/textarea.spec.md +15 -3
  453. package/src/components/Textarea/textarea.tsx +1 -1
  454. package/src/components/TimePicker/time-columns.tsx +1 -1
  455. package/src/components/TimePicker/time-picker.anatomy.stories.tsx +8 -2
  456. package/src/components/TimePicker/time-picker.spec.md +23 -3
  457. package/src/components/TimePicker/time-picker.tsx +5 -3
  458. package/src/components/Toast/toast.anatomy.stories.tsx +1 -1
  459. package/src/components/Toast/toast.spec.md +7 -4
  460. package/src/components/Toast/toast.stories.tsx +12 -6
  461. package/src/components/Toast/toast.tsx +1 -1
  462. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +1 -1
  463. package/src/components/Tooltip/tooltip.spec.md +11 -8
  464. package/src/components/Tooltip/tooltip.tsx +1 -1
  465. package/src/components/TreeView/tree-view.anatomy.stories.tsx +25 -9
  466. package/src/components/TreeView/tree-view.principles.stories.tsx +5 -5
  467. package/src/components/TreeView/tree-view.spec.md +11 -9
  468. package/src/components/TreeView/tree-view.stories.tsx +5 -3
  469. package/src/components/TreeView/tree-view.tsx +8 -5
  470. package/src/patterns/action-bar/action-bar.spec.md +11 -4
  471. package/src/patterns/element-anatomy/element-anatomy.spec.md +8 -6
  472. package/src/patterns/element-anatomy/inline-action.spec.md +18 -18
  473. package/src/patterns/element-anatomy/item-anatomy.spec.md +47 -42
  474. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +6 -6
  475. package/src/patterns/element-anatomy/item-anatomy.tsx +3 -3
  476. package/src/patterns/header-canonical/chrome-header.tsx +3 -2
  477. package/src/patterns/header-canonical/header-canonical.spec.md +24 -11
  478. package/src/patterns/header-canonical/header-canonical.stories.tsx +9 -9
  479. package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +22 -12
  480. package/src/patterns/overlay-surface/overlay-surface.spec.md +43 -39
  481. package/src/patterns/overlay-surface/overlay-surface.tsx +3 -2
  482. package/src/patterns/resize-handle/resize-handle.spec.md +10 -1
  483. package/src/patterns/resize-handle/resize-handle.stories.tsx +7 -7
  484. package/src/tokens/README.md +5 -2
  485. package/src/tokens/color/color.spec.md +17 -10
  486. package/src/tokens/color/semantic.css +2 -0
  487. package/src/tokens/density/density.spec.md +4 -14
  488. package/src/tokens/elevation/elevation.spec.md +7 -8
  489. package/src/tokens/layoutSpace/layoutSpace.spec.md +20 -27
  490. package/src/tokens/motion/motion.spec.md +15 -7
  491. package/src/tokens/opacity/opacity.spec.md +15 -26
  492. package/src/tokens/orphan-tokens.spec.md +14 -3
  493. package/src/tokens/radius/radius.spec.md +13 -15
  494. package/src/tokens/token-system.spec.md +10 -13
  495. package/src/tokens/typography/typography.spec.md +8 -12
  496. package/src/tokens/uiSize/uiSize.spec.md +28 -16
  497. /package/ds-canonical/hooks/{check_addon_subdir_ship.sh → retired/2026-06-11-prune-merge/check_addon_subdir_ship.sh} +0 -0
  498. /package/ds-canonical/hooks/{check_consumer_app_story_title.sh → retired/2026-06-11-prune-merge/check_consumer_app_story_title.sh} +0 -0
  499. /package/ds-canonical/hooks/{check_consumer_ds_primitive_misuse.sh → retired/2026-06-11-prune-merge/check_consumer_ds_primitive_misuse.sh} +0 -0
  500. /package/ds-canonical/hooks/{check_consumer_no_ds_catalog.sh → retired/2026-06-11-prune-merge/check_consumer_no_ds_catalog.sh} +0 -0
  501. /package/ds-canonical/hooks/{check_consumer_story_baseline.sh → retired/2026-06-11-prune-merge/check_consumer_story_baseline.sh} +0 -0
  502. /package/ds-canonical/hooks/{check_data_table_size_num_to_meta_width.sh → retired/2026-06-11-prune-merge/check_data_table_size_num_to_meta_width.sh} +0 -0
  503. /package/ds-canonical/hooks/{check_fork_user_plugin_install.sh → retired/2026-06-11-prune-merge/check_fork_user_plugin_install.sh} +0 -0
  504. /package/ds-canonical/hooks/{check_plugin_freshness.sh → retired/2026-06-11-prune-merge/check_plugin_freshness.sh} +0 -0
  505. /package/ds-canonical/hooks/{check_propose_cite_required.sh → retired/2026-06-11-prune-merge/check_propose_cite_required.sh} +0 -0
  506. /package/ds-canonical/hooks/{check_propose_plain_chinese.sh → retired/2026-06-11-prune-merge/check_propose_plain_chinese.sh} +0 -0
  507. /package/ds-canonical/hooks/{check_storybook_addon_preset_cjs.sh → retired/2026-06-11-prune-merge/check_storybook_addon_preset_cjs.sh} +0 -0
@@ -49,11 +49,10 @@ export const UsageGuidance: Story = {
49
49
  <div className="prose prose-sm max-w-prose mb-8">
50
50
  <p>適合 Field 的真實業務場景(點擊跳轉「展示」頁範例):</p>
51
51
  <ul className="space-y-1">
52
- <li><LinkTo kind="Design System/Components/Field/展示" name="Vertical"><span className="text-primary hover:underline font-medium cursor-pointer">Vertical</span></LinkTo></li>
53
- <li><LinkTo kind="Design System/Components/Field/展示" name="Horizontal"><span className="text-primary hover:underline font-medium cursor-pointer">Horizontal</span></LinkTo></li>
54
- <li><LinkTo kind="Design System/Components/Field/展示" name="水平 — 標籤 垂直對齊公式驗證"><span className="text-primary hover:underline font-medium cursor-pointer">水平 標籤 垂直對齊公式驗證</span></LinkTo></li>
55
- <li><LinkTo kind="Design System/Components/Field/展示" name="混合 控制元件 field 高度對齊"><span className="text-primary hover:underline font-medium cursor-pointer">混合 控制元件 的 field 高度對齊</span></LinkTo></li>
56
- <li><LinkTo kind="Design System/Components/Field/展示" name="SegmentedControl 作為 Field 控制元件"><span className="text-primary hover:underline font-medium cursor-pointer">SegmentedControl 作為 Field 控制元件</span></LinkTo></li>
52
+ <li><LinkTo kind="Design System/Components/Field/展示" name="垂直"><span className="text-primary hover:underline font-medium cursor-pointer">註冊 / 建立專案表單 — 引導式逐欄輸入(垂直佈局)</span></LinkTo></li>
53
+ <li><LinkTo kind="Design System/Components/Field/展示" name="水平"><span className="text-primary hover:underline font-medium cursor-pointer">帳號設定 / 偏好設定頁 — 修改已知屬性(水平佈局)</span></LinkTo></li>
54
+ <li><LinkTo kind="Design System/Components/Field/展示" name="混合控制元件的高度對齊"><span className="text-primary hover:underline font-medium cursor-pointer">同一表單混用 Input / Select / DatePicker 等控制元件</span></LinkTo></li>
55
+ <li><LinkTo kind="Design System/Components/Field/展示" name="SegmentedControl 作為 Field 控制元件"><span className="text-primary hover:underline font-medium cursor-pointer">少量互斥選項(計費週期、版面密度)用 SegmentedControl 當控制元件</span></LinkTo></li>
57
56
  </ul>
58
57
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方 vs 近親 段)。</p>
59
58
  </div>
@@ -116,7 +115,8 @@ export const UsageGuidance: Story = {
116
115
  note="「查看使用者資料」「訂單明細」這類純展示場景不該用 Field + readonly mode——DescriptionList 的 `dl / dt / dd` HTML 語義更適合唯讀屬性列表,a11y 也更清楚"
117
116
  >
118
117
  <div className="max-w-sm">
119
- <FieldGroup>
118
+ {/* horizontalLabelWidth:同表單 horizontal Field 必統一 label 寬(field.spec.md「FieldGroup horizontalLabelWidth cascade」)*/}
119
+ <FieldGroup horizontalLabelWidth="120px">
120
120
  <Field orientation="horizontal">
121
121
  <FieldLabel>Email</FieldLabel>
122
122
  <Input mode="readonly" defaultValue="user@example.com" />
@@ -197,7 +197,8 @@ export const OrientationRule: Story = {
197
197
  note="已知屬性的修改介面(account settings、使用者偏好)。label + control 並排節省垂直空間,讓多個設定一目了然。label 固定寬度對齊縱向軸"
198
198
  >
199
199
  <div className="max-w-xl">
200
- <FieldGroup>
200
+ {/* horizontalLabelWidth:同表單 label 等寬 → 控件起點對齊(2026-06-10 user 抓本 story 違反自家 canonical)*/}
201
+ <FieldGroup horizontalLabelWidth="120px">
201
202
  <Field orientation="horizontal" required>
202
203
  <FieldLabel>電子郵件</FieldLabel>
203
204
  <Input type="email" defaultValue="user@example.com" />
@@ -8,7 +8,7 @@ variants: {}
8
8
  sizes: {}
9
9
  ---
10
10
 
11
- <!-- @benchmark-cited: D5 retrofit 2026-05-18 — body claims marked per-claim @benchmark-unverified inline; canonical source URLs in frontmatter benchmark list. -->
11
+ <!-- @benchmark-cited: D5 retrofit 2026-05-18 — body claims marked per-claim @benchmark-unverified inline(M22(d) 顯式撤回;本檔 frontmatter benchmark list,來源 URL 未補)。 -->
12
12
 
13
13
  # Field 設計原則
14
14
 
@@ -23,7 +23,7 @@ Field 是**表單欄位的佈局容器**。只負責排版(label / control / d
23
23
 
24
24
  **實作基礎**:自建——本 DS 的 form layout 設計。shadcn 的 `Form` 元件走 react-hook-form + Zod + 自己的 Field primitive(含 Controller),本 DS 不採用這套耦合設計:Field 只做 layout + context,驗證由 consumer 自選(本 DS 建議 zod,見 `form-validation.spec.md`),保持更輕量、更獨立的定位。
25
25
 
26
- **Layout Family**:Field **不屬於** 4-Family Model 的 element layout families——它是 form composition pattern(包 Family 4 control + label + description)。見 CLAUDE.md「系統內部 Layout 4-Family Model」→「Field Composition」段落。
26
+ **Layout Family**:Field **不屬於** 4-Family Model 的 element layout families——它是 form composition pattern(包 Family 4 control + label + description)。見 CLAUDE.md「4-Family Layout Model」段 + `patterns/element-anatomy/element-anatomy.spec.md`「Field Composition(不在 family 但相關)」段。
27
27
 
28
28
  ---
29
29
 
@@ -32,7 +32,7 @@ Field 是**表單欄位的佈局容器**。只負責排版(label / control / d
32
32
  Field 和 Field Controls(Input / NumberInput / DatePicker / Select / Combobox / LinkInput / PeoplePicker / Textarea)是兩件事:
33
33
 
34
34
  - **Field**(本元件):只管佈局 + 狀態 context
35
- - **Field Controls / Checkbox / Switch / RadioGroup**:管自己的資料型別 + edit/readonly/disabled 三態 + 格式化(XxxDisplay DataTable 共用)
35
+ - **Field Controls / Checkbox / Switch / RadioGroup**:管自己的資料型別 + edit/readonly/disabled 三態 + 格式化(`mode="display"` 渲染供 DataTable cell 共用)
36
36
 
37
37
  這麼拆的理由:**Checkbox 在 table cell、form field、settings row 應該是同一個 primitive**,不該為了進到 form 就被包一層 CheckboxField;form 的高度對齊由 Field 的 control area 負責,不由 primitive 本身負責。Field Controls 的詳細共用規則見 `Field/field-controls.spec.md`。
38
38
 
@@ -77,7 +77,9 @@ Field 和 Field Controls(Input / NumberInput / DatePicker / Select / Combobox
77
77
  | Horizontal 模式 label 與 control 的 gap | `gap-x-3`(12px) |
78
78
  | Control area(任何 size) | `min-h-field-{size}` + `flex items-center` |
79
79
 
80
- **Label / Description / Error 字體固定 `text-body`(14px),不隨 field size 變。** Field size 只影響 input 高度,不影響表單佈局元素的 typography。世界級系統(Material、Ant Design、Atlassian、Carbon、Polaris)都是固定 label/helper text size <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
80
+ **Label / Description / Error 字體固定 `text-body`(14px),不隨 field size 變**——Field size 只影響 input 高度,不影響表單佈局元素的 typography。
81
+
82
+ **為什麼固定**:世界級系統(Material、Ant Design、Atlassian、Carbon、Polaris)都是固定 label/helper text size。 <!-- @benchmark-unverified -->
81
83
 
82
84
  ---
83
85
 
@@ -110,6 +112,8 @@ Label 在左、control + description + error 在右欄垂直堆疊。Label 與 c
110
112
 
111
113
  Horizontal 模式下 label 的欄寬由 `labelWidth` prop 控制(任何 CSS length 值),預設 `auto` 由 label 內容撐開。內部用 CSS variable `--field-label-width` 傳給 grid template column。
112
114
 
115
+ **硬規則(2026-06-10 codify,user 確認)**:同一表單的多個 horizontal Field **必經 `FieldGroup horizontalLabelWidth` 統一 label 寬**(單行 `labelWidth` 覆寫)——禁各自 `auto` 造成控件起點不對齊(錨例:field.principles 原違反自家 idiom,圖示控件參差)。機制詳下方「FieldGroup horizontalLabelWidth cascade」段。
116
+
113
117
  ---
114
118
 
115
119
  ## Horizontal 模式 label 垂直對齊
@@ -125,7 +129,7 @@ Horizontal 模式下 label 的欄寬由 `labelWidth` prop 控制(任何 CSS le
125
129
  - label 第一行永遠對齊第一個 item 第一行
126
130
  - 實作:`padding-top: calc((field-height - 1lh)/2)`
127
131
 
128
- **為什麼分兩套**:曾試 `min-h + flex-center` 統一 → block + 長 label regression(justify-center 失效 + 跟 SelectionItem 公式錯位)。Inline / block 不同對齊語意模型,必分兩套。純 CSS 跨 size/density/字體自動連動,無 JS 測量。對齊 Atlassian DSP / Salesforce Lightning(Polaris 用 baseline 修正,類似)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
132
+ **為什麼分兩套**:inline / block 是不同的對齊語意模型(固定高度可談整體中線 vs 多行群組只有第一行錨點),必分兩套。純 CSS 跨 size/density/字體自動連動,無 JS 測量。對齊 Atlassian DSP / Salesforce Lightning(Polaris 用 baseline 修正,類似)。(附註・技術陷阱:曾試 `min-h + flex-center` 統一,出 block + label regression——justify-center 失效 + SelectionItem 公式錯位,勿再嘗試) <!-- @benchmark-unverified -->
129
133
 
130
134
  ---
131
135
 
@@ -136,7 +140,7 @@ Field 的 control area 有兩種佈局模型,涵蓋所有 control 類型。**核
136
140
  | Layout | Control area 樣式 | 適用 control |
137
141
  |---|---|---|
138
142
  | **inline**(預設) | `min-h-field-{size}` + `flex items-center` | Input / NumberInput / DatePicker / Select / Combobox / LinkInput / Textarea(單行使用)、Checkbox / Switch / 單一 Button(如 upload picker) |
139
- | **block** | `flex flex-col items-start` + `padding-top: calc((field-height - 1lh) / 2)`,**不設 min-h** | RadioGroup / CheckboxGroup / FileDropzone / RichTextEditor / inline DataTable 等多行/任意高度區塊 |
143
+ | **block** | `flex flex-col items-start`,**不設 min-h、不加 padding-top**(第一行對齊由 block primitive 自帶 py 保證,見「如何宣告 block primitive」) | RadioGroup / CheckboxGroup / FileDropzone / RichTextEditor / inline DataTable 等多行/任意高度區塊 |
140
144
 
141
145
  ### 兩種模式的對齊幾何
142
146
 
@@ -157,11 +161,11 @@ Inline Block
157
161
  1. 第一個 Radio 往下掉,跟 label 第一行中線錯位
158
162
  2. FieldGroup 裡 inline / block field 並排時節奏斷掉
159
163
 
160
- 正確做法是「**第一行對齊**」——後續 item 從第一行往下流,label 公式錨在第一行中線。Atlassian DSP / Polaris / Material 都是這個模型。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
164
+ 正確做法是「**第一行對齊**」——後續 item 從第一行往下流,label 公式錨在第一行中線。Atlassian DSP / Polaris / Material 都是這個模型。 <!-- @benchmark-unverified -->
161
165
 
162
166
  ### 為什麼 primitive 不自己變高
163
167
 
164
- **Checkbox / Switch / RadioGroupItem 的 primitive 保持原生尺寸**(16-20px),不為了 form 而被拉高。世界級系統(shadcn、Radix、Material、Atlassian)全部這樣做。理由: <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
168
+ **Checkbox / Switch / RadioGroupItem 的 primitive 保持原生尺寸**(16-20px),不為了 form 而被拉高。世界級系統(shadcn、Radix、Material、Atlassian)全部這樣做。理由: <!-- @benchmark-unverified -->
165
169
 
166
170
  1. **Primitive 保持單一職責**——Checkbox 在 table cell、toolbar、menu 裡仍然是 16px,不受 form 高度污染
167
171
  2. **高度節奏由 Field 容器提供**——一次設定,所有 primitive 在任何 size / density 都自動對齊
@@ -243,7 +247,15 @@ Field 透過 Context 暴露以下狀態給子元件(Primitive 可以透過 `us
243
247
  - Disabled 時星號改為 `text-fg-disabled`(neutral-6),與 label 同步降色
244
248
  - 個別 FieldLabel 可用 `required` prop 覆寫 context 值
245
249
 
246
- **為什麼貼齊無 gap**:星號是 label 語意的一部分(WCAG 友善——screen reader 先讀 required 再讀 label),不是獨立視覺元素,所以不需要間距。
250
+ **為什麼貼齊無 gap**:星號是 label 語意的一部分,不是獨立視覺元素,所以不需要間距(星號本身 `aria-hidden`,SR required 資訊由控件 `aria-required` 承擔——見「A11y 預設」段)。
251
+
252
+ ## FieldLabel `info` icon(2026-06-10 文件化,對齊 field.tsx 既有實作)
253
+
254
+ label 文字後可帶 info icon(ℹ)hover 出 tooltip 補充說明:`<FieldLabel info="說明文字">`。
255
+
256
+ - **與 label 間距 `gap-1`(4px)**、InfoIcon **16px 固定**、色 `fg-muted` hover `fg-secondary`(field.tsx L388-408)
257
+ - **disabled 時整顆不渲染**(L400)— info 是 action affordance(hover 互動),非類型身份 indicator,non-editable 隱藏
258
+ - 設計定位:inline action pattern(補充工具,視覺退後)— label 的 primary interaction 是 input,info 是輔助
247
259
 
248
260
  ---
249
261
 
@@ -286,7 +298,7 @@ gap 三個語意層級(具體 gap class map 見 `field.tsx` FieldGroup,`compact`
286
298
 
287
299
  同一畫面 / 同一 FieldGroup 內多個 horizontal Field **必須共用 label 欄寬度**。若每個 Field 各自傳 `labelWidth`(或省略 → 內容撐開),label 寬度會參差不齊,Switch / Input 的左邊緣不對齊,視覺上每一行「歪七扭八」。
288
300
 
289
- **世界級 idiom**:macOS System Settings / iOS Settings / GitHub Settings / Notion preferences / Figma 偏好設定——setting list 的 label 全部固定寬、control 全部右對齊,列與列對齊成可掃描的欄位格網。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
301
+ **世界級 idiom**:macOS System Settings / iOS Settings / GitHub Settings / Notion preferences / Figma 偏好設定——setting list 的 label 全部固定寬、control 全部右對齊,列與列對齊成可掃描的欄位格網。 <!-- @benchmark-unverified -->
290
302
 
291
303
  `FieldGroup horizontalLabelWidth` 透過 React Context cascade 到所有子 Field(含 `vertical` 模式的 Field 不受影響——vertical 無 label 欄概念)。單一 Field 仍可用自己的 `labelWidth` prop 覆寫 group 預設(罕見 — 通常 group 預設就是 canonical)。
292
304
 
@@ -320,6 +332,13 @@ Button 的 height 與 `field-height` 共用同一組 token,放進 inline control
320
332
 
321
333
  ---
322
334
 
335
+ ## 邊界案例
336
+
337
+ - **空 Field(無 control children)**:code 不擋——control area 渲染成空容器。Field 是佈局容器,無 control 即無意義,屬 consumer 誤用
338
+ - **RTL**:未實作方向鏡像(horizontal label 欄固定在左、Switch `ml-auto` 推右皆以 LTR 設計);RTL 屬 DS-wide 決策,未定(與 Chip / Breadcrumb 同口徑)
339
+
340
+ ---
341
+
323
342
  ## 禁止事項
324
343
 
325
344
  - ❌ 不得在 Field 內再包 Field——Field 不支援巢狀
@@ -353,24 +372,21 @@ Field 內的資料輸入控件(Input / NumberInput / DatePicker / Select / Com
353
372
  | 唯讀資訊展示 | `DescriptionList` | Field 是表單容器,純展示用 `dl/dt/dd` 語義 |
354
373
  | 單一 Checkbox(勾選同意)without label 結構 | 直接 Checkbox + label prop | 單個 Checkbox 是 inline primitive,不需要 Field 佈局 |
355
374
  | DataTable cell 編輯(inline editable)| 直接放 Field Control | Field 是頁面表單佈局,table cell 空間受限 |
375
+ | 把 Field 當輸入元件本身(常見誤解:「Field = form 輸入框」)| `Input` 等 Field Control | Field 只管佈局 + context,輸入行為住在 control(見「與 Field Controls 的職責切分」)|
356
376
  | 純 action 按鈕(submit / cancel / 頁面導覽)| 頁面 footer / toolbar | Field 是資料輸入容器,不放頁面級 action |
357
377
  | 巢狀 Field | ❌ 不支援 | Field 不支援巢狀,多欄位用 FieldGroup |
358
378
 
359
379
  ---
360
380
 
361
- ## 為何無 Inspector
381
+ ## Anatomy stories
362
382
 
363
- Field 是**表單欄位容器**(label + helper + control + error 的佈局包裝),設計決策維度是 `orientation`(vertical / horizontal)× `size`(sm/md/lg)× `state`(required / invalid / disabled / readonly)× `color`——已由 `OrientationMatrix` / `SizeMatrix` / `StateBehavior` / `ColorMatrix` + 元件特有 `FieldGroupBehavior` 五張結構矩陣完整覆蓋。
364
-
365
- Inspector 對 container 類元件沒有對應教學價值——Field 本身不產生互動 affordance(互動由 Field Control 例如 Input / Select 處理),該 Inspect 的是 Field Control layer(已有各自元件的 Inspector)。重寫 Inspector = 複製 Input / Select 的 Inspector。
366
-
367
- 對應 anatomy story:保留 `Overview` + `OrientationMatrix` + `SizeMatrix` + `StateBehavior` + `ColorMatrix` + 元件特有 `FieldGroupBehavior`。
383
+ 對應 anatomy story:`Overview` + `Inspector`(元件檢閱器——切 orientation / size / mode / required / invalid 即時 render)+ `OrientationMatrix` + `SizeMatrix` + `StateBehavior` + `ColorMatrix` + 元件特有 `FieldGroupBehavior` + `Accessibility`。
368
384
 
369
385
  ---
370
386
 
371
387
  ## Field state machine SSOT(v13.3)
372
388
 
373
- **Canonical**:**focus dominates everything**(M11 延伸:focus 勝 hover/open/error-rest)。Cursor in input = user 編輯中 = 永遠藍。對齊 Material 3 / Polaris / Ant Design 5 共識。SSOT 在 `field-wrapper.tsx` 三 compoundVariant — 改一處全 control + cell + 各 variant 跟動。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
389
+ **Canonical**:**focus dominates everything**(M11 延伸:focus 勝 hover/open/error-rest)。Cursor in input = user 編輯中 = 永遠藍。對齊 Material 3 / Polaris / Ant Design 5 共識。SSOT 在 `field-wrapper.tsx` 三 compoundVariant — 改一處全 control + cell + 各 variant 跟動(**例外:error state CSS 目前逐 control 實作**——input.tsx / select.tsx / number-input.tsx 各自帶 `border-error` 系列,不在 field-wrapper compoundVariants,改 error 需逐 control 同步)。 <!-- @benchmark-unverified -->
374
390
 
375
391
  | State | Token | CSS |
376
392
  |---|---|---|
@@ -383,7 +399,7 @@ Inspector 對 container 類元件沒有對應教學價值——Field 本身不
383
399
 
384
400
  副作用(自動達成 Ant「選後藍 / 取消灰」):選 option → Radix `onCloseAutoFocus` return focus → focus-within fires → 藍 / 點外取消 → focus 移外 → 灰。純 focus 機制無需 transient class。
385
401
 
386
- **反模式 ❌**:per-control `open && 'border-primary'`(Combobox/Select/PeoplePicker)/ `data-[state=open]:border-primary` — 已 v13.3 全 retire。`hook check_field_state_token_consume` write-time 攔。
402
+ **反模式 ❌**:per-control `open && 'border-primary'`(Combobox/Select/PeoplePicker)/ `data-[state=open]:border-primary` — 已 v13.3 全 retirehook `check_field_family_invariants.sh` A.3(原 check_field_state_token_consume)write-time 攔。
387
403
 
388
404
  ---
389
405
 
@@ -406,7 +422,7 @@ Inspector 對 container 類元件沒有對應教學價值——Field 本身不
406
422
 
407
423
  **反模式 ❌**:naked 自寫 `outline-*` / `box-shadow inset` state ring(用 L3) / hardcode `<span h-[1lh]>` slot(用 L1)/ hardcode `items-center`(用 L2)/ per-control `open && 'border-primary'`(用 L3 SSOT)。
408
424
 
409
- **機械防漂移**:hooks `check_naked_row_mode_propagation` / `check_field_state_token_consume`(v13 升級)/ `check_inline_action_canonical_gap` / `check_row_slot_handcraft`(write-time)+ audit `/design-system-audit` Group N M36-M39
425
+ **機械防漂移**:hooks `check_field_family_invariants.sh`(A.1 naked row-mode propagation / A.3 Field state ring SSOT,原 standalone hooks 已 folded)+ `check_pattern_invariants.sh`(C.2 inline-action canonical gap / C.4 row slot handcraft)write-time+ `/design-system-audit` 對應 audit dim
410
426
 
411
427
  ## 相關
412
428
 
@@ -414,7 +430,6 @@ Inspector 對 container 類元件沒有對應教學價值——Field 本身不
414
430
  - `./form-validation.spec.md` — 表單驗證標準(blur 驗證、zod schema、error 顯示)
415
431
  - `../DescriptionList/description-list.spec.md` — 唯讀資訊展示(非表單)
416
432
  - `../../patterns/element-anatomy/item-anatomy.spec.md` — SelectionItem 佈局(Checkbox / Radio 放進 Field 時 block 模式的參照)
417
- - CLAUDE.md「元件 Props 命名原則」— Field 的 orientation / block control 宣告規則
418
433
 
419
434
  ## A11y 預設
420
435
 
@@ -427,12 +442,19 @@ Inspector 對 container 類元件沒有對應教學價值——Field 本身不
427
442
 
428
443
  **Focus**:focus-visible ring 對齊 DS canonical(`outline: 2px solid var(--ring)`);Field 不搶焦點,focus 由內部 control 自管。
429
444
 
445
+ **SR 識別路徑**(由既有 aria 接線達成,見「FieldContext」與「驗證與 aria 屬性」段):focus 進 control 時 SR 可得完整資訊——label(`htmlFor`/`id` 連結)、required(control 的 `aria-required`)、invalid(`aria-invalid`)、description(`aria-describedby`)、error(`aria-errormessage`;`<FieldError role="alert">` 另在錯誤出現時即時播報)。required 星號本身 `aria-hidden`,SR 資訊來自 `aria-required` 非 `*` 字元。
446
+
430
447
  **驗證**:Storybook a11y addon panel 應 0 critical violation;鍵盤完整可操作(無需滑鼠)。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。
431
448
 
432
449
  ## 被引用(auto-maintained,Dim 3 reciprocal audit)
433
450
 
434
451
  > 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
435
452
 
453
+ - `description-list.spec.md`
436
454
  - `element-anatomy.spec.md`
455
+ - `field-control-group.spec.md`
456
+ - `field-controls.spec.md`
457
+ - `form-validation.spec.md`
458
+ - `input.spec.md`
437
459
  - `radio-group.spec.md`
438
460
  - `switch.spec.md`
@@ -73,7 +73,7 @@ export const StateCascade: Story = {
73
73
  render: () => (
74
74
  <div className="flex flex-col gap-8">
75
75
  <div>
76
- <p className="text-body-sm font-medium text-fg-muted mb-3">{'<Field disabled> — 所有控件自動停用(含 Switch / Rating / Slider / Select / DatePicker)'}</p>
76
+ <p className="text-body-sm font-medium text-fg-secondary mb-3">{'<Field disabled> — 所有控件自動停用(含 Switch / Rating / Slider / Select / DatePicker)'}</p>
77
77
  <div className="flex flex-wrap gap-x-8 gap-y-4 max-w-3xl">
78
78
  <Field disabled className="w-44"><FieldLabel>負責人</FieldLabel><Input value="王小明" onChange={() => {}} /></Field>
79
79
  <Field disabled className="w-44"><FieldLabel>優先級</FieldLabel><Select options={CASCADE_PRIORITY} value="high" onChange={() => {}} aria-label="優先級" /></Field>
@@ -86,7 +86,7 @@ export const StateCascade: Story = {
86
86
  </div>
87
87
  </div>
88
88
  <div>
89
- <p className="text-body-sm font-medium text-fg-muted mb-3">{'<Field mode="display"> — 有展示態的控件自動切純展示(Select / DatePicker / Checkbox / Switch 修復後生效)'}</p>
89
+ <p className="text-body-sm font-medium text-fg-secondary mb-3">{'<Field mode="display"> — 有展示態的控件自動切純展示(Select / DatePicker / Checkbox / Switch 修復後生效)'}</p>
90
90
  <div className="flex flex-wrap gap-x-8 gap-y-4 max-w-3xl">
91
91
  <Field mode="display" className="w-44"><FieldLabel>負責人</FieldLabel><Input value="王小明" onChange={() => {}} /></Field>
92
92
  <Field mode="display" className="w-44"><FieldLabel>優先級</FieldLabel><Select options={CASCADE_PRIORITY} value="high" aria-label="優先級" /></Field>
@@ -131,7 +131,7 @@ export const Horizontal: Story = {
131
131
  // ── Horizontal 垂直對齊公式驗證 ─────────────────────────────────────────
132
132
 
133
133
  export const HorizontalLabelAlignment: Story = {
134
- name: '水平 — 標籤 垂直對齊公式驗證',
134
+ name: '水平:標籤垂直對齊公式驗證',
135
135
  render: () => (
136
136
  <div className="max-w-3xl flex flex-col gap-8">
137
137
  <div>
@@ -186,7 +186,7 @@ export const HorizontalLabelAlignment: Story = {
186
186
  // ── Checkbox / Switch 在 Field 內的高度對齊 ─────────────────────────────
187
187
 
188
188
  export const MixedControlAlignment: Story = {
189
- name: '混合 控制元件 的 field 高度對齊',
189
+ name: '混合控制元件的高度對齊',
190
190
  render: () => (
191
191
  <div className="flex flex-col gap-8 max-w-3xl">
192
192
  <div>
@@ -315,7 +315,9 @@ export const SegmentedControlInField: Story = {
315
315
  </Field>
316
316
  <Field orientation="horizontal" labelWidth="120px">
317
317
  <FieldLabel>訂閱通知</FieldLabel>
318
- <Switch />
318
+ {/* 混合表單(Input/SegmentedControl 同列)= Form-edit 情境 → Switch 跟其他控件靠左
319
+ (switch.spec.md「兩種對齊慣例」判準;ml-0 覆寫 Field 內預設 ml-auto 齊右 —— 齊右屬純 settings list)*/}
320
+ <Switch className="ml-0" />
319
321
  </Field>
320
322
  </FieldGroup>
321
323
  </div>
@@ -418,7 +420,7 @@ export const BlockControlRadioGroup: Story = {
418
420
  // ── Button as Data Input Affordance ─────────────────────────────────────
419
421
 
420
422
  export const ButtonAsControl: Story = {
421
- name: 'Button 作為 控制元件',
423
+ name: 'Button 作為控制元件',
422
424
  render: () => (
423
425
  <div className="flex flex-col gap-8 max-w-3xl">
424
426
  <div>
@@ -460,7 +462,7 @@ export const ButtonAsControl: Story = {
460
462
  // ── Label Width 變化 ────────────────────────────────────────────────────
461
463
 
462
464
  export const LabelWidth: Story = {
463
- name: '標籤 寬度',
465
+ name: '標籤寬度',
464
466
  render: () => (
465
467
  <div className="max-w-2xl flex flex-col gap-4">
466
468
  <p className="text-caption text-fg-muted">labelWidth 支援任何 CSS length 值</p>
@@ -223,9 +223,9 @@ const Field = React.forwardRef<HTMLDivElement, FieldProps>(
223
223
  // - inline: min-h-field-{size} + items-center
224
224
  // 單行 control(Input、Button 等)中線置中於 min-h box。
225
225
  //
226
- // - block: flex-col + items-start + padding-top: calc((field-height - 1lh) / 2)
227
- // 多行 control(RadioGroup 等),第一行往下推到 field-height 中線,
228
- // 後續 item 自然往下流。不設 min-h(內容自己決定高度)。
226
+ // - block: flex-col + items-start(不設 min-h、不加 padding-top,內容自己決定高度)
227
+ // 多行 control(RadioGroup 等),第一行中線由 block primitive 自帶 py 推到
228
+ // field-height/2,後續 item 自然往下流。
229
229
  // Block control area 不加額外 paddingTop——block primitive(RadioGroup 等)
230
230
  // 的子元件(SelectionItem)已自帶 py = calc((field-height - 1lh) / 2),
231
231
  // 第一個 item 的文字自然落在 field-height/2。額外加 paddingTop 會 double padding。
@@ -383,6 +383,9 @@ const FieldLabel = React.forwardRef<HTMLLabelElement, FieldLabelProps>(
383
383
  style={{ ...horizontalBlockStyle, ...style }}
384
384
  data-field-slot="label"
385
385
  data-field-disabled={disabled ? '' : undefined}
386
+ // 2026-06-10 a11y:styled-disabled label 必明告 inactive(WCAG 1.4.3 inactive-UI 豁免需可機判;
387
+ // axe 對無 aria-disabled 的 fg-disabled 文字誤報 color-contrast — deep-audit 抓 8 筆)
388
+ aria-disabled={disabled || undefined}
386
389
  {...props}
387
390
  >
388
391
  <span className="inline-flex items-center gap-1">
@@ -1,12 +1,10 @@
1
- <!-- @benchmark-cited: D5 retrofit 2026-05-18 — body claims marked per-claim @benchmark-unverified inline; canonical source URLs in frontmatter benchmark list. -->
1
+ <!-- @benchmark-cited: D5 retrofit 2026-05-18 — body claims marked per-claim @benchmark-unverified inline(M22(d) 顯式撤回;本檔 frontmatter benchmark list,來源 URL 未補)。 -->
2
2
 
3
3
  # Form Validation 設計原則
4
4
 
5
- > **本 spec = 跨表單的 validation 方法論 rules,非 UI 元件 spec,不適用 Layout Family 分類**(Dim 16 豁免)。
5
+ > **本 spec = 跨表單的 validation 方法論 rules**(表單層級行為規範,適用於所有含 Field 元件的表單)。非 UI 元件 spec,不適用 Layout Family 分類(Dim 16 豁免)。
6
6
  > 元件級 validation 視覺規格住在 `Field/field.spec.md`(Field wrapper chrome)+ 各 form control spec。
7
7
 
8
- 表單層級的驗證行為規範。適用於所有包含 Field 元件的表單。
9
-
10
8
  ---
11
9
 
12
10
  ## 表單驗證原則
@@ -43,7 +41,7 @@
43
41
  ### Submit 驗證
44
42
 
45
43
  7. **Submit 驗證全部**——點擊 submit 時對所有欄位執行驗證(不依賴個別 field 的 blur 狀態)
46
- 8. **Anchor 到第一個錯誤**——若有任何欄位出錯,scroll 並 focus 到第一個錯誤欄位
44
+ 8. **Anchor 到第一個錯誤**——若有任何欄位出錯,scroll 並 focus 到第一個錯誤欄位。多次 submit 重試時,每次都重新驗證全部欄位並重新計算「第一個錯誤」(rule 7 的自然結果),不保持上次 anchor 位置
47
45
  9. **Async / cross-field 驗證 defer 到 submit**——某些驗證無法在 blur 當下完成(如「名稱是否重複」需要 API 查詢、跨欄位邏輯如「結束日不得早於開始日」),這些在 submit 時統一判斷。若有錯誤,同樣 anchor 到第一個出錯欄位。
48
46
 
49
47
  ### 驗證分層
@@ -91,29 +89,29 @@
91
89
 
92
90
  四個關鍵決策,各自有世界級先例支撐:
93
91
 
94
- **(1) Blur-only validation(non-onChange)— 對齊 Polaris / Carbon「don't validate while typing」** <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
92
+ **(1) Blur-only validation(non-onChange)— 對齊 Polaris / Carbon「don't validate while typing」** <!-- @benchmark-unverified -->
95
93
 
96
94
  Ant Design default `validateTrigger=['onChange', 'onBlur']` 對使用者 aggressive — 才打「user@」就跳「invalid email」碎念,reader 思路被打斷。Polaris / Carbon / iOS / Atlassian 共識 onBlur + submit,讓使用者「先表達完意圖再評斷」。
97
95
 
98
- 捨棄 onChange 即時驗證的代價是「打錯看不到反饋」(打到第 3 位才發現密碼太短),但 Field 內可選 `realtime` mode 提供 hint(非 error)補足,本 spec 規範 default 行為。
96
+ 捨棄 onChange 即時驗證的代價是「打錯看不到反饋」(打到第 3 位才發現密碼太短)——本 spec 規範 default 行為(blur + submit);DS 目前無 onChange hint API。
99
97
 
100
- **(2) Edit 清 error + blur 重驗(已出錯後),非 onChange 重驗 — 對齊 Carbon / Atlassian 兩階段哲學** <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
98
+ **(2) Edit 清 error + blur 重驗(已出錯後),非 onChange 重驗 — 對齊 Carbon / Atlassian 兩階段哲學** <!-- @benchmark-unverified -->
101
99
 
102
100
  Material/Polaris/Ant 已出錯後 onChange re-validate(改第 1 字 error 又跳回)— 給使用者壓力。Carbon / Atlassian「edit 清 + blur 重驗」哲學:給使用者完整修正空間,離開時才再判決。
103
101
 
104
102
  對應使用者心智:「修改」是過程,「離開 field」是動作完成的 boundary,在 boundary 評斷比每字評斷尊重 user agency。
105
103
 
106
- **(3) Create always-enabled / Update disabled-until-dirty 不對稱 — 對齊 Stripe / Notion / Linear 現代慣例** <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
104
+ **(3) Create always-enabled / Update disabled-until-dirty 不對稱 — 對齊 Stripe / Notion / Linear 現代慣例** <!-- @benchmark-unverified -->
107
105
 
108
106
  Ant 對「Create」也 disabled-until-dirty(填了所有 required 才亮)— 但這讓使用者第一次進 form 看到 disabled button 困惑「為什麼按不了」。Stripe / Notion / Material 共識:Create 永遠 enabled — 點擊後若 invalid,顯示 error 並 scroll,使用者明確知道為什麼。
109
107
 
110
- Update 場景反向:沒改的 Update 沒提交意義(對齊「intent 才 commit」),disabled 表達「等你做動作」比 enabled 後點擊判斷「沒變化」更直接。對齊 Notion 設定頁 / Figma file rename 慣例。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
108
+ Update 場景反向:沒改的 Update 沒提交意義(對齊「intent 才 commit」),disabled 表達「等你做動作」比 enabled 後點擊判斷「沒變化」更直接。對齊 Notion 設定頁 / Figma file rename 慣例。 <!-- @benchmark-unverified -->
111
109
 
112
- **(4) 格式驗證 vs 業務驗證分層(blur vs submit)— 對齊 Material/Carbon「local vs cross-cutting validation」哲學** <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
110
+ **(4) 格式驗證 vs 業務驗證分層(blur vs submit)— 對齊 Material/Carbon「local vs cross-cutting validation」哲學** <!-- @benchmark-unverified -->
113
111
 
114
112
  Email 格式 / URL 格式 / 必填等「single-field 純 syntax」blur 即可判斷;名稱重複(API 查)/ 結束日 ≥ 開始日(跨欄位)等「business / async」必須 submit 才能判 — 強行 blur 觸發 API 對使用者體驗差(每換 field 一次 API call)。
115
113
 
116
- 對齊 Material `<TextField error>` + Form layer error 分層 / Carbon「format vs business」雙軌。視覺一致(都紅框 + error message)避免 reader 區分「為什麼這個 error 是 blur 出來那個是 submit 出來」。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
114
+ 對齊 Material `<TextField error>` + Form layer error 分層 / Carbon「format vs business」雙軌。視覺一致(都紅框 + error message)避免 reader 區分「為什麼這個 error 是 blur 出來那個是 submit 出來」。 <!-- @benchmark-unverified -->
117
115
 
118
116
  ## 禁止事項
119
117
 
@@ -129,22 +127,25 @@ Email 格式 / URL 格式 / 必填等「single-field 純 syntax」blur 即可判
129
127
  Form validation 的 ARIA / 鍵盤行為(對齊 WCAG 3.3.1 Error Identification + 3.3.3 Error Suggestion):
130
128
 
131
129
  - **Error message ARIA**:Field error 容器 `id="field-{name}-error"`,Input 設 `aria-describedby="field-{name}-error"` + `aria-invalid="true"`;SR 在 focus field 時自動讀「{label}, {error message}」
132
- - **Submit error scroll**:submit 失敗後,focus 自動 jump 到第一個 invalid field(`field.focus()` + `scrollIntoView({block: 'center'})`);對齊 Material / Atlassian 慣例 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
130
+ - **Submit error scroll**:submit 失敗後,focus 自動 jump 到第一個 invalid field(`field.focus()` + `scrollIntoView({block: 'center'})`);對齊 Material / Atlassian 慣例 <!-- @benchmark-unverified -->
133
131
  - **Error live region**:跨欄位 / async error 用 `aria-live="polite"` 容器宣告 — SR 在空閒時讀出,不中斷使用者打字
134
132
  - **Required indicator**:label 的 `*` 為純視覺、對讀屏隱藏(`aria-hidden="true"`,field.tsx:392);required 語意由內部輸入控件的 `aria-required`(input.tsx:192)承擔,避免讀屏讀出「asterisk」語義不清
135
- - **Color-only error 警告**:error border 紅色之外必有 icon 或文字(WCAG 1.4.1 不僅靠顏色)— DS error variant 自動 prefix `<AlertCircle/>` icon
136
-
137
- ## 被引用(auto-maintained,Dim 3 reciprocal audit)
133
+ - **Color-only error 警告**:error border 紅色之外必有文字訊息(WCAG 1.4.1 不僅靠顏色)— 由 `<FieldError>` 文字承擔;DS **不**在 input 內放 error 狀態 icon(見 `field-controls.spec.md`「禁止事項」)
138
134
 
139
- > 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
135
+ ## 相關
140
136
 
141
- - `combobox.spec.md`
142
- - `date-picker.spec.md`
137
+ - `field.spec.md` — Field wrapper 的 error 視覺 chrome(紅框 + error message slot)
138
+ - `field-controls.spec.md` — form control 共用 state(disabled / readonly / invalid)
139
+ - `../Input/input.spec.md` — `aria-required` / `aria-invalid` 實作端(input.tsx)
143
140
 
144
141
  ## 被引用(auto-maintained,Dim 3 reciprocal audit)
145
142
 
146
143
  > 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
147
144
 
145
+ - `combobox.spec.md`
146
+ - `date-picker.spec.md`
147
+ - `field-controls.spec.md`
148
+ - `field.spec.md`
148
149
  - `link-input.spec.md`
149
150
  - `textarea.spec.md`
150
151
  - `time-picker.spec.md`
@@ -7,6 +7,7 @@ import type { Meta, StoryObj } from '@storybook/react'
7
7
  import { FieldControlGroup } from './field-control-group'
8
8
  import { Select } from '@/design-system/components/Select/select'
9
9
  import { Input } from '@/design-system/components/Input/input'
10
+ import { OPERATOR_REGISTRY } from '@/design-system/components/DataTable/filter-operators'
10
11
 
11
12
  const meta: Meta<typeof FieldControlGroup> = {
12
13
  title: 'Design System/Components/FieldControlGroup/設計規格',
@@ -20,10 +21,10 @@ const FIELDS = [
20
21
  { value: 'sku', label: 'SKU' },
21
22
  { value: 'name', label: '名稱' },
22
23
  ]
23
- const OPS = [
24
- { value: 'is', label: '等於' },
25
- { value: 'contains', label: '包含' },
26
- ]
24
+ // op 選項消費 OPERATOR_REGISTRY SSOT(audit dim 32 — 禁 hardcode op 字串)
25
+ const OPS = OPERATOR_REGISTRY.string
26
+ .filter((o) => ['is', 'contains'].includes(o.op))
27
+ .map((o) => ({ value: o.op, label: o.label }))
27
28
 
28
29
  /* Overview */
29
30
  export const Overview: Story = {
@@ -42,9 +43,9 @@ export const Overview: Story = {
42
43
  <section>
43
44
  <h3 className="text-body font-bold mb-2">範例</h3>
44
45
  <FieldControlGroup block>
45
- <Select className="w-[120px]" options={FIELDS} value="name" onChange={() => {}} />
46
- <Select className="w-[100px]" options={OPS} value="contains" onChange={() => {}} />
47
- <Input className="flex-1" defaultValue="無線滑鼠" />
46
+ <Select className="w-[120px]" options={FIELDS} value="name" onChange={() => {}} aria-label="篩選欄位" />
47
+ <Select className="w-[100px]" options={OPS} value="contains" onChange={() => {}} aria-label="篩選運算子" />
48
+ <Input className="flex-1" defaultValue="無線滑鼠" aria-label="篩選值" />
48
49
  </FieldControlGroup>
49
50
  </section>
50
51
  </div>
@@ -60,8 +61,8 @@ export const SizeMatrix: Story = {
60
61
  <div key={size}>
61
62
  <p className="text-caption text-fg-muted mb-2">size="{size}"</p>
62
63
  <FieldControlGroup block>
63
- <Select size={size} className="w-[120px]" options={FIELDS} value="name" onChange={() => {}} />
64
- <Input size={size} className="flex-1" defaultValue="無線滑鼠" />
64
+ <Select size={size} className="w-[120px]" options={FIELDS} value="name" onChange={() => {}} aria-label="篩選欄位" />
65
+ <Input size={size} className="flex-1" defaultValue="無線滑鼠" aria-label="篩選值" />
65
66
  </FieldControlGroup>
66
67
  </div>
67
68
  ))}
@@ -77,22 +78,22 @@ export const StateBehavior: Story = {
77
78
  <div>
78
79
  <p className="text-caption text-fg-muted mb-2">default</p>
79
80
  <FieldControlGroup block>
80
- <Select className="w-[120px]" options={FIELDS} value="name" onChange={() => {}} />
81
- <Input className="flex-1" defaultValue="無線滑鼠" />
81
+ <Select className="w-[120px]" options={FIELDS} value="name" onChange={() => {}} aria-label="篩選欄位" />
82
+ <Input className="flex-1" defaultValue="無線滑鼠" aria-label="篩選值" />
82
83
  </FieldControlGroup>
83
84
  </div>
84
85
  <div>
85
86
  <p className="text-caption text-fg-muted mb-2">disabled(整 group children 各自 disabled)</p>
86
87
  <FieldControlGroup block>
87
- <Select className="w-[120px]" options={FIELDS} value="name" onChange={() => {}} disabled />
88
- <Input className="flex-1" defaultValue="無線滑鼠" disabled />
88
+ <Select className="w-[120px]" options={FIELDS} value="name" onChange={() => {}} disabled aria-label="篩選欄位" />
89
+ <Input className="flex-1" defaultValue="無線滑鼠" disabled aria-label="篩選值" />
89
90
  </FieldControlGroup>
90
91
  </div>
91
92
  <div>
92
93
  <p className="text-caption text-fg-muted mb-2">cell error(其中一 child invalid → border-error;聚焦該 child 時才提升到 z-3 蓋過鄰接 border)</p>
93
94
  <FieldControlGroup block>
94
- <Select className="w-[120px]" options={FIELDS} value="name" onChange={() => {}} />
95
- <Input className="flex-1" defaultValue="無線滑鼠" error />
95
+ <Select className="w-[120px]" options={FIELDS} value="name" onChange={() => {}} aria-label="篩選欄位" />
96
+ <Input className="flex-1" defaultValue="無線滑鼠" error aria-label="篩選值" />
96
97
  </FieldControlGroup>
97
98
  </div>
98
99
  </div>
@@ -6,6 +6,7 @@ import { Field, FieldLabel, FieldGroup } from '@/design-system/components/Field/
6
6
  import { Select } from '@/design-system/components/Select/select'
7
7
  import { Input } from '@/design-system/components/Input/input'
8
8
  import { Button } from '@/design-system/components/Button/button'
9
+ import { OPERATOR_REGISTRY } from '@/design-system/components/DataTable/filter-operators'
9
10
 
10
11
  const meta: Meta<typeof FieldControlGroup> = {
11
12
  title: 'Design System/Components/FieldControlGroup/設計原則',
@@ -17,7 +18,10 @@ type Story = StoryObj<typeof FieldControlGroup>
17
18
 
18
19
  const CODES = [{ value: '+886', label: '+886' }, { value: '+1', label: '+1' }]
19
20
  const FIELDS = [{ value: 'sku', label: 'SKU' }, { value: 'name', label: '名稱' }]
20
- const OPS = [{ value: 'is', label: '等於' }, { value: 'contains', label: '包含' }]
21
+ // op 選項消費 OPERATOR_REGISTRY SSOT(audit dim 32 hardcode op 字串)
22
+ const OPS = OPERATOR_REGISTRY.string
23
+ .filter((o) => ['is', 'contains'].includes(o.op))
24
+ .map((o) => ({ value: o.op, label: o.label }))
21
25
 
22
26
  /* WhenToUse + WhenNotToUse + VsRule + ContentGuidelines = single UsageGuidance */
23
27
  export const UsageGuidance: Story = {
@@ -139,6 +139,7 @@ interface FieldControlGroupProps extends HTMLAttributes<HTMLDivElement> {
139
139
  | hover(子)| 該 child border-hover | 3 |
140
140
  | focus / focus-within(子)| 該 child focus ring | 3 |
141
141
  | disabled(子)| 該 child disabled style + **FCG-local override `border-[var(--border-opaque)]`**(K12,2026-05-04) | 0 |
142
+ | error(子)| 該 child border-error | 2(預設層;聚焦該 child 時才升 3 蓋過鄰接 border)|
142
143
 
143
144
  **Disabled border integrity canonical(K12,2026-05-04)**:全域 disabled = `border-transparent`(讓 standalone field 視覺輕量),但**FCG context 下,disabled child 強制 `border-[var(--border-opaque)]`** — 確保:(a) FCG 整體外圈 border 健在,(b) inner divider 健在(不會因兩相鄰 disabled cells 都 transparent 而消失)。bg-disabled 仍區分狀態,border 維護群組視覺整合性。對齊 [Bootstrap input-group](https://getbootstrap.com/docs/5.3/forms/input-group/) / [Ant Space.Compact](https://ant.design/components/space#spacecompact) disabled idiom。
144
145
 
@@ -153,7 +154,6 @@ interface FieldControlGroupProps extends HTMLAttributes<HTMLDivElement> {
153
154
  **Token 系統設計**:`--border-opaque` 在 `semantic.css` 新增(grep `--border-opaque:` 查定義行,不寫死行號避免漂移),語意「視覺等同 `--border` 但 alpha-immune」。對齊 [Ant Design `colorBorderSecondary`](https://ant.design/docs/react/customize-theme#seedtoken) solid idiom — Ant 用此 token 在 table 外框 + row divider(non-white bg 場景),跟 input alpha border 視覺層級分。
154
155
 
155
156
  **為什麼不 override bg**:user 明確要求 disabled cells 有底色(辨識 state)。bg 灰底是 disabled state 的主要視覺載體,FCG context 不應抹除。
156
- | error(子)| 該 child border-error | 2(預設層;聚焦該 child 時才升 3 蓋過鄰接 border)|
157
157
 
158
158
  **整 row error**:目前 v1 不支援 row-level error(走 cell-level)。未來若需可走 outer border-error wrapper,但 v1 follow Ant 不做。
159
159
 
@@ -162,6 +162,15 @@ interface FieldControlGroupProps extends HTMLAttributes<HTMLDivElement> {
162
162
  - Loading:子 control 各自處理(Input loading state / Select loading)
163
163
  - Empty:N/A(layout primitive,無資料概念)
164
164
  - 驗證:子 control 自管(form library 透過 Field 處理)
165
+ - 「子必 direct child」規則**無 runtime 偵測**——違反(多包 wrapper div)時的症狀是圓角破圖(CSS `[&>*]` 命中 wrapper),見禁止事項 2026-05-04 #2;靠 review + Storybook 目視抓
166
+ - a11y 驗證:Storybook a11y addon panel 0 critical violation;Tab 順序 = children DOM 順序
167
+
168
+ ## 邊界案例
169
+
170
+ - **子高度不一**:容器 `items-stretch`,但 field controls 自帶固定 `h-field-*`,不會被拉齊——混 size 即視覺高低差(故禁止,見禁止事項)
171
+ - **某子 disabled、某子 edit**:機制上可行(disabled 子降 z-0 + K12 border 維持 divider),但語意一體應一致(見禁止事項);**全組 disabled** 走 K12 canonical(見 States)
172
+ - **極窄容器**:無特化處理——固定寬 children(`w-[Xpx]`)溢出容器、`flex-1` children 壓縮;子自管 width 的對價,consumer 自行配置
173
+ - **單一 child**:radius 選擇器 `:first-child:not(:last-child)` 不命中,圓角完整保留(等同未包 group,但此時應直接用 Field)
165
174
 
166
175
  ## 世界級對照
167
176
 
@@ -174,6 +183,13 @@ interface FieldControlGroupProps extends HTMLAttributes<HTMLDivElement> {
174
183
 
175
184
  3/4 共識 → 我們的實作對齊 Ant + Bootstrap 主軸。
176
185
 
186
+ ## 相關
187
+
188
+ - `../Field/field.spec.md` — FieldGroup(多 Field 垂直堆疊近親,gap 分離)的 home
189
+ - `../Button/button-group.tsx` — 同 border-collapse mechanism 的 Button 版(無獨立 spec,機制同源)
190
+ - `../Field/field-controls.spec.md` — children(Input / Select / DatePicker 等)共用 Field control 規則
191
+ - `../Checkbox/checkbox.spec.md` / `../RadioGroup/radio-group.spec.md` — semantic group 近親(1 question 多 options,非本元件 scope)
192
+
177
193
  ## 變更紀錄
178
194
 
179
195
  - 2026-05-04 v1:Initial。基於 Ant compact-item.ts source verify 訂機制。Filter / Sort 對齊 Ant W-A 子自管 width。