@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
@@ -8,6 +8,7 @@ import { Select } from '@/design-system/components/Select/select'
8
8
  import { Input } from '@/design-system/components/Input/input'
9
9
  import { NumberInput } from '@/design-system/components/NumberInput/number-input'
10
10
  import { Button } from '@/design-system/components/Button/button'
11
+ import { OPERATOR_REGISTRY } from '@/design-system/components/DataTable/filter-operators'
11
12
 
12
13
  const meta: Meta<typeof FieldControlGroup> = {
13
14
  title: 'Design System/Components/FieldControlGroup/展示',
@@ -42,7 +43,7 @@ const CURRENCIES = [
42
43
  ]
43
44
 
44
45
  /* ── 電話輸入(國碼 + 號碼)── */
45
- export const Phone_NumberInput: Story = {
46
+ export const PhoneNumber: Story = {
46
47
  name: '電話',
47
48
  render: () => {
48
49
  const [code, setCode] = React.useState('+886')
@@ -75,7 +76,7 @@ export const Phone_NumberInput: Story = {
75
76
  }
76
77
 
77
78
  /* ── 金額輸入(幣別 + 數字)── */
78
- export const Currency_Amount: Story = {
79
+ export const CurrencyAmount: Story = {
79
80
  name: '金額',
80
81
  render: () => {
81
82
  const [currency, setCurrency] = React.useState('TWD')
@@ -105,7 +106,7 @@ export const Currency_Amount: Story = {
105
106
  }
106
107
 
107
108
  /* ── 搜尋 + 提交按鈕 ── */
108
- export const Search_Submit: Story = {
109
+ export const SearchSubmit: Story = {
109
110
  name: '搜尋與提交',
110
111
  render: () => {
111
112
  const [q, setQ] = React.useState('')
@@ -133,13 +134,12 @@ const FILTER_FIELDS = [
133
134
  { value: 'category', label: '類別' },
134
135
  { value: 'stock', label: '庫存' },
135
136
  ]
136
- const STRING_OPS = [
137
- { value: 'contains', label: '包含' },
138
- { value: 'is', label: '等於' },
139
- { value: 'starts_with', label: '開頭為' },
140
- ]
137
+ // op 選項消費 OPERATOR_REGISTRY SSOT(DataTable/filter-operators.ts;audit dim 32 — 禁 hardcode op 字串)
138
+ const STRING_OPS = OPERATOR_REGISTRY.string
139
+ .filter((o) => o.valueShape === 'text')
140
+ .map((o) => ({ value: o.op, label: o.label }))
141
141
 
142
- export const FilterRow_RealUsage: Story = {
142
+ export const FilterRow: Story = {
143
143
  name: '篩選列(資料表進階篩選實例)',
144
144
  render: () => {
145
145
  const [field, setField] = React.useState('name')
@@ -116,13 +116,14 @@ FieldControlGroup.displayName = 'FieldControlGroup'
116
116
  // Story auto-compile metadata — Phase 4 migration(2026-05-10 #12 task complete)
117
117
  // Per scripts/compile-stories.mjs --check。FieldControlGroup is self-contained
118
118
  // structural wrapper(border-collapse pattern for Field controls)。
119
- // **No own sizes** — size prop is cascade-only(passes through to children Field controls,
120
- // not own visual variants),so sizes:{} matches spec frontmatter (no sizes declared)
119
+ // **No own sizes** — size prop 目前為 no-op(不傳遞給 children,無 Context Provider /
120
+ // cloneElement;children 尺寸自管,見上方 docblock「Size」段 + spec「FCG `size` prop 目前為 no-op」),
121
+ // so sizes:{} matches spec frontmatter (no sizes declared)。
121
122
  export const fieldControlGroupMeta = {
122
123
  component: 'FieldControlGroup',
123
124
  family: 'self-contained',
124
125
  variants: {},
125
- sizes: {}, // self-contained wrapper, sizes cascade to children only
126
+ sizes: {}, // self-contained wrapper;size prop no-op(children 尺寸自管)
126
127
  states: ['default', 'children-hover', 'children-focus', 'children-disabled'],
127
128
  tokens: {
128
129
  bg: [], // structural wrapper has no own bg
@@ -47,7 +47,7 @@ export const Overview: Story = {
47
47
  ['name', 'string', '必填', '檔名'],
48
48
  ['mode', "'compact' | 'rich'", "'compact'", 'compact=Paperclip 16px icon / rich=Avatar 48px 縮圖'],
49
49
  ['status', "'uploading' | 'completed' | 'error'", '—', '上傳狀態(不傳=已上傳靜態)'],
50
- ['progress', 'number', '—', '上傳進度 0-100(uploading 時顯示 bar)'],
50
+ ['progress', 'number', '—', '上傳進度 0-100( status 時顯示 bar;completed 強制 100%)'],
51
51
  ['description', 'ReactNode', '—', 'rich 任意場景 / compact 只有 error 才顯示。ReactNode — 可含 inline clickable link(如「View log」)'],
52
52
  ['thumbnailSrc', 'string', '—', 'rich mode 的縮圖 URL(圖片類檔案)'],
53
53
  ['actions', 'ReactNode', '—', 'suffix actions(例:delete / cancel button)'],
@@ -127,7 +127,7 @@ export const ColorMatrix: Story = {
127
127
  <Td mono>uploading</Td>
128
128
  <Td><span className="inline-flex items-center gap-1.5"><Swatch value="--foreground" size="sm" /><span className="font-mono">--foreground</span></span></Td>
129
129
  <Td><span className="inline-flex items-center gap-1.5"><Swatch value="--fg-secondary" size="sm" /><span className="font-mono">--fg-secondary</span></span></Td>
130
- <Td><span className="inline-flex items-center gap-1.5"><Swatch value="--primary" size="sm" /><span className="font-mono">--primary(inProgress)</span></span></Td>
130
+ <Td><span className="inline-flex items-center gap-1.5"><Swatch value="--info" size="sm" /><span className="font-mono">--info(inProgress)</span></span></Td>
131
131
  <Td>—(只顯示 bar)</Td>
132
132
  </tr>
133
133
  <tr>
@@ -149,16 +149,6 @@ export const ColorMatrix: Story = {
149
149
  </div>
150
150
  </div>
151
151
 
152
- <div>
153
- <H3>視覺對照(rich mode)</H3>
154
- <div className="flex flex-col gap-2 max-w-lg">
155
- <FileItem name="Q1-marketing-report.pdf" description="2.4 MB · 已上傳" mode="rich" />
156
- <FileItem name="brand-guidelines.pdf" description="1.2 MB · 上傳中 45%" status="uploading" progress={45} mode="rich" />
157
- <FileItem name="Q2-forecast.xlsx" description="上傳成功 · 3.8 MB" status="completed" progress={100} mode="rich" />
158
- <FileItem name="legacy-data.csv" description="上傳失敗 · 網路中斷" status="error" mode="rich" />
159
- </div>
160
- </div>
161
-
162
152
  <div>
163
153
  <H3>Container background(per mode,**無 hover-bg**)</H3>
164
154
  <Desc>
@@ -199,7 +189,7 @@ export const ModeMatrix: Story = {
199
189
 
200
190
  <div>
201
191
  <H3>compact(預設)— Paperclip 16px icon 在左</H3>
202
- <Desc>掃描模式(text-caption),資訊密度高。適合批次上傳的 logs / CSV / JSON。Description 只在 error 才顯示。</Desc>
192
+ <Desc>掃描模式(text-body + leading-compact;desc 為 text-caption,兩 mode 統一),資訊密度高。適合批次上傳的 logs / CSV / JSON。Description 只在 error 才顯示。</Desc>
203
193
  <div className="flex flex-col gap-1 max-w-lg">
204
194
  <FileItem name="users.csv" mode="compact" status="completed" progress={100} />
205
195
  <FileItem name="orders.json" mode="compact" status="uploading" progress={42} />
@@ -214,7 +204,7 @@ export const ModeMatrix: Story = {
214
204
  <table className="text-caption border-collapse">
215
205
  <thead><tr><Th>Mode</Th><Th>Prefix</Th><Th>Typography</Th><Th>Description</Th><Th>使用場景</Th></tr></thead>
216
206
  <tbody>
217
- <tr><Td mono>compact(預設)</Td><Td>Paperclip 16px</Td><Td>掃描模式(text-caption)</Td><Td>只有 error 才顯示</Td><Td>批次上傳、一般檔案</Td></tr>
207
+ <tr><Td mono>compact(預設)</Td><Td>Paperclip 16px</Td><Td>掃描模式(text-body;兩 mode 統一 scanning)</Td><Td>只有 error 才顯示</Td><Td>批次上傳、一般檔案</Td></tr>
218
208
  <tr><Td mono>rich</Td><Td>Avatar 48px square</Td><Td>掃描模式(text-body;兩 mode 統一 scanning)</Td><Td>任何場景</Td><Td>圖片、文件、需要預覽</Td></tr>
219
209
  </tbody>
220
210
  </table>
@@ -245,7 +235,7 @@ export const SizeMatrix: Story = {
245
235
  </tr>
246
236
  </thead>
247
237
  <tbody>
248
- <tr><Td>Prefix</Td><Td mono>Paperclip 16px(foreground)</Td><Td mono>Avatar 48px square(縮圖或 fallback)</Td></tr>
238
+ <tr><Td>Prefix</Td><Td mono>Paperclip 16px(fg-muted)</Td><Td mono>Avatar 48px square(縮圖或 fallback)</Td></tr>
249
239
  <tr><Td>Row 高度(無 bar)</Td><Td mono>px-3 py-2(content 1lh)</Td><Td mono>content col minHeight 48(avatar)+ py-3</Td></tr>
250
240
  <tr><Td>Typography</Td><Td mono>text-body leading-compact(掃描模式)</Td><Td mono>text-body leading-compact(掃描模式;兩 mode 統一 scanning)</Td></tr>
251
241
  <tr><Td>Description</Td><Td>僅 error 才顯示</Td><Td>任何場景都可顯示</Td></tr>
@@ -263,7 +253,7 @@ export const SizeMatrix: Story = {
263
253
  <div className="grid grid-cols-2 gap-6">
264
254
  <div>
265
255
  <div className="text-caption text-fg-muted mb-2 font-mono">mode="compact"</div>
266
- <div className="flex flex-col gap-0 max-w-sm border border-border rounded-md">
256
+ <div className="flex flex-col gap-1 max-w-sm">
267
257
  <FileItem name="Q1-report.pdf" mode="compact" status="completed" progress={100} />
268
258
  <FileItem name="users.csv" mode="compact" status="completed" progress={100} />
269
259
  <FileItem name="products.xlsx" mode="compact" status="uploading" progress={62} />
@@ -310,7 +300,7 @@ export const StateBehavior: Story = {
310
300
  <table className="text-caption border-collapse">
311
301
  <thead><tr><Th>Status</Th><Th>Progress bar 色</Th><Th>Status icon</Th></tr></thead>
312
302
  <tbody>
313
- <tr><Td mono>uploading</Td><Td mono>bg-primary</Td><Td>—</Td></tr>
303
+ <tr><Td mono>uploading</Td><Td mono>bg-info</Td><Td>—</Td></tr>
314
304
  <tr><Td mono>completed</Td><Td mono>bg-success(100%)</Td><Td>CircleCheck(text-success)</Td></tr>
315
305
  <tr><Td mono>error</Td><Td mono>bg-error(寬度 = consumer 傳入 progress)</Td><Td>XCircle(text-error)</Td></tr>
316
306
  <tr><Td>(無 status)</Td><Td>無 bar</Td><Td>—</Td></tr>
@@ -43,19 +43,19 @@ export const UsageGuidance: Story = {
43
43
  <p>適合 FileItem 的真實業務場景(點擊跳轉「展示」頁範例):</p>
44
44
  <ul className="space-y-1">
45
45
  <li>
46
- <LinkTo kind="Design System/Components/FileItem/展示" name="Rich"><span className="text-primary hover:underline font-medium cursor-pointer">Rich</span></LinkTo>
46
+ <LinkTo kind="Design System/Components/FileItem/展示" name="豐富樣式"><span className="text-primary hover:underline font-medium cursor-pointer">圖片 / 設計稿上傳 — 需要縮圖預覽(豐富樣式)</span></LinkTo>
47
47
  </li>
48
48
  <li>
49
- <LinkTo kind="Design System/Components/FileItem/展示" name="Compact"><span className="text-primary hover:underline font-medium cursor-pointer">Compact</span></LinkTo>
49
+ <LinkTo kind="Design System/Components/FileItem/展示" name="緊湊樣式"><span className="text-primary hover:underline font-medium cursor-pointer">CSV / JSON 批次匯入的密集清單(緊湊樣式)</span></LinkTo>
50
50
  </li>
51
51
  <li>
52
- <LinkTo kind="Design System/Components/FileItem/展示" name="懸停替換"><span className="text-primary hover:underline font-medium cursor-pointer">Hover 替換</span></LinkTo>
52
+ <LinkTo kind="Design System/Components/FileItem/展示" name="懸停替換"><span className="text-primary hover:underline font-medium cursor-pointer">上傳完成後滑入整列、狀態 icon 換成下載鈕(懸停替換)</span></LinkTo>
53
53
  </li>
54
54
  <li>
55
- <LinkTo kind="Design System/Components/FileItem/展示" name="已上傳"><span className="text-primary hover:underline font-medium cursor-pointer">已上傳</span></LinkTo>
55
+ <LinkTo kind="Design System/Components/FileItem/展示" name="已上傳"><span className="text-primary hover:underline font-medium cursor-pointer">工單 / 訊息附件的已上傳靜態檔案</span></LinkTo>
56
56
  </li>
57
57
  <li>
58
- <LinkTo kind="Design System/Components/FileItem/展示" name="緊湊 混合"><span className="text-primary hover:underline font-medium cursor-pointer">Compact 混合</span></LinkTo>
58
+ <LinkTo kind="Design System/Components/FileItem/展示" name="緊湊 混合"><span className="text-primary hover:underline font-medium cursor-pointer">同一批次混合上傳中 / 完成 / 失敗狀態的清單</span></LinkTo>
59
59
  </li>
60
60
  </ul>
61
61
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見 <code>Vs*Rule</code> stories)。</p>
@@ -121,7 +121,7 @@ export const ModeRule: Story = {
121
121
 
122
122
  <Rule
123
123
  title="compact — 批次上傳的一般檔案(CSV / JSON)"
124
- note="左側 Paperclip 16px icon,掃描模式(text-caption),資訊密度高。適合一次顯示 10+ 檔案的 batch upload 清單"
124
+ note="左側 Paperclip 16px icon,掃描模式(text-body + leading-compact,desc 為 text-caption;兩 mode 統一),資訊密度高。適合一次顯示 10+ 檔案的 batch upload 清單"
125
125
  >
126
126
  <div className="flex flex-col gap-1">
127
127
  <FileItem
@@ -161,7 +161,7 @@ export const StatusProgressRule: Story = {
161
161
  render: () => (
162
162
  <div>
163
163
  <Rule
164
- title="uploading — progress bar primary 色 + 百分比 description"
164
+ title="uploading — progress bar info 色 + 百分比 description"
165
165
  note="上傳中清楚展示進度,讓使用者知道「多久能好」而不是只看到一個等待狀態"
166
166
  >
167
167
  <FileItem
@@ -319,19 +319,9 @@ upload-manager 的 completed(100% bar + ✓)屬「剛完成的 upload session」
319
319
  {files.map(f => <FileItem key={f.id} mode="rich" {...f} />)}
320
320
  </div>
321
321
 
322
- // ✅ Compact 無 status(form attachment,靜態灰底)
322
+ // ✅ Compact 一律 gap-1(無 status / 有 status / mixed 上傳中+已存附件 都同此 wrapper,見上表)
323
323
  <div className="flex flex-col gap-1">
324
- {files.map(f => <FileItem key={f.id} mode="compact" {...f} />)}
325
- </div>
326
-
327
- // ✅ Compact 有 status only(全有 status,uploading/error/completed)
328
- <div className="flex flex-col gap-1">
329
- {files.map(f => <FileItem key={f.id} mode="compact" status={f.status} progress={f.progress} {...f} />)}
330
- </div>
331
-
332
- // ✅ Compact mixed 上傳中 + 已存附件(email 草稿、舊附件 + 新上傳)
333
- <div className="flex flex-col gap-1">
334
- {allFiles.map(f => <FileItem key={f.id} mode="compact" status={f.isUploading ? 'uploading' : undefined} {...f} />)}
324
+ {files.map(f => <FileItem key={f.id} mode="compact" status={f.isUploading ? 'uploading' : undefined} {...f} />)}
335
325
  </div>
336
326
 
337
327
  // ❌ 反例:list 加外框 + overflow-hidden(雙重 card / 強制邊框相黏)
@@ -341,7 +331,7 @@ upload-manager 的 completed(100% bar + ✓)屬「剛完成的 upload session」
341
331
  ```
342
332
 
343
333
  **Clickable → 下載 / 預覽 canonical**(AR15):
344
- - FileItem 提供 `onClick` prop,consumer 傳入即進 clickable 模式(hover + cursor + keyboard)
334
+ - FileItem 提供 `onClick` prop,consumer 傳入即進 clickable 模式(僅滑鼠可點 + `cursor-pointer`;刻意不加整列 keyboard 焦點 / hover-bg — 見「A11y 預設」nested-interactive rationale 與「Hover 行為 canonical」)
345
335
  - 兩種 surface 都可以用 `onClick`(upload-manager 可跟 `onDownload` hover-swap 並存)
346
336
  - consumer 決定具體行為(download / FileViewer),元件只提供 row 可點擊能力
347
337
 
@@ -396,7 +386,7 @@ Consumer 自行組合。按 `patterns/element-anatomy/item-anatomy.spec.md`「Pr
396
386
  - `mode="rich"` → `var(--field-height-xs)`(24 固定,與 Button xs iconOnly 同)
397
387
  - `mode="compact"` → `var(--field-height-xs)`(24,同 rich;compact 靠 status slot wrapper 的 `[&>[data-unbounded]]:my-[calc((1lh-var(--field-height-xs))/2)]` 把 24 footprint 收斂到 1lh,不撐高 row,視覺 / touch target 仍 24)
398
388
 
399
- Passive status icon 置中於 action-sized 容器,hover 時 active action 填滿同一容器。這讓 flex gap token 測量的是**兩個同尺寸 action slot 之間的真實 gap**,不被 hover bg overflow 吃掉——status slot 尺寸 = 同 size delete slot,gap token 才能如實呈現;歷史 bug 細節見 CLAUDE.md `# 失敗記憶索引`。
389
+ Passive status icon 置中於 action-sized 容器,hover 時 active action 填滿同一容器。這讓 flex gap token 測量的是**兩個同尺寸 action slot 之間的真實 gap**,不被 hover bg overflow 吃掉——status slot 尺寸 = 同 size delete slot,gap token 才能如實呈現;歷史 bug 細節見 `.claude/skills/design-system-audit/references/historical-bugs.md`。
400
390
 
401
391
  世界級 DS 的幾何鐵律:**同一 flex 列的互動元素必須有統一 box 尺寸**,gap token 才能如實呈現。
402
392
 
@@ -412,16 +402,7 @@ Passive status icon 置中於 action-sized 容器,hover 時 active action 填滿
412
402
  mode="rich"
413
403
  name="report.pdf"
414
404
  status="completed"
415
- onDownload={() => downloadFile(id)} // hover ✓ →
416
- actions={<Button size="xs" iconOnly variant="text" startIcon={Trash2} onClick={del} aria-label="刪除" />}
417
- />
418
-
419
- <FileItem
420
- mode="compact"
421
- name="backup.json"
422
- status="error"
423
- description="There's something wrong."
424
- onRetry={() => retryUpload(id)} // hover ✗ → ⟲
405
+ onDownload={() => downloadFile(id)} // hover ✓ → ↓;error 場景同理:onRetry → hover ✗ → ⟲
425
406
  actions={<Button size="xs" iconOnly variant="text" startIcon={Trash2} onClick={del} aria-label="刪除" />}
426
407
  />
427
408
  ```
@@ -451,6 +432,7 @@ Passive status icon 置中於 action-sized 容器,hover 時 active action 填滿
451
432
  |------|------|------|
452
433
  | **FileItem** | 單一檔案 row primitive — 顯示一個檔案的 name / status / progress / actions | List 內的單筆 / detail / preview / message attachment |
453
434
  | **FileUpload** | Dropzone + file list orchestrator — 拖放區 + 多 FileItem 排列 + 上傳狀態管理 | 完整上傳流程入口 |
435
+ | **FileViewer** | 檔案內容預覽 overlay — FileItem `onClick` 的預設開啟目標 | 點 row 後的檔案全幅預覽 |
454
436
 
455
437
  **判斷**:
456
438
 
@@ -470,6 +452,18 @@ Passive status icon 置中於 action-sized 容器,hover 時 active action 填滿
470
452
  - ❌ **不混用 rich + compact 在同一 list**(詳「Invariant 1」)— 高度差破壞 row rhythm,prefix 視覺語言衝突。
471
453
  - ❌ **不用 FileItem 做下載進度**(瀏覽器原生下載 UX 已足夠)— FileItem 為 upload narrative 設計,download progress 走自訂元件。
472
454
 
455
+ **常見誤解**:FileItem 該有 hover-bg → 永不(三型態 permanent-anchored,見「Hover 行為 canonical」);status 會染整 row 底色 → 只升階 progress bar / status icon / description(見「Inspector 與矩陣的教學分工」);`surface` 影響進度條 → 進度條只看 `status`(見「可下載狀態 canonical」)。
456
+
457
+ ---
458
+
459
+ ## 邊界案例
460
+
461
+ - **超長檔名**:label 經 `ItemContent` 預設 `truncate` 單行截斷(ellipsis),不換行。
462
+ - **過長 description**:自由換行不截斷(FileItem 未傳 `descriptionClamp`);rich 多行 desc 時 progress bar 隨內容下移並保 8px min gap(見 Rich layout invariant)。
463
+ - **progress 精度**:傳入值直接顯示(rich 的 `%` 文字與 bar 同源),元件不四捨五入;`completed` 強制 100。
464
+ - **RTL**:未特化——compact progress bar offset 用 physical `left/right`(見 `file-item.tsx`),RTL 支援需另案。
465
+ - **Dark mode**:走 semantic token 自動 adapt。無 `disabled` prop(展示型 row,非 form control)。
466
+
473
467
  ---
474
468
 
475
469
  ## Inspector 與矩陣的教學分工
@@ -489,3 +483,9 @@ ColorMatrix 已建:展示 status × 元素(filename / description / progress bar
489
483
  - `../TreeView/tree-view.spec.md` — 階層 file structure 場景
490
484
  - `../ProgressBar/progress-bar.spec.md` — ProgressBar SSOT(FileItem consumes ProgressBar for upload bar)
491
485
  - `../../tokens/color/color.spec.md` — Track 底色(`bg-secondary` 使用原則)
486
+
487
+ ## 被引用(auto-maintained,Dim 3 reciprocal audit)
488
+
489
+ > 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
490
+
491
+ - `file-upload.spec.md`
@@ -86,6 +86,8 @@ export const Overview: Story = {
86
86
  ['accept', 'string', '—', 'MIME filter,支援 .pdf / image/* / application/pdf'],
87
87
  ['maxSize', 'number', '—', '單檔最大 bytes;超過進 onReject'],
88
88
  ['disabled', 'boolean', 'false', '完全停用(語意 token bg-disabled + cursor-not-allowed;互動由 handler isBlocked guard 擋,非 pointer-events-none)'],
89
+ ['variant', "'dropzone' | 'button'", "'dropzone'", '觸發外觀:dropzone 大拖放區(drag + click)/ button 緊湊按鈕(click-only,form-friendly)'],
90
+ ['buttonLabel', 'string', "'Choose file'", "variant='button' 的按鈕文字"],
89
91
  ['title', 'string', "'Click or drag file here to upload'", '預設結構(Empty)的主標題'],
90
92
  ['description', 'string', '單/多檔字串自動切換', '預設結構(Empty)的副標題'],
91
93
  ['children', 'ReactNode', '—', '傳入則整個覆寫預設 Empty 結構(consumer 完全客製)'],
@@ -290,8 +292,8 @@ export const SizeMatrix: Story = {
290
292
  由 Empty 元件 擁有(主檔)(`../Empty/empty.spec.md`)。
291
293
  <br />
292
294
  <strong>空間大小由 consumer 以 wrapper width / min-height 控制</strong>
293
- ——如需窄版擺在表單欄位旁,改用小 Button + hidden input 模式
294
- (見「設計原則 / 何時用大 Dropzone vs 小 Button」)。
295
+ ——如需窄版擺在表單欄位旁,改用 `variant="button"`
296
+ (見「設計原則」UsageGuidance「表單內 inline field」)。
295
297
  </Desc>
296
298
  <div className="overflow-x-auto">
297
299
  <table className="text-caption border-collapse">
@@ -2,7 +2,7 @@
2
2
  import React from 'react'
3
3
  import LinkTo from '@storybook/addon-links/react'
4
4
  import type { Meta, StoryObj } from '@storybook/react'
5
- import { Paperclip, X } from 'lucide-react'
5
+ import { X } from 'lucide-react'
6
6
  import { FileUpload } from './file-upload'
7
7
  import { FileItem } from '@/design-system/components/FileItem/file-item'
8
8
  import { Button } from '@/design-system/components/Button/button'
@@ -64,16 +64,16 @@ export const UsageGuidance: Story = {
64
64
  <p>適合 FileUpload 的真實業務場景(點擊跳轉「展示」頁範例):</p>
65
65
  <ul className="space-y-1">
66
66
  <li>
67
- <LinkTo kind="Design System/Components/FileUpload/展示" name="單檔上傳"><span className="text-primary hover:underline font-medium cursor-pointer">單檔上傳</span></LinkTo>
67
+ <LinkTo kind="Design System/Components/FileUpload/展示" name="單檔上傳"><span className="text-primary hover:underline font-medium cursor-pointer">履歷 / 大頭貼等單檔上傳</span></LinkTo>
68
68
  </li>
69
69
  <li>
70
- <LinkTo kind="Design System/Components/FileUpload/展示" name="批次上傳"><span className="text-primary hover:underline font-medium cursor-pointer">批次上傳</span></LinkTo>
70
+ <LinkTo kind="Design System/Components/FileUpload/展示" name="批次上傳"><span className="text-primary hover:underline font-medium cursor-pointer">相簿照片 / 多檔附件批次上傳</span></LinkTo>
71
71
  </li>
72
72
  <li>
73
- <LinkTo kind="Design System/Components/FileUpload/展示" name="內建 files 屬性"><span className="text-primary hover:underline font-medium cursor-pointer">內建 files 屬性</span></LinkTo>
73
+ <LinkTo kind="Design System/Components/FileUpload/展示" name="內建 files 屬性"><span className="text-primary hover:underline font-medium cursor-pointer">上傳後直接顯示進度清單(內建 files 屬性)</span></LinkTo>
74
74
  </li>
75
75
  <li>
76
- <LinkTo kind="Design System/Components/FileUpload/展示" name="自訂內容"><span className="text-primary hover:underline font-medium cursor-pointer">自訂內容</span></LinkTo>
76
+ <LinkTo kind="Design System/Components/FileUpload/展示" name="自訂內容"><span className="text-primary hover:underline font-medium cursor-pointer">品牌化上傳區 — 自訂 dropzone 內容</span></LinkTo>
77
77
  </li>
78
78
  </ul>
79
79
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見 <code>Vs*Rule</code> stories)。</p>
@@ -127,8 +127,8 @@ export const UsageGuidance: Story = {
127
127
  </Rule>
128
128
 
129
129
  <Rule
130
- title="✅ 表單內 inline field — 用小 Button + hidden input"
131
- note="form 裡和其他 field 並列時,大 dropzone 會破壞欄位節奏。對照:Jira issue 附件按鈕、Stripe 發票上傳欄位——小按鈕不喧賓奪主。"
130
+ title='✅ 表單內 inline field — FileUpload variant="button"'
131
+ note="form 裡和其他 field 並列時,大 dropzone 會破壞欄位節奏;variant=&quot;button&quot; 與 dropzone 共用同一套上傳邏輯(hidden input / accept / maxSize / onReject)。對照:Jira issue 附件按鈕、Stripe 發票上傳欄位——小按鈕不喧賓奪主。"
132
132
  >
133
133
  <div className="flex flex-col gap-3 w-full">
134
134
  <Field>
@@ -139,20 +139,18 @@ export const UsageGuidance: Story = {
139
139
  <FieldLabel>申請人身分證</FieldLabel>
140
140
  <div className="flex items-center gap-2">
141
141
  <Input className="flex-1" placeholder="A123456789" />
142
- <Button variant="tertiary" size="md" startIcon={Paperclip}>
143
- 附上照片
144
- </Button>
142
+ <FileUpload variant="button" buttonLabel="附上照片" accept="image/*" onUpload={noop} />
145
143
  </div>
146
144
  </Field>
147
145
  </div>
148
- <Label>↑ 「附上照片」內部是 `&lt;input type="file" hidden /&gt;` + button.onClick</Label>
146
+ <Label>↑ 「附上照片」= FileUpload variant="button"——與 dropzone 同上傳邏輯,只是觸發外觀緊湊化</Label>
149
147
  </Rule>
150
148
 
151
149
  <Rule
152
150
  title="❌ form 裡塞大 dropzone"
153
151
  note="上下欄位高度落差太大,使用者視覺節奏被打斷——這是 form field 不該出現的體積。"
154
152
  >
155
- <Label warn>⚠️ form 內 inline 上傳欄位,不要用 FileUpload</Label>
153
+ <Label warn>⚠️ form 內 inline 上傳欄位,不要用大 dropzone(改 variant="button")</Label>
156
154
  </Rule>
157
155
  </div>
158
156
  </div>
@@ -1,7 +1,13 @@
1
1
  ---
2
2
  component: FileUpload
3
3
  family: self-contained
4
- variants: {}
4
+ variants:
5
+ dropzone:
6
+ when: "預設 — 大型 dashed 拖放區;「拖放有價值」+ 主要上傳區(drag + click)"
7
+ world-class: ["Ant Upload.Dragger", "Polaris DropZone"]
8
+ button:
9
+ when: "form 內單欄 / 空間省 / 與其他 field 並列 — 緊湊 Button 觸發(click-only)"
10
+ world-class: ["Ant Upload(button list)"]
5
11
  sizes: {}
6
12
  traits:
7
13
  - hasInteractiveStates
@@ -50,12 +56,12 @@ FileUpload 是**拖放 / 點擊上傳區塊**——可拖曳檔案進入或點
50
56
 
51
57
  ## 四狀態視覺(world-class dropzone 慣例)
52
58
 
53
- | data-state | 情境 | 視覺(2026-06-03 修正)|
59
+ | data-state | 情境 | 視覺 |
54
60
  |------------|------|------|
55
61
  | `idle`(預設) | 使用者未互動 | dashed `border-border`(**元件邊框 `--border`,非 `--divider` 分隔線**)+ surface 底 |
56
- | `hover` = `drag-over`(**統一,2026-06-03 Q2-A 純 border-driven,對齊 Ant Dragger `colorPrimaryHover`**)| 游標懸停 / 拖檔進入 | **`border-primary`**(只變邊框)+ 底維持 surface(不變 bg)。state 信號靠邊框,非底色;兩態同視覺 |
57
- | `loading`(**deferred**)| `loading` prop 為 true | **2026-06-03 deferred:其唯一用途(無清單單檔/頭像替換)場景未定義,已從 showcase 移除為 3-state(idle/drag-over/disabled);prop 在 tsx 保留供未來。** 行為:CircularProgress 取代內容;`cursor-progress`(Q4:不加 `pointer-events-none`,互動由 isBlocked guard 擋);aria-busy=true。有清單的 flow 進度一律走 FileItem 自身的 progress bar(uploading status)—— FileUpload 內建 list 仍 `surface=form`(dropzone 非獨立浮層 upload manager,見 file-item.spec.md「upload-manager 浮層面板 composition」)|
58
- | `disabled` | `disabled` prop 為 true | **語意 token(Q3,非 opacity — dashed outline surface 走 DS outline-disabled 慣例,3/4 世界級 Ant/Polaris/Carbon 用 token)**:`bg-disabled` 底 + 邊框不變色 + 文字/icon → `fg-disabled`(由 `<Empty disabled>` 控,icon-circle 維持 muted)+ `cursor-not-allowed`(**Q4 移除 pointer-events-none 後生效**)|
62
+ | `hover` = `drag-over`(**統一,純 border-driven,對齊 Ant Dragger `colorPrimaryHover`**)| 游標懸停 / 拖檔進入 | **`border-primary`**(只變邊框)+ 底維持 surface(不變 bg)。state 信號靠邊框,非底色;兩態同視覺 |
63
+ | `loading`(**deferred**)| `loading` prop 為 true | **Deferred:其唯一用途(無清單單檔/頭像替換)場景未定義,showcase 僅呈現 3-state(idle/drag-over/disabled);prop 在 tsx 保留供未來。** 行為:CircularProgress 取代內容;`cursor-progress`(不加 `pointer-events-none`,互動由 isBlocked guard 擋);aria-busy=true。有清單的 flow 進度一律走 FileItem 自身的 progress bar(uploading status)—— FileUpload 內建 list 仍 `surface=form`(dropzone 非獨立浮層 upload manager,見 file-item.spec.md「upload-manager 浮層面板 composition」)|
64
+ | `disabled` | `disabled` prop 為 true | **語意 token(非 opacity — dashed outline surface 走 DS outline-disabled 慣例,3/4 世界級 Ant/Polaris/Carbon 用 token)**:`bg-disabled` 底 + 邊框不變色 + 文字/icon → `fg-disabled`(由 `<Empty disabled>` 控,icon-circle 維持 muted)+ `cursor-not-allowed`(移除 `pointer-events-none` 後生效)|
59
65
 
60
66
  **State 優先序**:`disabled > loading > drag-over > idle`。disabled 最硬(完全不可用),loading 次之(處理中,user 要等),drag-over 最柔(互動中),idle 預設。
61
67
 
@@ -77,6 +83,8 @@ FileUpload 是**拖放 / 點擊上傳區塊**——可拖曳檔案進入或點
77
83
 
78
84
  **兩 variant 共用** `onUpload` / `onReject` / `accept` / `maxSize` / `files` 清單渲染;**都可放進 `<Field>` control slot**。對齊 Ant `Upload`(button list)vs `Upload.Dragger`(dropzone)。M21 prop-variant test 過(同上傳行為、不同觸發外觀、value 結構同 `File[]`)。
79
85
 
86
+ **vs 直接用 `<Button>`**:Button 只有觸發外觀、無上傳邏輯(hidden input / accept / maxSize / onReject);任何檔案上傳一律 FileUpload,空間緊湊才選 `variant="button"`。
87
+
80
88
  > **FileUpload 常出現在 form**(`*Excel file` 欄位等)—— `variant` 只決定「觸發外觀大小」,**非「form vs 非 form」**;兩者都常在 form 裡。
81
89
 
82
90
  ## children 插槽 vs 預設結構
@@ -95,11 +103,15 @@ FileUpload 是**拖放 / 點擊上傳區塊**——可拖曳檔案進入或點
95
103
  ## Props 行為
96
104
 
97
105
  - `onUpload(files)`: 使用者選取或拖放檔案,**過濾後**回傳 `File[]`
98
- - `onReject(files, reason)`: `maxSize` / `accept` 擋下的檔案(consumer 可據此顯示 Toast / Alert)
106
+ - `onReject(files, reason)`: 被擋下的檔案;`reason` 值域 `'size'`(超過 maxSize)/ `'type'`(不符 accept)。同次選取兩類各觸發一次。rejected 檔案的 UI 展示由 consumer 決定(Toast / Alert / FileItem `status="error"`),FileUpload 本身不渲染拒絕訊息
99
107
  - `multiple`: 預設 `false`(單檔);若 `false` 且使用者拖多檔,**只取第一個**交給 `onUpload`(不拋錯,符合 Ant Design 慣例)
100
108
  - `accept`: MIME filter,同時支援副檔名(`.pdf`)、通配(`image/*`)、完整 MIME(`application/pdf`)
101
109
  - `maxSize`: 單檔最大 bytes;超過靜默忽略(consumer 若要錯誤訊息,監聽 `onReject`)
102
- - `disabled`: 完全停用(互動由 `isBlocked` guard 擋 + `cursor-not-allowed`;Q4 移除 `pointer-events-none` 後不再靠它擋)
110
+ - `disabled`: 完全停用(互動由 `isBlocked` guard 擋 + `cursor-not-allowed`;已移除 `pointer-events-none`,不再靠它擋)
111
+ - `files`: uploaded / uploading 檔案清單(`FileUploadStatus[]`:id / name / size? / progress? / status? / description? / thumbnailSrc?)。傳入 → drop zone 下方渲染列表,每項經 `FileItem`(status 對應:uploading = progress bar / completed = ✓ / error = ✗);不傳 → 不顯示。consumer 持 state(progress / status),FileUpload 只負責渲染
112
+ - `fileListMode`: 清單每項顯示模式;預設 `'compact'`(單行),`'rich'` 含 thumbnail / size / progress bar
113
+ - `onRemove(id)`: 清單移除 callback;有值 → 每項右側顯示 X 移除鈕(ARIA label 由 `removeAriaLabel` 模板客製,預設「移除 {name}」),無 → view-only
114
+ - `variant` / `buttonLabel`: 見「兩種觸發外觀」段
103
115
 
104
116
  ---
105
117
 
@@ -116,6 +128,16 @@ FileUpload 是**拖放 / 點擊上傳區塊**——可拖曳檔案進入或點
116
128
 
117
129
  ---
118
130
 
131
+ ## 邊界案例
132
+
133
+ - **超長檔名**:`files` 清單經 FileItem 渲染,label 單行 truncate(ellipsis)— 詳 `file-item.spec.md`「邊界案例」
134
+ - **多檔拖放 + `multiple=false`**:只取過濾後第一個檔交給 `onUpload`,不拋錯(見「Props 行為」)
135
+ - **空選取(0 檔)**:不觸發任何 callback;`files={[]}` 不渲染清單(等同不傳)
136
+ - **maxSize / accept 全擋下**:`onUpload` 不觸發,只觸發 `onReject`
137
+ - Disabled / loading 見「四狀態視覺」;dark mode / density 由 semantic token 自動處理
138
+
139
+ ---
140
+
119
141
  ## A11y 預設
120
142
 
121
143
  - `role="button"` + `tabIndex=0`(disabled 時 `-1`)
@@ -129,6 +151,13 @@ FileUpload 是**拖放 / 點擊上傳區塊**——可拖曳檔案進入或點
129
151
 
130
152
  - `../FileItem/file-item.spec.md` — 已上傳檔案的 list row 渲染(配對元件)
131
153
  - `../Empty/empty.spec.md` — 本元件預設 children 消費,「icon + title + description」SSOT
132
- - `../Button/button.spec.md` — 小按鈕式上傳的替代方案
154
+ - `../Button/button.spec.md` — `variant="button"` 消費的觸發元件(見「兩種觸發外觀」)
133
155
  - Ant Design `Upload.Dragger` 原始參考 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
134
156
  - Polaris `DropZone` 原始參考 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
157
+
158
+ ## 被引用(auto-maintained,Dim 3 reciprocal audit)
159
+
160
+ > 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
161
+
162
+ - `empty.spec.md`
163
+ - `file-item.spec.md`
@@ -193,7 +193,7 @@ const FileUpload = React.forwardRef<HTMLDivElement, FileUploadProps>(
193
193
  onRemove ? (
194
194
  // Collection remove(per-file)— 不是 dismiss surface,故不套 `dismiss` prop。
195
195
  // 視覺與 dismiss 一致(text variant + fg-muted dim)— 對齊 inline-action.spec.md
196
- // 「Dismiss canonical — X close only」(section L204);L229 明文:onRemove callback 不觸發 dismiss prop。
196
+ // 「Dismiss canonical — X close only」+「dismiss prop 觸發條件」段:onRemove callback 不觸發 dismiss prop。
197
197
  <Button
198
198
  iconOnly
199
199
  variant="text"
@@ -288,7 +288,8 @@ const FileUpload = React.forwardRef<HTMLDivElement, FileUploadProps>(
288
288
  'border-border bg-surface',
289
289
  // hover = drag-over 統一(2026-06-03 Q2-A 純 border-driven,對齊 Ant Dragger colorPrimaryHover):
290
290
  // 兩者都 → primary 邊框,底色維持 surface(不變 bg)。state 信號靠邊框,非底色。
291
- 'hover:border-primary data-[state=drag-over]:border-primary',
291
+ // 2026-06-11 R2(M5 狀態疊加):hover 變 primary 僅限 idle/error —— disabled/loading 維持原邊框(spec「disabled 邊框不變色」)
292
+ 'data-[state=idle]:hover:border-primary data-[state=drag-over]:border-primary',
292
293
  // loading(2026-06-03 Q4:移除 pointer-events-none — 它會讓 cursor-progress 失效;
293
294
  // 互動已由 handleClick + drag/key handlers 的 isBlocked guard 擋,不需 pointer-events-none)
294
295
  'data-[state=loading]:cursor-progress',
@@ -359,7 +360,8 @@ export const fileUploadMeta = {
359
360
  component: 'FileUpload',
360
361
  family: null, // non-family composite / overlay / layout
361
362
  variants: {
362
-
363
+ dropzone: { purpose: '大型 dashed 拖放區(預設)— 拖放有價值 + 主要上傳區(drag + click)' },
364
+ button: { purpose: '緊湊 Button 觸發 — form 內單欄 / 空間省(click-only)' },
363
365
  },
364
366
  sizes: {
365
367
 
@@ -104,7 +104,7 @@ export const Overview: Story = {
104
104
  </div>
105
105
  {/* Viewport + InfoPanel */}
106
106
  <div className="flex-1 min-h-0 flex">
107
- <div className="flex-1 bg-canvas flex items-center justify-center relative">
107
+ <div className="flex-1 bg-overlay flex items-center justify-center relative">
108
108
  <span className="text-body-lg text-fg-secondary">② Viewport — renderer 渲染區(flex-1)</span>
109
109
  <span className="absolute left-6 top-1/2 -translate-y-1/2 text-caption text-fg-muted">← prev</span>
110
110
  <span className="absolute right-6 top-1/2 -translate-y-1/2 text-caption text-fg-muted">next →</span>
@@ -150,7 +150,7 @@ export const Overview: Story = {
150
150
  <tr>
151
151
  <Td mono>Viewport</Td>
152
152
  <Td>永遠顯示</Td>
153
- <Td mono>flex-1, bg-canvas</Td>
153
+ <Td mono>flex-1(無自身 bg;bg-overlay 透出)</Td>
154
154
  <Td>renderer 渲染區;prev/next arrow 絕對定位於左右</Td>
155
155
  </tr>
156
156
  <tr>
@@ -377,7 +377,7 @@ export const Inspector: Story = {
377
377
  <tr>
378
378
  <Td>Viewport 背景</Td>
379
379
  <Td>
380
- <TokenCell token="--canvas" display="bg-canvas" />
380
+ <TokenCell token="--overlay" display="bg-overlay 透出(自身無 bg)" />
381
381
  </Td>
382
382
  </tr>
383
383
  <tr>
@@ -467,7 +467,7 @@ export const ColorMatrix: Story = {
467
467
  <H3>Chrome 鎖 dark 的理由</H3>
468
468
  <Desc>
469
469
  FileViewer 是「內容展示 chrome」,viewer 本身是獨立沉浸式 context——類比 Tooltip 永久 dark、
470
- 全螢幕影片播放器 UI 永遠暗色。Dark 底 + bg-canvas 讓圖片 / media 顯色最自然
470
+ 全螢幕影片播放器 UI 永遠暗色。Dark 底 + 半透明 bg-overlay backdrop 讓圖片 / media 顯色最自然
471
471
  (亮底會跟任何含白色邊緣的圖片打架);rollback 到 light theme 會讓圖片與 chrome 產生
472
472
  色溫衝突。實作:DialogPrimitive.Content 內包 &lt;div data-theme="dark"&gt;,子元件 token
473
473
  自動解析為 dark 值,不影響背景頁面 theme。
@@ -488,9 +488,9 @@ export const ColorMatrix: Story = {
488
488
  <div className="flex">
489
489
  <div
490
490
  className="flex-1 flex items-center justify-center"
491
- style={{ height: 180, backgroundColor: 'var(--canvas)' }}
491
+ style={{ height: 180, backgroundColor: 'var(--overlay)' }}
492
492
  >
493
- <span className="text-body text-fg-secondary">Viewport — bg-canvas</span>
493
+ <span className="text-body text-fg-secondary">Viewport — 無自身 bg(bg-overlay 透出)</span>
494
494
  </div>
495
495
  <aside className="w-56 bg-surface-raised border-l border-divider flex items-center justify-center">
496
496
  <span className="text-body text-fg-secondary">InfoPanel — bg-surface-raised</span>
@@ -525,9 +525,9 @@ export const ColorMatrix: Story = {
525
525
  <tr>
526
526
  <Td>Viewport 背景</Td>
527
527
  <Td>
528
- <TokenCell token="--canvas" display="bg-canvas" />
528
+ <TokenCell token="--overlay" display="bg-overlay 透出(自身無 bg)" />
529
529
  </Td>
530
- <Td>近黑底,讓 media 顯色</Td>
530
+ <Td>viewer 自身不畫 viewport bg;半透明遮罩透出,media 周圍近黑</Td>
531
531
  </tr>
532
532
  <tr>
533
533
  <Td>Toolbar / InfoPanel / Filmstrip</Td>
@@ -813,14 +813,19 @@ export const StateBehavior: Story = {
813
813
  </tr>
814
814
  <tr>
815
815
  <Td>Zoom in</Td>
816
- <Td mono>滾輪 / + / = / ZoomInput</Td>
816
+ <Td mono>+ / = / 按鈕</Td>
817
817
  <Td>下一個 preset(10/25/50/75/100/125/150/200/400)</Td>
818
818
  </tr>
819
819
  <tr>
820
820
  <Td>Zoom out</Td>
821
- <Td mono>反向滾輪 / -</Td>
821
+ <Td mono>- / − 按鈕</Td>
822
822
  <Td>上一個 preset</Td>
823
823
  </tr>
824
+ <tr>
825
+ <Td>連續 / 任意值</Td>
826
+ <Td mono>滾輪(step 0.03)/ ZoomInput 打字</Td>
827
+ <Td>multiplicative 連續縮放,任意 %(非 preset;spec「Wheel step canonical」);ZoomInput preset menu 才跳 preset</Td>
828
+ </tr>
824
829
  <tr>
825
830
  <Td>Reset 100%</Td>
826
831
  <Td mono>0 key</Td>
@@ -1116,7 +1121,7 @@ export const Accessibility = {
1116
1121
  render: () => (
1117
1122
  <div className="max-w-3xl text-body text-fg-secondary">
1118
1123
  <h3 className="text-h5 text-foreground mb-2">無障礙設計</h3>
1119
- <p className="whitespace-pre-line">{"詳 `fileviewer.spec.md` 「A11y 預設」段。摘要:\n\nRadix DialogPrimitive 自動處理:\n- role=\"dialog\" + aria-modal=\"true\" \n- <DialogPrimitive.Title> (sr-only)自動成 aria-labelledby 目標——screen reader 開啟時讀「檔案檢視器:{file.name}」\n- Focus trap:焦點鎖在 viewer 內\n- Esc 關閉(Radix DismissableLayer);Backdrop click 關閉為自寫 geometric onClick handler(非 Radix outside-click — Content 為 fixed inset-0 全螢幕覆蓋,判斷 click 落在 img rect 外才關)\n\n自加的 a11y:\n- 所有 iconOnly button 皆有 aria-label (中文,跟 DS 其他元件風格一致)\n- Filmstrip role=\"group\" + thumb <button> + aria-current (非 tablist:選圖導航非切 tabpanel)\n- InfoPanel 用 <aside aria-label=\""}</p>
1124
+ <p className="whitespace-pre-line">{"詳 `file-viewer.spec.md` 「A11y 預設」段。摘要:\n\nRadix DialogPrimitive 自動處理:\n- role=\"dialog\" + aria-modal=\"true\" \n- <DialogPrimitive.Title> (sr-only)自動成 aria-labelledby 目標——screen reader 開啟時讀「檔案檢視器:{file.name}」\n- Focus trap:焦點鎖在 viewer 內\n- Esc 關閉(Radix DismissableLayer);Backdrop click 關閉為自寫 geometric onClick handler(非 Radix outside-click — Content 為 fixed inset-0 全螢幕覆蓋,判斷 click 落在 img rect 外才關)\n\n自加的 a11y:\n- 所有 iconOnly button 皆有 aria-label (中文,跟 DS 其他元件風格一致)\n- Filmstrip role=\"group\" + thumb <button> + aria-current (非 tablist:選圖導航非切 tabpanel)\n- InfoPanel 用 <aside aria-label=\"檔案詳細資訊\"> 語意標記\n- onOpenAutoFocus preventDefault:避免焦點自動跑進第一個 tabbable(讓鍵盤從 viewport 開始)"}</p>
1120
1125
  </div>
1121
1126
  ),
1122
1127
  }