@qijenchen/design-system 0.1.0-beta.80 → 0.1.0-beta.82

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 (548) hide show
  1. package/CLAUDE.md +1 -1
  2. package/dist/components/Alert/alert.d.ts +5 -0
  3. package/dist/components/Alert/alert.d.ts.map +1 -1
  4. package/dist/components/Alert/alert.js +4 -1
  5. package/dist/components/Alert/alert.js.map +1 -1
  6. package/dist/components/AppShell/app-shell.d.ts +1 -1
  7. package/dist/components/AppShell/app-shell.js.map +1 -1
  8. package/dist/components/AspectRatio/aspect-ratio.d.ts +2 -1
  9. package/dist/components/AspectRatio/aspect-ratio.d.ts.map +1 -1
  10. package/dist/components/AspectRatio/aspect-ratio.js +1 -1
  11. package/dist/components/AspectRatio/aspect-ratio.js.map +1 -1
  12. package/dist/components/Avatar/avatar.d.ts +3 -2
  13. package/dist/components/Avatar/avatar.d.ts.map +1 -1
  14. package/dist/components/Avatar/avatar.js +8 -3
  15. package/dist/components/Avatar/avatar.js.map +1 -1
  16. package/dist/components/Badge/badge.d.ts.map +1 -1
  17. package/dist/components/Badge/badge.js +2 -1
  18. package/dist/components/Badge/badge.js.map +1 -1
  19. package/dist/components/Breadcrumb/breadcrumb.d.ts +14 -1
  20. package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
  21. package/dist/components/Breadcrumb/breadcrumb.js +9 -3
  22. package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
  23. package/dist/components/BulkActionBar/bulk-action-bar.d.ts +4 -1
  24. package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
  25. package/dist/components/BulkActionBar/bulk-action-bar.js +2 -2
  26. package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
  27. package/dist/components/Button/button.d.ts +4 -4
  28. package/dist/components/Button/button.d.ts.map +1 -1
  29. package/dist/components/Button/button.js +73 -44
  30. package/dist/components/Button/button.js.map +1 -1
  31. package/dist/components/Button/index.d.ts +1 -0
  32. package/dist/components/Button/index.d.ts.map +1 -1
  33. package/dist/components/Button/index.js +3 -0
  34. package/dist/components/Button/index.js.map +1 -1
  35. package/dist/components/Calendar/calendar.d.ts +3 -1
  36. package/dist/components/Calendar/calendar.d.ts.map +1 -1
  37. package/dist/components/Calendar/calendar.js +40 -17
  38. package/dist/components/Calendar/calendar.js.map +1 -1
  39. package/dist/components/Carousel/carousel.d.ts +1 -1
  40. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  41. package/dist/components/Carousel/carousel.js +3 -1
  42. package/dist/components/Carousel/carousel.js.map +1 -1
  43. package/dist/components/Chart/chart.d.ts.map +1 -1
  44. package/dist/components/Chart/chart.js +1 -1
  45. package/dist/components/Chart/chart.js.map +1 -1
  46. package/dist/components/Checkbox/checkbox.d.ts +6 -3
  47. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  48. package/dist/components/Checkbox/checkbox.js +26 -4
  49. package/dist/components/Checkbox/checkbox.js.map +1 -1
  50. package/dist/components/Checkbox/index.d.ts +1 -0
  51. package/dist/components/Checkbox/index.d.ts.map +1 -1
  52. package/dist/components/Checkbox/index.js +3 -0
  53. package/dist/components/Checkbox/index.js.map +1 -1
  54. package/dist/components/Chip/chip.d.ts +9 -3
  55. package/dist/components/Chip/chip.d.ts.map +1 -1
  56. package/dist/components/Chip/chip.js +46 -13
  57. package/dist/components/Chip/chip.js.map +1 -1
  58. package/dist/components/Coachmark/coachmark.d.ts +5 -3
  59. package/dist/components/Coachmark/coachmark.d.ts.map +1 -1
  60. package/dist/components/Coachmark/coachmark.js +10 -7
  61. package/dist/components/Coachmark/coachmark.js.map +1 -1
  62. package/dist/components/Combobox/combobox.d.ts +3 -1
  63. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  64. package/dist/components/Combobox/combobox.js +11 -7
  65. package/dist/components/Combobox/combobox.js.map +1 -1
  66. package/dist/components/Command/command.d.ts +2 -2
  67. package/dist/components/Command/command.d.ts.map +1 -1
  68. package/dist/components/Command/command.js +4 -2
  69. package/dist/components/Command/command.js.map +1 -1
  70. package/dist/components/DataTable/active-editor-controller.d.ts +4 -3
  71. package/dist/components/DataTable/active-editor-controller.d.ts.map +1 -1
  72. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  73. package/dist/components/DataTable/cell-registry.js +3 -0
  74. package/dist/components/DataTable/cell-registry.js.map +1 -1
  75. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  76. package/dist/components/DataTable/data-table-interaction-layer.d.ts +4 -3
  77. package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
  78. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  79. package/dist/components/DataTable/data-table.d.ts +38 -7
  80. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  81. package/dist/components/DataTable/data-table.js +213 -63
  82. package/dist/components/DataTable/data-table.js.map +1 -1
  83. package/dist/components/DataTable/filter-tree.d.ts +5 -0
  84. package/dist/components/DataTable/filter-tree.d.ts.map +1 -1
  85. package/dist/components/DateGrid/date-grid.d.ts +3 -3
  86. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  87. package/dist/components/DateGrid/date-grid.js +52 -6
  88. package/dist/components/DateGrid/date-grid.js.map +1 -1
  89. package/dist/components/DatePicker/date-picker.d.ts +2 -8
  90. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  91. package/dist/components/DatePicker/date-picker.js +139 -109
  92. package/dist/components/DatePicker/date-picker.js.map +1 -1
  93. package/dist/components/DescriptionList/description-list.d.ts.map +1 -1
  94. package/dist/components/DescriptionList/description-list.js +1 -1
  95. package/dist/components/DescriptionList/description-list.js.map +1 -1
  96. package/dist/components/Dialog/dialog.d.ts +1 -1
  97. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  98. package/dist/components/Dialog/dialog.js +2 -1
  99. package/dist/components/Dialog/dialog.js.map +1 -1
  100. package/dist/components/DropdownMenu/dropdown-menu.d.ts +4 -4
  101. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  102. package/dist/components/DropdownMenu/dropdown-menu.js +25 -8
  103. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  104. package/dist/components/Empty/empty.d.ts.map +1 -1
  105. package/dist/components/Empty/empty.js +1 -1
  106. package/dist/components/Empty/empty.js.map +1 -1
  107. package/dist/components/Field/field-wrapper.d.ts +1 -0
  108. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  109. package/dist/components/Field/field-wrapper.js +27 -7
  110. package/dist/components/Field/field-wrapper.js.map +1 -1
  111. package/dist/components/Field/field.d.ts +1 -1
  112. package/dist/components/Field/field.d.ts.map +1 -1
  113. package/dist/components/Field/field.js +2 -1
  114. package/dist/components/Field/field.js.map +1 -1
  115. package/dist/components/Field/use-form-validation.d.ts +5 -1
  116. package/dist/components/Field/use-form-validation.d.ts.map +1 -1
  117. package/dist/components/Field/use-form-validation.js +31 -21
  118. package/dist/components/Field/use-form-validation.js.map +1 -1
  119. package/dist/components/FieldControlGroup/field-control-group.d.ts +3 -3
  120. package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
  121. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  122. package/dist/components/FileItem/file-item.js +3 -2
  123. package/dist/components/FileItem/file-item.js.map +1 -1
  124. package/dist/components/FileUpload/file-upload.d.ts +1 -1
  125. package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
  126. package/dist/components/FileUpload/file-upload.js +22 -10
  127. package/dist/components/FileUpload/file-upload.js.map +1 -1
  128. package/dist/components/FileViewer/can-render-image.d.ts +6 -0
  129. package/dist/components/FileViewer/can-render-image.d.ts.map +1 -0
  130. package/dist/components/FileViewer/can-render-image.js +11 -0
  131. package/dist/components/FileViewer/can-render-image.js.map +1 -0
  132. package/dist/components/FileViewer/file-viewer.d.ts +2 -2
  133. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  134. package/dist/components/FileViewer/file-viewer.js +24 -16
  135. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  136. package/dist/components/FileViewer/image-renderer.d.ts +0 -5
  137. package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
  138. package/dist/components/FileViewer/image-renderer.js +1 -9
  139. package/dist/components/FileViewer/image-renderer.js.map +1 -1
  140. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  141. package/dist/components/HoverCard/hover-card.js +3 -1
  142. package/dist/components/HoverCard/hover-card.js.map +1 -1
  143. package/dist/components/Input/input.d.ts +26 -3
  144. package/dist/components/Input/input.d.ts.map +1 -1
  145. package/dist/components/Input/input.js +12 -8
  146. package/dist/components/Input/input.js.map +1 -1
  147. package/dist/components/LinkInput/link-input.d.ts +3 -3
  148. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  149. package/dist/components/LinkInput/link-input.js +29 -28
  150. package/dist/components/LinkInput/link-input.js.map +1 -1
  151. package/dist/components/Menu/menu-item.d.ts.map +1 -1
  152. package/dist/components/Menu/menu-item.js +5 -4
  153. package/dist/components/Menu/menu-item.js.map +1 -1
  154. package/dist/components/Notice/notice.d.ts +5 -0
  155. package/dist/components/Notice/notice.d.ts.map +1 -1
  156. package/dist/components/Notice/notice.js +11 -5
  157. package/dist/components/Notice/notice.js.map +1 -1
  158. package/dist/components/NumberInput/number-input.d.ts +1 -1
  159. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  160. package/dist/components/NumberInput/number-input.js +17 -8
  161. package/dist/components/NumberInput/number-input.js.map +1 -1
  162. package/dist/components/OverflowIndicator/overflow-indicator.d.ts +1 -1
  163. package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
  164. package/dist/components/OverflowIndicator/overflow-indicator.js +2 -1
  165. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  166. package/dist/components/Pagination/index.d.ts +2 -0
  167. package/dist/components/Pagination/index.d.ts.map +1 -0
  168. package/dist/components/Pagination/index.js +6 -0
  169. package/dist/components/Pagination/index.js.map +1 -0
  170. package/dist/components/Pagination/pagination.d.ts +49 -0
  171. package/dist/components/Pagination/pagination.d.ts.map +1 -0
  172. package/dist/components/Pagination/pagination.js +182 -0
  173. package/dist/components/Pagination/pagination.js.map +1 -0
  174. package/dist/components/PeoplePicker/people-picker.d.ts +3 -3
  175. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  176. package/dist/components/PeoplePicker/people-picker.js +38 -36
  177. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  178. package/dist/components/PeoplePicker/person-display.d.ts +4 -0
  179. package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
  180. package/dist/components/PeoplePicker/person-display.js +3 -3
  181. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  182. package/dist/components/Popover/popover.d.ts.map +1 -1
  183. package/dist/components/Popover/popover.js +58 -32
  184. package/dist/components/Popover/popover.js.map +1 -1
  185. package/dist/components/ProfileCard/profile-card.d.ts +11 -3
  186. package/dist/components/ProfileCard/profile-card.d.ts.map +1 -1
  187. package/dist/components/ProfileCard/profile-card.js +14 -4
  188. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  189. package/dist/components/RadioGroup/radio-group.d.ts +1 -1
  190. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  191. package/dist/components/RadioGroup/radio-group.js +28 -3
  192. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  193. package/dist/components/Rating/rating.js +3 -3
  194. package/dist/components/Rating/rating.js.map +1 -1
  195. package/dist/components/ScrollArea/scroll-area.d.ts +1 -1
  196. package/dist/components/ScrollArea/scroll-area.js +2 -2
  197. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  198. package/dist/components/SegmentedControl/segmented-control.d.ts +2 -2
  199. package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
  200. package/dist/components/SegmentedControl/segmented-control.js +23 -10
  201. package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
  202. package/dist/components/Select/select.d.ts +10 -4
  203. package/dist/components/Select/select.d.ts.map +1 -1
  204. package/dist/components/Select/select.js +57 -14
  205. package/dist/components/Select/select.js.map +1 -1
  206. package/dist/components/SelectMenu/index.js +4 -2
  207. package/dist/components/SelectMenu/select-menu.d.ts +31 -6
  208. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  209. package/dist/components/SelectMenu/select-menu.js +169 -85
  210. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  211. package/dist/components/Separator/separator.js +1 -1
  212. package/dist/components/Separator/separator.js.map +1 -1
  213. package/dist/components/Sheet/sheet.d.ts +1 -1
  214. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  215. package/dist/components/Sheet/sheet.js +3 -2
  216. package/dist/components/Sheet/sheet.js.map +1 -1
  217. package/dist/components/Sidebar/sidebar.d.ts +2 -2
  218. package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
  219. package/dist/components/Sidebar/sidebar.js +28 -10
  220. package/dist/components/Sidebar/sidebar.js.map +1 -1
  221. package/dist/components/Skeleton/skeleton.d.ts +1 -1
  222. package/dist/components/Skeleton/skeleton.js +2 -2
  223. package/dist/components/Skeleton/skeleton.js.map +1 -1
  224. package/dist/components/Slider/slider.d.ts +1 -1
  225. package/dist/components/Slider/slider.d.ts.map +1 -1
  226. package/dist/components/Slider/slider.js +7 -3
  227. package/dist/components/Slider/slider.js.map +1 -1
  228. package/dist/components/Steps/steps.d.ts.map +1 -1
  229. package/dist/components/Steps/steps.js +6 -3
  230. package/dist/components/Steps/steps.js.map +1 -1
  231. package/dist/components/Switch/switch.d.ts +2 -2
  232. package/dist/components/Switch/switch.d.ts.map +1 -1
  233. package/dist/components/Switch/switch.js +47 -9
  234. package/dist/components/Switch/switch.js.map +1 -1
  235. package/dist/components/Tabs/tabs.d.ts +8 -5
  236. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  237. package/dist/components/Tabs/tabs.js +45 -33
  238. package/dist/components/Tabs/tabs.js.map +1 -1
  239. package/dist/components/Tag/tag.d.ts.map +1 -1
  240. package/dist/components/Tag/tag.js +20 -4
  241. package/dist/components/Tag/tag.js.map +1 -1
  242. package/dist/components/Textarea/textarea.d.ts +2 -1
  243. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  244. package/dist/components/Textarea/textarea.js +24 -13
  245. package/dist/components/Textarea/textarea.js.map +1 -1
  246. package/dist/components/TimePicker/time-picker.d.ts +2 -2
  247. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  248. package/dist/components/TimePicker/time-picker.js +23 -12
  249. package/dist/components/TimePicker/time-picker.js.map +1 -1
  250. package/dist/components/Toast/toast.d.ts +1 -1
  251. package/dist/components/Toast/toast.d.ts.map +1 -1
  252. package/dist/components/Toast/toast.js +7 -3
  253. package/dist/components/Toast/toast.js.map +1 -1
  254. package/dist/components/Tooltip/tooltip.js +1 -1
  255. package/dist/components/Tooltip/tooltip.js.map +1 -1
  256. package/dist/components/TreeView/tree-view.d.ts +1 -1
  257. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  258. package/dist/components/TreeView/tree-view.js +11 -5
  259. package/dist/components/TreeView/tree-view.js.map +1 -1
  260. package/dist/hooks/use-controllable.d.ts.map +1 -1
  261. package/dist/hooks/use-controllable.js +4 -2
  262. package/dist/hooks/use-controllable.js.map +1 -1
  263. package/dist/hooks/use-is-touch-device.d.ts.map +1 -1
  264. package/dist/hooks/use-is-touch-device.js +3 -1
  265. package/dist/hooks/use-is-touch-device.js.map +1 -1
  266. package/dist/index.d.ts +2 -0
  267. package/dist/index.d.ts.map +1 -1
  268. package/dist/index.js +14 -0
  269. package/dist/index.js.map +1 -1
  270. package/dist/patterns/element-anatomy/item-anatomy.d.ts +2 -2
  271. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  272. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  273. package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
  274. package/dist/tokens/categorical-color.d.ts +1 -1
  275. package/dist/tokens/categorical-color.js.map +1 -1
  276. package/dist/tokens/uiSize/icon-size.d.ts +2 -2
  277. package/ds-canonical/fork/governance.lock +1 -1
  278. package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +10 -1
  279. package/ds-canonical/hooks/check_audit_post_report_validator.sh +13 -0
  280. package/ds-canonical/hooks/check_full_story_visual_interaction_sweep.sh +4 -3
  281. package/ds-canonical/hooks/check_opacity_token_usage.sh +10 -1
  282. package/ds-canonical/hooks/check_plugin_fork_health.sh +30 -22
  283. package/ds-canonical/hooks/check_story_invariants.sh +17 -5
  284. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +5 -2
  285. package/ds-canonical/references/principle-dim-map.json +100 -0
  286. package/ds-canonical/references/story-baseline-registry.json +4 -3
  287. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +1 -1
  288. package/ds-canonical/skills/design-system-audit/SKILL.md +2 -2
  289. package/ds-story-manifest.json +41 -18
  290. package/llms-full.txt +11 -1
  291. package/llms.txt +3 -1
  292. package/package.json +1 -1
  293. package/src/components/Alert/alert.anatomy.stories.tsx +9 -6
  294. package/src/components/Alert/alert.spec.md +3 -3
  295. package/src/components/Alert/alert.tsx +9 -1
  296. package/src/components/AppShell/app-shell.anatomy.stories.tsx +3 -0
  297. package/src/components/AppShell/app-shell.spec.md +3 -2
  298. package/src/components/AppShell/app-shell.tsx +1 -1
  299. package/src/components/AspectRatio/aspect-ratio.spec.md +1 -2
  300. package/src/components/AspectRatio/aspect-ratio.tsx +3 -2
  301. package/src/components/Avatar/avatar.principles.stories.tsx +1 -1
  302. package/src/components/Avatar/avatar.spec.md +10 -30
  303. package/src/components/Avatar/avatar.stories.tsx +1 -1
  304. package/src/components/Avatar/avatar.tsx +30 -16
  305. package/src/components/Badge/badge.anatomy.stories.tsx +2 -2
  306. package/src/components/Badge/badge.spec.md +5 -4
  307. package/src/components/Badge/badge.tsx +4 -1
  308. package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +2 -2
  309. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +15 -10
  310. package/src/components/Breadcrumb/breadcrumb.spec.md +5 -3
  311. package/src/components/Breadcrumb/breadcrumb.tsx +10 -4
  312. package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +3 -2
  313. package/src/components/BulkActionBar/bulk-action-bar.spec.md +15 -14
  314. package/src/components/BulkActionBar/bulk-action-bar.tsx +10 -4
  315. package/src/components/Button/button.anatomy.stories.tsx +6 -6
  316. package/src/components/Button/button.spec.md +3 -2
  317. package/src/components/Button/button.tsx +67 -27
  318. package/src/components/Button/index.ts +1 -0
  319. package/src/components/Calendar/calendar.anatomy.stories.tsx +5 -3
  320. package/src/components/Calendar/calendar.principles.stories.tsx +3 -1
  321. package/src/components/Calendar/calendar.spec.md +9 -2
  322. package/src/components/Calendar/calendar.stories.tsx +3 -1
  323. package/src/components/Calendar/calendar.tsx +53 -19
  324. package/src/components/Carousel/carousel.principles.stories.tsx +1 -1
  325. package/src/components/Carousel/carousel.tsx +4 -1
  326. package/src/components/Chart/chart.spec.md +1 -1
  327. package/src/components/Chart/chart.tsx +2 -1
  328. package/src/components/Checkbox/checkbox.principles.stories.tsx +1 -1
  329. package/src/components/Checkbox/checkbox.spec.md +1 -1
  330. package/src/components/Checkbox/checkbox.tsx +41 -7
  331. package/src/components/Checkbox/index.ts +1 -0
  332. package/src/components/Chip/chip.anatomy.stories.tsx +9 -9
  333. package/src/components/Chip/chip.principles.stories.tsx +3 -3
  334. package/src/components/Chip/chip.spec.md +7 -4
  335. package/src/components/Chip/chip.tsx +60 -15
  336. package/src/components/CircularProgress/circular-progress.anatomy.stories.tsx +1 -1
  337. package/src/components/CircularProgress/circular-progress.principles.stories.tsx +7 -5
  338. package/src/components/CircularProgress/circular-progress.spec.md +2 -0
  339. package/src/components/CircularProgress/circular-progress.stories.tsx +3 -11
  340. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +2 -2
  341. package/src/components/Coachmark/coachmark.principles.stories.tsx +1 -1
  342. package/src/components/Coachmark/coachmark.spec.md +3 -3
  343. package/src/components/Coachmark/coachmark.tsx +23 -13
  344. package/src/components/Combobox/combobox.anatomy.stories.tsx +4 -4
  345. package/src/components/Combobox/combobox.spec.md +1 -1
  346. package/src/components/Combobox/combobox.tsx +30 -17
  347. package/src/components/Command/command.principles.stories.tsx +10 -12
  348. package/src/components/Command/command.spec.md +5 -5
  349. package/src/components/Command/command.stories.tsx +4 -4
  350. package/src/components/Command/command.tsx +3 -2
  351. package/src/components/DataTable/active-editor-controller.ts +4 -3
  352. package/src/components/DataTable/cell-registry.tsx +8 -0
  353. package/src/components/DataTable/data-table-filter-panel.tsx +8 -2
  354. package/src/components/DataTable/data-table-interaction-layer.tsx +8 -7
  355. package/src/components/DataTable/data-table.anatomy.stories.tsx +4 -4
  356. package/src/components/DataTable/data-table.css +2 -2
  357. package/src/components/DataTable/data-table.spec.md +72 -115
  358. package/src/components/DataTable/data-table.stories.tsx +51 -3
  359. package/src/components/DataTable/data-table.tsx +348 -63
  360. package/src/components/DataTable/filter-operators.spec.md +4 -4
  361. package/src/components/DataTable/filter-tree.ts +192 -42
  362. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +1 -1
  363. package/src/components/DateGrid/date-grid.spec.md +11 -8
  364. package/src/components/DateGrid/date-grid.tsx +63 -8
  365. package/src/components/DatePicker/date-picker.principles.stories.tsx +1 -1
  366. package/src/components/DatePicker/date-picker.spec.md +1 -0
  367. package/src/components/DatePicker/date-picker.stories.tsx +19 -0
  368. package/src/components/DatePicker/date-picker.tsx +52 -15
  369. package/src/components/DescriptionList/description-list.spec.md +1 -1
  370. package/src/components/DescriptionList/description-list.tsx +3 -1
  371. package/src/components/Dialog/dialog.spec.md +1 -1
  372. package/src/components/Dialog/dialog.tsx +2 -1
  373. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +3 -1
  374. package/src/components/DropdownMenu/dropdown-menu.spec.md +8 -5
  375. package/src/components/DropdownMenu/dropdown-menu.tsx +33 -7
  376. package/src/components/Empty/empty.anatomy.stories.tsx +1 -1
  377. package/src/components/Empty/empty.spec.md +2 -2
  378. package/src/components/Empty/empty.stories.tsx +4 -15
  379. package/src/components/Empty/empty.tsx +4 -1
  380. package/src/components/Field/field-controls.spec.md +2 -1
  381. package/src/components/Field/field-wrapper.tsx +28 -8
  382. package/src/components/Field/field.anatomy.stories.tsx +7 -7
  383. package/src/components/Field/field.principles.stories.tsx +1 -1
  384. package/src/components/Field/field.spec.md +9 -7
  385. package/src/components/Field/field.tsx +2 -1
  386. package/src/components/Field/form-validation.spec.md +6 -3
  387. package/src/components/Field/use-form-validation.ts +49 -21
  388. package/src/components/FieldControlGroup/field-control-group.anatomy.stories.tsx +11 -10
  389. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
  390. package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
  391. package/src/components/FieldControlGroup/field-control-group.tsx +3 -3
  392. package/src/components/FileItem/file-item.anatomy.stories.tsx +6 -4
  393. package/src/components/FileItem/file-item.tsx +3 -2
  394. package/src/components/FileUpload/file-upload.anatomy.stories.tsx +1 -1
  395. package/src/components/FileUpload/file-upload.spec.md +4 -1
  396. package/src/components/FileUpload/file-upload.tsx +36 -10
  397. package/src/components/FileViewer/can-render-image.ts +15 -0
  398. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +6 -4
  399. package/src/components/FileViewer/file-viewer.spec.md +2 -2
  400. package/src/components/FileViewer/file-viewer.tsx +47 -21
  401. package/src/components/FileViewer/image-renderer.tsx +3 -8
  402. package/src/components/HoverCard/hover-card.spec.md +1 -1
  403. package/src/components/HoverCard/hover-card.tsx +3 -1
  404. package/src/components/Input/input.anatomy.stories.tsx +4 -4
  405. package/src/components/Input/input.principles.stories.tsx +1 -1
  406. package/src/components/Input/input.spec.md +1 -1
  407. package/src/components/Input/input.stories.tsx +11 -5
  408. package/src/components/Input/input.tsx +11 -9
  409. package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -0
  410. package/src/components/LinkInput/link-input.tsx +49 -34
  411. package/src/components/Menu/menu-item.anatomy.stories.tsx +1 -1
  412. package/src/components/Menu/menu-item.spec.md +4 -3
  413. package/src/components/Menu/menu-item.tsx +15 -4
  414. package/src/components/Notice/notice.anatomy.stories.tsx +10 -5
  415. package/src/components/Notice/notice.spec.md +4 -3
  416. package/src/components/Notice/notice.tsx +22 -5
  417. package/src/components/NumberInput/number-input.anatomy.stories.tsx +5 -4
  418. package/src/components/NumberInput/number-input.principles.stories.tsx +2 -1
  419. package/src/components/NumberInput/number-input.spec.md +2 -1
  420. package/src/components/NumberInput/number-input.tsx +25 -9
  421. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +3 -3
  422. package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
  423. package/src/components/OverflowIndicator/overflow-indicator.tsx +2 -1
  424. package/src/components/Pagination/index.ts +5 -0
  425. package/src/components/Pagination/pagination.anatomy.stories.tsx +143 -0
  426. package/src/components/Pagination/pagination.principles.stories.tsx +81 -0
  427. package/src/components/Pagination/pagination.spec.md +150 -0
  428. package/src/components/Pagination/pagination.stories.tsx +98 -0
  429. package/src/components/Pagination/pagination.tsx +263 -0
  430. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +1 -1
  431. package/src/components/PeoplePicker/people-picker.spec.md +4 -4
  432. package/src/components/PeoplePicker/people-picker.stories.tsx +1 -1
  433. package/src/components/PeoplePicker/people-picker.tsx +65 -55
  434. package/src/components/PeoplePicker/person-display.tsx +10 -4
  435. package/src/components/Popover/popover.anatomy.stories.tsx +2 -2
  436. package/src/components/Popover/popover.spec.md +13 -14
  437. package/src/components/Popover/popover.tsx +88 -39
  438. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +4 -4
  439. package/src/components/ProfileCard/profile-card.principles.stories.tsx +2 -2
  440. package/src/components/ProfileCard/profile-card.spec.md +1 -2
  441. package/src/components/ProfileCard/profile-card.stories.tsx +1 -1
  442. package/src/components/ProfileCard/profile-card.tsx +29 -8
  443. package/src/components/RadioGroup/radio-group.principles.stories.tsx +1 -1
  444. package/src/components/RadioGroup/radio-group.spec.md +2 -2
  445. package/src/components/RadioGroup/radio-group.stories.tsx +3 -1
  446. package/src/components/RadioGroup/radio-group.tsx +37 -3
  447. package/src/components/Rating/rating.anatomy.stories.tsx +11 -8
  448. package/src/components/Rating/rating.principles.stories.tsx +1 -1
  449. package/src/components/Rating/rating.spec.md +2 -2
  450. package/src/components/Rating/rating.stories.tsx +1 -1
  451. package/src/components/Rating/rating.tsx +3 -3
  452. package/src/components/ScrollArea/scroll-area.spec.md +1 -0
  453. package/src/components/ScrollArea/scroll-area.tsx +1 -1
  454. package/src/components/SegmentedControl/segmented-control.anatomy.stories.tsx +3 -3
  455. package/src/components/SegmentedControl/segmented-control.spec.md +7 -3
  456. package/src/components/SegmentedControl/segmented-control.tsx +29 -11
  457. package/src/components/Select/select.anatomy.stories.tsx +2 -2
  458. package/src/components/Select/select.spec.md +3 -2
  459. package/src/components/Select/select.tsx +62 -19
  460. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +1 -1
  461. package/src/components/SelectMenu/select-menu.principles.stories.tsx +1 -1
  462. package/src/components/SelectMenu/select-menu.spec.md +12 -8
  463. package/src/components/SelectMenu/select-menu.tsx +176 -20
  464. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
  465. package/src/components/SelectionControl/selection-item.principles.stories.tsx +2 -2
  466. package/src/components/SelectionControl/selection-item.spec.md +2 -1
  467. package/src/components/SelectionControl/selection-item.stories.tsx +1 -1
  468. package/src/components/Separator/separator.anatomy.stories.tsx +2 -2
  469. package/src/components/Separator/separator.tsx +1 -1
  470. package/src/components/Sheet/sheet.spec.md +2 -1
  471. package/src/components/Sheet/sheet.tsx +4 -2
  472. package/src/components/Sidebar/sidebar.anatomy.stories.tsx +1 -1
  473. package/src/components/Sidebar/sidebar.spec.md +2 -1
  474. package/src/components/Sidebar/sidebar.stories.tsx +6 -4
  475. package/src/components/Sidebar/sidebar.tsx +41 -13
  476. package/src/components/Skeleton/skeleton.principles.stories.tsx +1 -1
  477. package/src/components/Skeleton/skeleton.spec.md +1 -2
  478. package/src/components/Skeleton/skeleton.tsx +1 -1
  479. package/src/components/Slider/slider.anatomy.stories.tsx +4 -4
  480. package/src/components/Slider/slider.spec.md +19 -85
  481. package/src/components/Slider/slider.tsx +13 -5
  482. package/src/components/Steps/steps.anatomy.stories.tsx +1 -1
  483. package/src/components/Steps/steps.spec.md +6 -4
  484. package/src/components/Steps/steps.tsx +11 -3
  485. package/src/components/Switch/switch.spec.md +4 -3
  486. package/src/components/Switch/switch.tsx +59 -10
  487. package/src/components/Tabs/tabs.anatomy.stories.tsx +7 -6
  488. package/src/components/Tabs/tabs.principles.stories.tsx +5 -3
  489. package/src/components/Tabs/tabs.spec.md +15 -48
  490. package/src/components/Tabs/tabs.tsx +70 -34
  491. package/src/components/Tag/tag.principles.stories.tsx +2 -2
  492. package/src/components/Tag/tag.spec.md +11 -43
  493. package/src/components/Tag/tag.tsx +30 -4
  494. package/src/components/Textarea/textarea.principles.stories.tsx +2 -1
  495. package/src/components/Textarea/textarea.spec.md +2 -1
  496. package/src/components/Textarea/textarea.tsx +20 -10
  497. package/src/components/TimePicker/time-picker.anatomy.stories.tsx +2 -1
  498. package/src/components/TimePicker/time-picker.spec.md +2 -2
  499. package/src/components/TimePicker/time-picker.tsx +35 -16
  500. package/src/components/Toast/toast.anatomy.stories.tsx +5 -6
  501. package/src/components/Toast/toast.principles.stories.tsx +10 -11
  502. package/src/components/Toast/toast.spec.md +5 -5
  503. package/src/components/Toast/toast.stories.tsx +5 -1
  504. package/src/components/Toast/toast.tsx +13 -7
  505. package/src/components/Tooltip/tooltip.spec.md +3 -3
  506. package/src/components/Tooltip/tooltip.tsx +1 -1
  507. package/src/components/TreeView/tree-view.anatomy.stories.tsx +6 -1
  508. package/src/components/TreeView/tree-view.principles.stories.tsx +2 -2
  509. package/src/components/TreeView/tree-view.spec.md +2 -2
  510. package/src/components/TreeView/tree-view.tsx +27 -5
  511. package/src/hooks/use-controllable.ts +7 -2
  512. package/src/hooks/use-is-touch-device.ts +8 -1
  513. package/src/index.ts +2 -1
  514. package/src/patterns/element-anatomy/element-anatomy.spec.md +3 -3
  515. package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
  516. package/src/patterns/element-anatomy/item-anatomy.spec.md +5 -14
  517. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +1 -0
  518. package/src/patterns/element-anatomy/item-anatomy.tsx +5 -6
  519. package/src/patterns/header-canonical/header-canonical.css +1 -2
  520. package/src/patterns/header-canonical/header-canonical.spec.md +5 -5
  521. package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +5 -4
  522. package/src/patterns/horizontal-overflow/horizontal-overflow.stories.tsx +137 -0
  523. package/src/patterns/overlay-surface/overlay-surface.spec.md +8 -7
  524. package/src/patterns/overlay-surface/overlay-surface.stories.tsx +116 -0
  525. package/src/patterns/overlay-surface/overlay-surface.tsx +5 -5
  526. package/src/patterns/resize-handle/resize-handle.spec.md +1 -1
  527. package/src/tokens/categorical-color.ts +1 -1
  528. package/src/tokens/color/color.spec.md +11 -13
  529. package/src/tokens/color/color.stories.tsx +1 -1
  530. package/src/tokens/color/primitives.css +7 -5
  531. package/src/tokens/color/semantic.css +8 -2
  532. package/src/tokens/density/density.spec.md +2 -0
  533. package/src/tokens/elevation/elevation.spec.md +1 -1
  534. package/src/tokens/layoutSpace/layoutSpace.spec.md +2 -1
  535. package/src/tokens/layoutSpace/layoutSpace.stories.tsx +3 -3
  536. package/src/tokens/motion/motion.spec.md +1 -1
  537. package/src/tokens/motion/motion.stories.tsx +142 -0
  538. package/src/tokens/opacity/opacity.spec.md +1 -1
  539. package/src/tokens/opacity/opacity.stories.tsx +7 -11
  540. package/src/tokens/radius/radius.spec.md +1 -1
  541. package/src/tokens/typography/typography.css +1 -1
  542. package/src/tokens/typography/typography.spec.md +1 -1
  543. package/src/tokens/uiSize/icon-size.ts +2 -2
  544. package/src/tokens/uiSize/uiSize.spec.md +12 -11
  545. package/src/tokens/uiSize/uiSize.stories.tsx +103 -0
  546. package/src/tokens/utility-registry.json +5 -4
  547. package/dist/node_modules/react-hook-form/dist/index.esm.js +0 -1950
  548. package/dist/node_modules/react-hook-form/dist/index.esm.js.map +0 -1
@@ -193,6 +193,8 @@ const buttonVariants = cva(
193
193
  // aria-pressed fallback(Radix overlay trigger override data-state 時仍生效)
194
194
  'aria-pressed:bg-primary-subtle aria-pressed:text-primary aria-pressed:border-transparent',
195
195
  'aria-pressed:hover:text-primary-hover',
196
+ // 2026-07-07 補齊第三階:fallback 分支與 data-[state=on] 分支同拼寫(選中之上按壓 = active 階)
197
+ 'aria-pressed:active:text-primary-active',
196
198
  ],
197
199
  },
198
200
  // neutral tone:灰底(neutral-selected family)— sidebar/contextual nav pressed
@@ -212,7 +214,7 @@ const buttonVariants = cva(
212
214
  ],
213
215
  defaultVariants: {
214
216
  // labeled 預設 = tertiary(2026-06-06 從 primary 改;iconOnly 預設 = text,由 resolvedVariant 注入)。
215
- // 真正預設邏輯在元件 resolvedVariant(L394);此 cva default 供直接 buttonVariants() 呼叫者一致。
217
+ // 真正預設邏輯在元件內 resolvedVariant(相對指法,免行號漂移);此 cva default 供直接 buttonVariants() 呼叫者一致。
216
218
  variant: 'tertiary',
217
219
  size: 'md',
218
220
  pressedTone: 'emphasis',
@@ -303,7 +305,7 @@ export interface ButtonProps
303
305
  * - Clear → 欄位清空,用 Inline Action
304
306
  * - Remove → collection 移除,用一般 Button / Inline Action
305
307
  *
306
- * 詳見 button.spec.md「Dismiss 視覺類」段 + patterns/element-anatomy/item-anatomy.spec.md
308
+ * 詳見 button.spec.md「Dismiss 視覺類」段 + patterns/element-anatomy/inline-action.spec.md
307
309
  * 「Dismiss canonical — X close only」段。
308
310
  */
309
311
  dismiss?: boolean
@@ -370,7 +372,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
370
372
 
371
373
  // 2026-05-23 deep-audit Phase A.4 Decision 1(user verbatim「決策ㄧ照你建議做」):dev-warn `iconOnly + (endIcon|badge)` 並用。
372
374
  // Spec SSOT:button.spec.md「iconOnly 嚴格定義為『只有一個 icon,正方形』,不可與 endIcon 或 badge 並用」。
373
- // 例外:overlayBadge 跟 iconOnly 並用 canonical(L372 反向 warn 已 cover)。
375
+ // 例外:overlayBadge 跟 iconOnly 並用 canonical(上方 overlayBadge 反向 warn 已 cover;相對指法,免行號漂移)。
374
376
  // 不擋 image canonical「icon + 下拉指示 = 不加 iconOnly + startIcon + endIcon + aria-label」(那 case iconOnly=false 不 trigger)。
375
377
  if (process.env.NODE_ENV !== 'production' && iconOnly && (EndIcon !== undefined || badge != null)) {
376
378
  // eslint-disable-next-line no-console
@@ -426,27 +428,13 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
426
428
  const unboundedAttr =
427
429
  resolvedVariant === 'text' || dismiss ? { 'data-unbounded': 'true' } : {}
428
430
 
429
- const buttonEl = (
430
- <Comp
431
- className={cn(
432
- buttonVariants({ variant: resolvedVariant, danger: resolvedDanger, size: resolvedSize, pressedTone, className }),
433
- // iconOnly 鐵律:padding-free + aspect-square + flex-center (Polaris idiom)
434
- // 0 magic-number 0 公式自動正方形。詳 ICON_ONLY_BASE rationale。
435
- resolvedIconOnly && ICON_ONLY_BASE,
436
- // Dismiss 視覺弱化:override Button text variant 預設 foreground 為 fg-muted → hover foreground
437
- // 跟 Inline Action dismiss 視覺一致(cross-implementation dimming canonical)
438
- dismiss && 'text-fg-muted hover:text-foreground',
439
- resolvedFullWidth && 'w-full',
440
- )}
441
- ref={ref}
442
- type="button"
443
- disabled={disabled || loading}
444
- aria-busy={loading || undefined}
445
- aria-label={ariaLabel}
446
- {...toggleAttrs}
447
- {...unboundedAttr}
448
- {...restProps}
449
- >
431
+ // Native path 內部結構:[loading spinner | startIcon(+overlayBadge)] [label] [badge + endIcon]。
432
+ // asChild 時**不渲染**(2026-07-04 fix):Radix Slot 規範 children 必為單一 element
433
+ // (React.Children.only)— 內部多 expression children 變 array 必 runtime throw;
434
+ // asChild 分支只 render consumer child,內容由 consumer 自帶。
435
+ // CLAUDE.md 失敗記憶索引「asChild ? Slot : Native 內部 JSX 仍渲染多 children」條。
436
+ const nativeChildren = (
437
+ <>
450
438
  {loading ? (
451
439
  <CircularProgress size={iconSize} className="text-current" />
452
440
  ) : StartIcon ? (
@@ -478,6 +466,55 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
478
466
  {EndIcon && <EndIcon size={iconSize} aria-hidden />}
479
467
  </span>
480
468
  )}
469
+ </>
470
+ )
471
+
472
+ const sharedClassName = cn(
473
+ buttonVariants({ variant: resolvedVariant, danger: resolvedDanger, size: resolvedSize, pressedTone, className }),
474
+ // iconOnly 鐵律:padding-free + aspect-square + flex-center (Polaris idiom)
475
+ // 0 magic-number 0 公式自動正方形。詳 ICON_ONLY_BASE rationale。
476
+ resolvedIconOnly && ICON_ONLY_BASE,
477
+ // Dismiss 視覺弱化:override Button text variant 預設 foreground 為 fg-muted → hover foreground
478
+ // 跟 Inline Action dismiss 視覺一致(cross-implementation dimming canonical)
479
+ dismiss && 'text-fg-muted hover:text-foreground',
480
+ resolvedFullWidth && 'w-full',
481
+ )
482
+
483
+ // 2026-07-05 D4 修:asChild 原 silently drop loading/disabled 語意(無 aria-busy/aria-disabled/
484
+ // click guard → <a> loading 期間可重複點擊)。補 ARIA(任何 element 合法;aria-disabled 自動點亮
485
+ // cva 既有視覺)+ functional guard(APG aria-disabled 模式:語意+視覺由元件、功能阻斷由 guard)。
486
+ const { onClick: slotConsumerOnClick, ...slotRest } = restProps as typeof restProps & { onClick?: React.MouseEventHandler<HTMLElement> }
487
+ const buttonEl = asChild ? (
488
+ // Slot 分支:children 必為單一 consumer element(React.Children.only),
489
+ // 內部 slot(loading/startIcon/badge)不渲染;不硬塞 type/disabled(consumer child 可能是 <a>)。
490
+ <Comp
491
+ className={sharedClassName}
492
+ ref={ref}
493
+ aria-label={ariaLabel}
494
+ aria-busy={loading || undefined}
495
+ aria-disabled={disabled || loading || undefined}
496
+ onClick={disabled || loading
497
+ ? (e: React.MouseEvent<HTMLElement>) => { e.preventDefault(); e.stopPropagation() }
498
+ : slotConsumerOnClick}
499
+ {...toggleAttrs}
500
+ {...unboundedAttr}
501
+ {...slotRest}
502
+ >
503
+ {children}
504
+ </Comp>
505
+ ) : (
506
+ <Comp
507
+ className={sharedClassName}
508
+ ref={ref}
509
+ type="button"
510
+ disabled={disabled || loading}
511
+ aria-busy={loading || undefined}
512
+ aria-label={ariaLabel}
513
+ {...toggleAttrs}
514
+ {...unboundedAttr}
515
+ {...restProps}
516
+ >
517
+ {nativeChildren}
481
518
  </Comp>
482
519
  )
483
520
 
@@ -523,10 +560,13 @@ export const buttonMeta = {
523
560
  md: { fieldHeight: 32, iconSize: 16, typography: 'body' },
524
561
  lg: { fieldHeight: 36, iconSize: 20, typography: 'body-lg' },
525
562
  },
526
- states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
563
+ // 'pressed' = toggle on 持續態(data-[state=on],emphasis 淡藍底/neutral 灰底;2026-07-07 meta 詞彙統一補列)
564
+ states: ['default', 'hover', 'active', 'pressed', 'focus-visible', 'disabled'],
527
565
  tokens: {
528
- bg: ['--primary', '--primary-hover', '--primary-active', '--bg-disabled'],
529
- fg: ['--on-emphasis', '--fg-disabled'],
566
+ // 2026-07-04 audit 補齊:原只列 primary variant 家族;按 buttonVariants cva 真實 class 補
567
+ // secondary/tertiary/text/link/danger 消費的 token(auto-compile token 矩陣依此)
568
+ bg: ['--primary', '--primary-hover', '--primary-active', '--primary-subtle', '--bg-disabled', '--surface', '--neutral-hover', '--neutral-active', '--neutral-selected'],
569
+ fg: ['--on-emphasis', '--fg-disabled', '--foreground', '--primary', '--error', '--error-hover', '--error-active'],
530
570
  ring: ['--ring'],
531
571
  },
532
572
  defaultVariant: 'tertiary', // 2026-06-10 修 stale:對齊 cva defaultVariants(2026-06-06 labeled 預設改 tertiary,meta 漏同步)
@@ -3,3 +3,4 @@
3
3
  // import { ... } from '@qijenchen/design-system/{components,patterns}/<Dir>'
4
4
  // Regenerate: node scripts/gen-component-indexes.mjs
5
5
  export * from './button'
6
+ export * from './button-group'
@@ -18,7 +18,9 @@ export default meta
18
18
 
19
19
  type Story = StoryObj<typeof Calendar>
20
20
 
21
- const now = new Date()
21
+ // 釘固定日期(2026-07-07 VR flake):真實 new Date() 讓 today 標記隨換日移格 → ubuntu
22
+ // baseline 每隔幾天假 breach(0.07–0.5%)。釘死 = deterministic 渲染;月中讓事件分布自然。
23
+ const now = new Date(2026, 6, 15)
22
24
  const thisMonth = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0')
23
25
 
24
26
  const sampleEvents: CalendarEvent[] = [
@@ -95,7 +97,7 @@ export const ColorMatrix: Story = {
95
97
 
96
98
  <div>
97
99
  <H3>Cell 視覺 token</H3>
98
- <Desc>月 view cell 的狀態色彩。Cell 高度固定容納日期 header + 3 event tile;7 欄等分。</Desc>
100
+ <Desc>月 view cell 的狀態色彩。Cell 最小高度 min-h-28 容納日期 header + 3 event tile,隨容器高度伸縮;7 欄等分。</Desc>
99
101
  <div className="overflow-x-auto">
100
102
  <table className="text-caption border-collapse">
101
103
  <thead>
@@ -109,7 +111,7 @@ export const ColorMatrix: Story = {
109
111
  <tr>
110
112
  <Td>Cell 高度</Td>
111
113
  <Td mono>min-h-28(112px)</Td>
112
- <Td>容納 header + 3 event tile,MVP view 固定</Td>
114
+ <Td>容納 header + 3 event tile 的下限,隨容器高度伸縮(root h-full + grid flex-1)</Td>
113
115
  </tr>
114
116
  <tr>
115
117
  <Td>Cell 寬度</Td>
@@ -14,7 +14,9 @@ const meta: Meta = {
14
14
  export default meta
15
15
  type Story = StoryObj
16
16
 
17
- const now = new Date()
17
+ // 釘固定日期(2026-07-07 VR flake):真實 new Date() 讓 today 標記隨換日移格 → ubuntu
18
+ // baseline 每隔幾天假 breach(0.07–0.5%)。釘死 = deterministic 渲染;月中讓事件分布自然。
19
+ const now = new Date(2026, 6, 15)
18
20
  const thisMonth = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0')
19
21
 
20
22
  const Section = ({ title, children }: { title: string; children: React.ReactNode }) => (
@@ -92,6 +92,13 @@ Calendar 是**事件檢視 canvas**,讓 user 以**月 / 週 / 日** 三種 view
92
92
  weekStartsOn={0 | 1} // 0=Sun, 1=Mon
93
93
  renderEventTile={(event) => ReactNode} // 自訂 event tile 視覺
94
94
  size="md" | "lg" // cell 大小(MVP 只 md;lg 為 tech debt)
95
+ locale="en-US" // Intl 語系(月份標題 / 星期名;預設 'en-US')
96
+ prevAriaLabel="上個月" // i18n override:prev 導覽鈕 aria-label
97
+ nextAriaLabel="下個月" // i18n override:next 導覽鈕 aria-label
98
+ navAriaLabel="行事曆月份導覽" // i18n override:月份導覽 <nav> landmark aria-label
99
+ viewToggleAriaLabel="檢視切換" // i18n override:檢視切換 SegmentedControl aria-label
100
+ todayLabel="今天" // i18n override:「今天」按鈕文字
101
+ createLabel="新事件" // i18n override:「新事件」CTA 文字(僅在傳 onCreateEvent 時渲染)
95
102
  className
96
103
  />
97
104
  ```
@@ -134,7 +141,7 @@ interface CalendarEvent {
134
141
 
135
142
  ### Cell 規則
136
143
 
137
- - **Cell 尺寸**:MVP 月 view cell 高度固定,容納日期 header + 3 個 event tile;寬度 7 欄等分
144
+ - **Cell 尺寸**:MVP 月 view cell 最小高度 `min-h-28`(112px),容納日期 header + 3 個 event tile,並隨容器高度伸縮(root `h-full` + grid `flex-1`);寬度 7 欄等分
138
145
  - **日期 header**:右上角數字(對齊 Google Calendar 視覺慣例)
139
146
  - **Today cell**:日期數字以 info-filled pill 強調(對齊 Google Calendar today pill)
140
147
  - **Outside day cell**:上/下月溢出日期弱化字色 + 背景略暗區分
@@ -159,7 +166,7 @@ interface CalendarEvent {
159
166
  - 左 Nav:`<Button iconOnly>` prev/next + `<Button>今天</Button>` 跳 today
160
167
  - 中央 title:`<h2 className="text-h3">` or `text-body-lg font-medium`
161
168
  - 右視圖切換:`<SegmentedControl>`(day/week/month)—— MVP 只啟用 month,其他 disabled
162
- - 右上 CTA:`<Button variant="primary" startIcon={Plus}>新事件</Button>`
169
+ - 右上 CTA:`<Button variant="primary" startIcon={Plus}>新事件</Button>` — **條件渲染:僅在傳 `onCreateEvent` 回調時出現**(未傳 = 無 CTA,純瀏覽場景);文案由 `createLabel` prop override(對齊 `todayLabel` 等 chrome 文字 i18n override 慣例)
163
170
 
164
171
  對齊 `patterns/action-bar/action-bar.spec.md`(左 context / 中 focus / 右 CTA 的經典分組)。
165
172
 
@@ -21,7 +21,9 @@ type Story = StoryObj<typeof Calendar>
21
21
 
22
22
  // ── 真實業務情境 ─────────────────────────────────────────────────────
23
23
 
24
- const now = new Date()
24
+ // 釘固定日期(2026-07-07 VR flake):真實 new Date() 讓 today 標記隨換日移格 → ubuntu
25
+ // baseline 每隔幾天假 breach(0.07–0.5%)。釘死 = deterministic 渲染;月中讓事件分布自然。
26
+ const now = new Date(2026, 6, 15)
25
27
  const thisMonth = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0')
26
28
 
27
29
  /**
@@ -98,6 +98,8 @@ export interface CalendarProps extends Omit<React.HTMLAttributes<HTMLDivElement>
98
98
  navAriaLabel?: string
99
99
  viewToggleAriaLabel?: string
100
100
  todayLabel?: string
101
+ /** 「新事件」CTA 文字。Override for i18n。CTA 僅在傳 `onCreateEvent` 時渲染(spec Toolbar 段)。 */
102
+ createLabel?: string
101
103
  }
102
104
 
103
105
  // ── Event tile color tokens ─────────────────────────────────────────────────
@@ -114,17 +116,8 @@ function coerceDate(value: string | Date): Date {
114
116
  return value instanceof Date ? value : new Date(value)
115
117
  }
116
118
 
117
- function eventsOnDate(events: CalendarEvent[], date: Date): CalendarEvent[] {
118
- return events.filter((e) => {
119
- const start = coerceDate(e.start)
120
- const end = coerceDate(e.end)
121
- // 日期落在 [start, end] 範圍內(日精度)
122
- const d = new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime()
123
- const s = new Date(start.getFullYear(), start.getMonth(), start.getDate()).getTime()
124
- const eEnd = new Date(end.getFullYear(), end.getMonth(), end.getDate()).getTime()
125
- return d >= s && d <= eEnd
126
- })
127
- }
119
+ // eventsOnDate(per-cell events 掃描)已由 eventsByDate bucketing memo 取代並移除
120
+ // (2026-07-06 D3 perf;留著會觸發 noUnusedLocals TS6133)。
128
121
 
129
122
  // ── Component ──────────────────────────────────────────────────────────────
130
123
 
@@ -151,6 +144,7 @@ const Calendar = React.forwardRef<HTMLDivElement, CalendarProps>(function Calend
151
144
  navAriaLabel = '行事曆月份導覽', // i18n-allow: DS default; consumer override via navAriaLabel prop
152
145
  viewToggleAriaLabel = '檢視切換', // i18n-allow: DS default; consumer override via viewToggleAriaLabel prop
153
146
  todayLabel = '今天', // i18n-allow: DS default; consumer override via todayLabel prop
147
+ createLabel = '新事件', // i18n-allow: DS default; consumer override via createLabel prop
154
148
  ...props
155
149
  }, ref) {
156
150
  // Controlled / uncontrolled refDate
@@ -186,10 +180,13 @@ const Calendar = React.forwardRef<HTMLDivElement, CalendarProps>(function Calend
186
180
  return eachDayOfInterval({ start: gridStart, end: gridEnd })
187
181
  }, [refDate, weekStartsOn])
188
182
 
189
- const monthTitle = new Intl.DateTimeFormat(locale, {
190
- year: 'numeric',
191
- month: 'long',
192
- }).format(refDate)
183
+ // 2026-07-06 D3 perf:formatter locale memo(對齊 weekdayNames 既有做法),避免每 render 重建
184
+ // Intl.DateTimeFormat(已知貴的 constructor);.format(refDate) 每 render 仍執行(cheap)。
185
+ const monthTitleFormatter = React.useMemo(
186
+ () => new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'long' }),
187
+ [locale],
188
+ )
189
+ const monthTitle = monthTitleFormatter.format(refDate)
193
190
 
194
191
  const today = new Date()
195
192
 
@@ -200,6 +197,41 @@ const Calendar = React.forwardRef<HTMLDivElement, CalendarProps>(function Calend
200
197
  )
201
198
  }, [days, locale])
202
199
 
200
+ // 2026-07-06 D3 perf:原每 render 對 42 cells 各跑 eventsOnDate 全 events filter = O(42×N) +
201
+ // 每比較配 ~5 個 Date;數百-上千 events(行事曆真實規模)單次 render 數十 ms,且父層任何 re-render
202
+ // 全額重付。改 useMemo 單趟 bucket:每 event 只 parse 一次,展開 [start..end](clamp 到可見 grid,
203
+ // 避免長跨度 event 展開爆量)寫入 date→events Map,順便完成 allDay 排序。cell 內改 O(1) map.get。行為 Δ=0。
204
+ const eventsByDate = React.useMemo(() => {
205
+ const map = new Map<string, CalendarEvent[]>()
206
+ if (days.length === 0) return map
207
+ const first = days[0]
208
+ const last = days[days.length - 1]
209
+ const gridStart = new Date(first.getFullYear(), first.getMonth(), first.getDate()).getTime()
210
+ const gridEnd = new Date(last.getFullYear(), last.getMonth(), last.getDate()).getTime()
211
+ for (const e of events) {
212
+ const start = coerceDate(e.start)
213
+ const end = coerceDate(e.end)
214
+ const eEnd = new Date(end.getFullYear(), end.getMonth(), end.getDate()).getTime()
215
+ let sMs = new Date(start.getFullYear(), start.getMonth(), start.getDate()).getTime()
216
+ if (eEnd < gridStart || sMs > gridEnd) continue // 可見窗外對 42 cells 無貢獻
217
+ if (sMs < gridStart) sMs = gridStart
218
+ const lastMs = Math.min(eEnd, gridEnd)
219
+ const cursor = new Date(sMs)
220
+ while (cursor.getTime() <= lastMs) {
221
+ const key = `${cursor.getFullYear()}-${cursor.getMonth() + 1}-${cursor.getDate()}`
222
+ const bucket = map.get(key)
223
+ if (bucket) bucket.push(e)
224
+ else map.set(key, [e])
225
+ cursor.setDate(cursor.getDate() + 1)
226
+ }
227
+ }
228
+ // allDay 排前(對齊 Google Calendar 全天列在上;V8 stable sort 保 events 原序 → 與舊 per-cell 一致)
229
+ for (const bucket of map.values()) {
230
+ bucket.sort((a, b) => Number(b.allDay ?? false) - Number(a.allDay ?? false))
231
+ }
232
+ return map
233
+ }, [events, days])
234
+
203
235
  const handleToday = () => setRefDate(new Date())
204
236
  const handlePrev = () => setRefDate(subMonths(refDate, 1))
205
237
  const handleNext = () => setRefDate(addMonths(refDate, 1))
@@ -265,7 +297,7 @@ const Calendar = React.forwardRef<HTMLDivElement, CalendarProps>(function Calend
265
297
 
266
298
  {onCreateEvent && (
267
299
  <Button variant="primary" size="sm" startIcon={Plus} onClick={onCreateEvent}>
268
- 新事件
300
+ {createLabel}
269
301
  </Button>
270
302
  )}
271
303
  </div>
@@ -294,8 +326,9 @@ const Calendar = React.forwardRef<HTMLDivElement, CalendarProps>(function Calend
294
326
  {days.slice(rowIdx * 7, rowIdx * 7 + 7).map((date) => {
295
327
  const inMonth = isSameMonth(date, refDate)
296
328
  const isToday = isSameDay(date, today)
297
- // 2026-06-01 allDay:全天事件排 cell 頂端(對齊 Google Calendar 全天列在上)
298
- const dayEvents = eventsOnDate(events, date).slice().sort((a, b) => Number(b.allDay ?? false) - Number(a.allDay ?? false))
329
+ // 2026-06-01 allDay:全天事件排 cell 頂端(對齊 Google Calendar 全天列在上)——
330
+ // 排序已在 eventsByDate bucketing memo 內完成(D3 perf),cell O(1) 查表
331
+ const dayEvents = eventsByDate.get(`${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`) ?? []
299
332
  const visibleEvents = dayEvents.slice(0, MAX_TILES_PER_CELL)
300
333
  const overflowCount = dayEvents.length - visibleEvents.length
301
334
 
@@ -414,7 +447,8 @@ export const calendarMeta = {
414
447
  sizes: {
415
448
 
416
449
  },
417
- states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
450
+ // 'active' 移除 — 日期格按壓視覺屬 DateGrid(其 meta 已 −active);nav 按壓屬內嵌 Button(2026-07-07 詞彙統一 DS-wide 按壓訊號盤點:檔內 0 active: utility / 0 *-active token)。
451
+ states: ['default', 'hover', 'focus-visible', 'disabled'],
418
452
  tokens: {
419
453
  bg: ['bg-muted', 'bg-neutral-hover', 'bg-info', 'bg-surface'],
420
454
  fg: ['text-fg-disabled', 'text-fg-muted', 'text-foreground'],
@@ -41,7 +41,7 @@ const Label = ({ children, warn }: { children: React.ReactNode; warn?: boolean }
41
41
 
42
42
  const Section = ({ title, children }: { title: string; children: React.ReactNode }) => (
43
43
  <section className="mb-12">
44
- <h2 className="text-heading-3 font-bold text-foreground mb-4 pb-2 border-b border-border">{title}</h2>
44
+ <h2 className="text-h3 font-bold text-foreground mb-4 pb-2 border-b border-border">{title}</h2>
45
45
  {children}
46
46
  </section>
47
47
  )
@@ -271,6 +271,7 @@ const CarouselPrevious = React.forwardRef<HTMLButtonElement, ArrowProps>(
271
271
  )
272
272
  CarouselPrevious.displayName = 'CarouselPrevious'
273
273
 
274
+ // code-quality-allow: long-function — 與 CarouselPrevious 結構對稱的 JSX-heavy arrow(方向/位置/邊界三態 className 組合),拆 helper 會破壞兩支對讀性
274
275
  const CarouselNext = React.forwardRef<HTMLButtonElement, ArrowProps>(
275
276
  ({ className, 'aria-label': ariaLabel = '下一張' /* i18n-allow: DS default; consumer override via aria-label prop */ }, ref) => {
276
277
  const { orientation, scrollNext, canScrollNext } = useCarousel()
@@ -371,7 +372,9 @@ export const carouselMeta = {
371
372
  sizes: {
372
373
 
373
374
  },
374
- states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
375
+ // 'active' 移除 全檔無按壓視覺(prev/next 按壓屬內嵌 Button meta,同 Notice/Alert 排除慣例;
376
+ // 2026-07-07 詞彙統一 DS-wide 交叉掃補修)。dot hover 為自有 hover 態(bg-on-emphasis alpha 變體)。
377
+ states: ['default', 'hover', 'focus-visible', 'disabled'],
375
378
  tokens: {
376
379
  bg: [],
377
380
  fg: [],
@@ -162,7 +162,7 @@ Chart 是 **composite data-visualization** 元件,不是單一互動 primitive:
162
162
  ## A11y 預設
163
163
 
164
164
  - **顏色非唯一語義**:不只靠色彩區分類別,配合 icon / label / pattern(Recharts 的 `strokeDasharray` 等)讓色盲 / 黑白列印仍可辨識
165
- - **Tooltip 鍵盤可存取**:Recharts v3 的 `accessibilityLayer`( DS 所有 chart 範例皆已啟用)內建鍵盤導覽——圖表 SVG 自動取得 `tabIndex=0` + `role="application"`,使用者用方向鍵 ←/→ 沿軸刻度逐一瀏覽資料點,讀屏器朗讀當前資料點;無需 controlled `activeIndex` 手動接線
165
+ - **Tooltip 鍵盤可存取**:Recharts v3 的 `accessibilityLayer`(Recharts 3.x 預設啟用;本 DS 長條 / 折線 / 面積範例另顯式標明此 prop 文件化依賴,圓餅 / 環圈走預設)內建鍵盤導覽——圖表 SVG 自動取得 `tabIndex=0` + `role="application"`,使用者用方向鍵 ←/→ 沿軸刻度逐一瀏覽資料點,讀屏器朗讀當前資料點;無需 controlled `activeIndex` 手動接線
166
166
  - **對比度**:`--chart-*` 在 light/dark 的 step 選擇已考量對 canvas bg 的對比(light=step-6 / dark=step-5)
167
167
 
168
168
  ---
@@ -255,7 +255,8 @@ const ChartTooltipContent = React.forwardRef<HTMLDivElement, ChartTooltipContent
255
255
  {nestLabel ? tooltipLabel : null}
256
256
  <span className="text-fg-secondary">{itemConfig?.label || item.name}</span>
257
257
  </div>
258
- {item.value && (
258
+ {/* 2026-07-04 修:falsy check 會讓合法資料值 0 不渲染(shadcn 上游同病)→ null check */}
259
+ {item.value != null && (
259
260
  <span className="text-foreground font-mono font-medium tabular-nums">
260
261
  {item.value.toLocaleString()}
261
262
  </span>
@@ -209,7 +209,7 @@ export const ClampPolicyRule: Story = {
209
209
  title="Per-instance override:傳 'none' 不是 undefined"
210
210
  note="這組覆寫設定在 SelectionItem 上(Checkbox 內部用它組合 label)。React destructure default 會把 undefined 當「沒傳」→ fallback 到預設。要明確表達「不截」請傳 'none'"
211
211
  >
212
- <Label>SelectionItem labelMaxLines='none' / labelMaxLines={1} / labelMaxLines={2}(1-10 的數字)</Label>
212
+ <Label>SelectionItem labelMaxLines='none' / labelMaxLines={1} / labelMaxLines={2}(1-6 的數字;7 以上無對應 line-clamp class,傳了不生效)</Label>
213
213
  </Rule>
214
214
  </div>
215
215
  ),
@@ -249,7 +249,7 @@ Indeterminate 是由父層邏輯控制的狀態,Checkbox 本身不會自動進
249
249
 
250
250
  - **Uncontrolled**:只傳 `defaultChecked`,DOM 自管 — 適合表單 native submit
251
251
  - **Controlled**:傳 `checked` + `onCheckedChange`,React state 主導 — 適合需即時聯動其他欄位
252
- - **Read-only**:用 `readOnly` prop(不是省略 `onCheckedChange`)。standalone:`readOnly` 設 `aria-readonly` / `data-readonly` / `tabIndex=-1` + cva `pointer-events-none`,鎖互動但保留 checked 視覺;**Field 內(無 inline label)改渲染 readonly 灰框 + ✓/—**(見上方 mode 段)。注意:只傳 `checked` 而不傳 `onCheckedChange` 僅讓 Radix 把值鎖在 prop(不會更新),控件仍可 focus / click、不會進 disabled state — 那不是 readonly
252
+ - **Read-only**:用 `readOnly` prop(不是省略 `onCheckedChange`)。standalone:`readOnly` 設 `aria-readonly` / `data-readonly` / `tabIndex=-1` + cva `pointer-events-none` + onClick preventDefault guard(2026-07-05 D4 補:`<label htmlFor>` 的 synthetic click 非 pointer event,`pointer-events-none` 攔不到,需 guard 才真鎖),鎖互動但保留 checked 視覺;**Field 內(無 inline label)改渲染 readonly 灰框 + ✓/—**(見上方 mode 段)。注意:只傳 `checked` 而不傳 `onCheckedChange` 僅讓 Radix 把值鎖在 prop(不會更新),控件仍可 focus / click、不會進 disabled state — 那不是 readonly
253
253
 
254
254
  ### `mode` prop(Field mode,正交於 size)
255
255
 
@@ -85,13 +85,16 @@ export interface CheckboxProps
85
85
  /**
86
86
  * Inline label。提供時 Checkbox 自動透過 SelectionItem 包裝,
87
87
  * 套用 text-body / text-foreground / disabled 色 的 codified 樣式。
88
- * 在 <Field> context 內時此 prop 會被忽略(由 FieldLabel 接管)。
88
+ * 在 <Field> context 內且不在 CheckboxGroup 內時,此 prop 會被忽略(由 FieldLabel 接管);
89
+ * CheckboxGroup 內每個 checkbox 的 label 是它自己的選項名,必保留
90
+ * (同檔 shouldSuppressLabel = insideField && !insideGroup,AR50 anchor)。
89
91
  */
90
92
  label?: React.ReactNode
91
93
  /**
92
94
  * Inline description(secondary 文字)。須與 label 搭配使用。
93
95
  * 套用 text-body / text-fg-secondary 樣式。
94
- * 在 <Field> context 內時此 prop 會被忽略(由 FieldDescription 接管)。
96
+ * 在 <Field> context 內且不在 CheckboxGroup 內時,此 prop 會被忽略(由 FieldDescription 接管);
97
+ * CheckboxGroup 內的 description 屬各選項自有,必保留(同 label 的 group 例外)。
95
98
  */
96
99
  description?: React.ReactNode
97
100
  /** 可選左側 icon(label 前)— 2026-06-12 M30 修:轉發 SelectionItem 既有 canonical 槽(selection-item.tsx jsDoc 為 SSOT;與 avatar 互斥)*/
@@ -188,13 +191,29 @@ const Checkbox = React.forwardRef<
188
191
  // readonly 灰框 size:走 SSOT resolver(prop > ctx > 'md',field-context.ts:150-161)
189
192
  const resolvedBoxSize = useResolvedFieldSize(size ?? undefined, 'md') as 'sm' | 'md' | 'lg'
190
193
 
194
+ // 2026-07-04 修:display / readonly-in-Field 分支的裸 span/div 原丟棄剩餘 props
195
+ // (id/data-*/aria-*)且不轉發 forwardRef ref → consumer 的 aria-label 等靜默失效。
196
+ // 抽出 Radix 專屬 non-DOM props,其餘 DOM props 於兩分支 spread 轉發(與 Switch/
197
+ // RadioGroup 同修);edit 主路徑不受影響(rootEl 仍 spread 完整 props)。
198
+ const {
199
+ checked: checkedProp,
200
+ defaultChecked: defaultCheckedProp,
201
+ onCheckedChange: _onCheckedChange,
202
+ required: _required,
203
+ name: _name,
204
+ value: _value,
205
+ asChild: _asChild,
206
+ children: _children,
207
+ ...restDomProps
208
+ } = props
209
+
191
210
  // ── mode='display'(下移至所有 hooks 之後,per #35 Rules of Hooks)──────────
192
211
  // 純展示模式:無互動 primitive、渲染 ✓ / —(checked=true → ✓ / 其他 → —)。取代 BooleanDisplay。
193
212
  if (resolvedMode === 'display') {
194
- const isChecked = props.checked === true
213
+ const isChecked = checkedProp === true
195
214
  return isChecked
196
- ? <span className="text-foreground">✓</span>
197
- : <span className="text-fg-muted">—</span>
215
+ ? <span {...restDomProps} ref={ref as React.Ref<HTMLSpanElement>} className="text-foreground">✓</span>
216
+ : <span {...restDomProps} ref={ref as React.Ref<HTMLSpanElement>} className="text-fg-muted">—</span>
198
217
  }
199
218
 
200
219
  // ── mode='readonly' in Field(2026-06-12 user 拍板「灰框 + ✓/—」)─────────────
@@ -206,10 +225,12 @@ const Checkbox = React.forwardRef<
206
225
  // Scope:僅 Field 內且無 inline label(FieldLabel 接管 label 的表單欄位場景);
207
226
  // standalone readOnly(settings list / SelectionItem row)維持原樣鎖互動不變。
208
227
  if (effectiveReadOnly && insideField && effectiveLabel == null) {
209
- const isChecked = (props.checked ?? props.defaultChecked) === true
228
+ const isChecked = (checkedProp ?? defaultCheckedProp) === true
210
229
  const boxSize = resolvedBoxSize
211
230
  return (
212
231
  <div
232
+ {...(restDomProps as React.HTMLAttributes<HTMLDivElement>)}
233
+ ref={ref as React.Ref<HTMLDivElement>}
213
234
  role="checkbox"
214
235
  aria-checked={isChecked}
215
236
  aria-readonly="true"
@@ -239,6 +260,18 @@ const Checkbox = React.forwardRef<
239
260
  aria-describedby={fieldCtx?.descriptionId}
240
261
  className={cn(checkboxVariants({ size }), className)}
241
262
  {...props}
263
+ // 2026-07-05 D4 修:readOnly 鎖互動補 label 繞道 — <label htmlFor> 的 synthetic click
264
+ // 不是 pointer event,cva pointer-events-none 攔不到,點 label 文字仍 toggle。
265
+ // preventDefault 讓 Radix composeEventHandlers(checkForDefaultPrevented)跳過內部 toggle;
266
+ // 必列在 {...props} 之後才不被 consumer onClick 覆蓋(readOnly 時 consumer onClick 一併鎖,
267
+ // 與 pointer-events-none 擋直接點擊的行為一致)。
268
+ onClick={(event) => {
269
+ if (effectiveReadOnly) {
270
+ event.preventDefault()
271
+ return
272
+ }
273
+ props.onClick?.(event)
274
+ }}
242
275
  >
243
276
  <CheckboxPrimitive.Indicator className="grid place-content-center text-current">
244
277
  {props.checked === 'indeterminate'
@@ -283,7 +316,8 @@ export const checkboxMeta = {
283
316
  md: { fieldHeight: 32, iconSize: 12, typography: 'body' },
284
317
  lg: { fieldHeight: 36, iconSize: 16, typography: 'body-lg' },
285
318
  },
286
- states: ['default', 'hover', 'focus-visible', 'disabled'],
319
+ // 'checked' = 勾選持續態(bg-primary;2026-07-07 meta 詞彙統一補列,含 indeterminate 同族)
320
+ states: ['default', 'hover', 'checked', 'focus-visible', 'disabled'],
287
321
  tokens: {
288
322
  bg: ['bg-disabled', 'bg-primary', 'bg-primary-hover', 'bg-surface'],
289
323
  fg: ['text-fg-disabled', 'text-fg-secondary', 'text-foreground'],
@@ -3,3 +3,4 @@
3
3
  // import { ... } from '@qijenchen/design-system/{components,patterns}/<Dir>'
4
4
  // Regenerate: node scripts/gen-component-indexes.mjs
5
5
  export * from './checkbox'
6
+ export * from './checkbox-group'
@@ -134,7 +134,7 @@ export const ColorMatrix: Story = {
134
134
  <div>
135
135
  <H3>狀態 × 色彩 Token 矩陣</H3>
136
136
  <Desc>
137
- Chip 採 pill-設計準則 選中規則(跟 SegmentedControl 共用):selected 時 `--primary-hover` 同時染 border 和 text,
137
+ Chip 採 pill-設計準則 選中規則(跟 SegmentedControl 共用;semantic.css SSOT,2026-07-06 拍板改 base):selected 時 `--primary` 同時染 border 和 text,
138
138
  **底色維持 `--surface` 不變**——不用 primary-subtle 底色(那是 Button subtle 的視覺語言,跟 filter chip 不同)。
139
139
  視覺輕量是 filter 語意必要條件,避免 chip 群組喧賓奪主壓過資料表格。
140
140
  </Desc>
@@ -167,8 +167,8 @@ export const ColorMatrix: Story = {
167
167
  <tr>
168
168
  <Td mono>selected</Td>
169
169
  <Td><span className="inline-flex items-center gap-1.5"><Swatch value="--surface" size="sm" /><span className="font-mono">--surface(不變)</span></span></Td>
170
- <Td><span className="inline-flex items-center gap-1.5"><Swatch value="--primary-hover" size="sm" /><span className="font-mono">--primary-hover</span></span></Td>
171
- <Td><span className="inline-flex items-center gap-1.5"><Swatch value="--primary-hover" size="sm" /><span className="font-mono">--primary-hover</span></span></Td>
170
+ <Td><span className="inline-flex items-center gap-1.5"><Swatch value="--primary" size="sm" /><span className="font-mono">--primary</span></span></Td>
171
+ <Td><span className="inline-flex items-center gap-1.5"><Swatch value="--primary" size="sm" /><span className="font-mono">--primary</span></span></Td>
172
172
  <Td mono>currentColor</Td>
173
173
  </tr>
174
174
  <tr>
@@ -185,7 +185,7 @@ export const ColorMatrix: Story = {
185
185
 
186
186
  <div>
187
187
  <H3>視覺並列對照</H3>
188
- <Desc>左 default / 中 selected / 右 disabled,觀察 border 同時升階到 primary-hover 的視覺變化(非 bg 變化)。</Desc>
188
+ <Desc>左 default / 中 selected / 右 disabled,觀察 border 同時染 primary(base)的視覺變化(非 bg 變化)。</Desc>
189
189
  <div className="flex items-center gap-8 px-6 py-5 rounded-lg bg-canvas border border-divider">
190
190
  <div className="flex flex-col gap-2 items-start">
191
191
  <span className="text-[11px] text-fg-muted font-mono">default</span>
@@ -301,7 +301,7 @@ export const StateBehavior: Story = {
301
301
  <H3>四種狀態 × 色彩 Token</H3>
302
302
  <Desc>
303
303
  選中態走 pill-設計準則 規則(跟 SegmentedControl 完全一致):
304
- **primary-hover 同時染 border 和 text**,底色維持 bg-surface 不變——不用 primary-subtle
304
+ **primary base 同時染 border 和 text**,底色維持 bg-surface 不變——不用 primary-subtle
305
305
  底色(那是 Button subtle 的視覺語言,跟 chip 不同)。
306
306
  </Desc>
307
307
  <div className="overflow-x-auto mb-4">
@@ -323,8 +323,8 @@ export const StateBehavior: Story = {
323
323
  <tr>
324
324
  <Td mono>selected (data-state=on)</Td>
325
325
  <Td><TokenCell token="--surface" /></Td>
326
- <Td><TokenCell token="--primary-hover" display="primary-hover" /></Td>
327
- <Td><TokenCell token="--primary-hover" display="primary-hover" /></Td>
326
+ <Td><TokenCell token="--primary" display="primary" /></Td>
327
+ <Td><TokenCell token="--primary" display="primary" /></Td>
328
328
  </tr>
329
329
  <tr>
330
330
  <Td mono>disabled</Td>
@@ -351,7 +351,7 @@ export const StateBehavior: Story = {
351
351
  <H3>Selected 色彩跟 SegmentedControl 完全一致</H3>
352
352
  <Desc>
353
353
  兩者都是 pill 選擇語意(filter / 單選 radio),選中態視覺語言統一:
354
- primary-hover 染 border + text,底色不變。只有「連體 vs 獨立」的視覺差異,選中規則是同一套。
354
+ primary base 染 border + text,底色不變。只有「連體 vs 獨立」的視覺差異,選中規則是同一套。
355
355
  </Desc>
356
356
  <div className="border border-dashed border-divider rounded-md p-4">
357
357
  <div className="text-caption text-fg-muted mb-2">Chip(獨立 pill)</div>
@@ -416,7 +416,7 @@ export const Accessibility = {
416
416
  render: () => (
417
417
  <div className="max-w-3xl text-body text-fg-secondary">
418
418
  <h3 className="text-h5 text-foreground mb-2">無障礙設計</h3>
419
- <p className="whitespace-pre-line">{"詳 「無障礙設計」段。摘要:\n\n ARIA / Pattern :繼承 Radix toggle-group 鍵盤模式(role / aria-* / 鍵盤導覽)。詳 [Radix Accessibility docs](https://www.radix-ui.com/primitives/docs/components/toggle-group#accessibility)。\n\n Keyboard 行為 :\n\n- Tab — 進入整組 chip(只有一個 Tab 停留點)\n- ←/→ — 在組內 chip 之間移動焦點\n- Tab — 再按一次離開整組\n- Enter / Space — 切換選取\n\n Focus :焦點環為 2px 的 --ring 色描邊(focus-visible 才顯示)。整組 chip 用方向鍵移動焦點、Tab 進出整組——這不是 Dialog 那種把焦點鎖在裡面的行為。\n\n 驗證 :Storybook a11y addon panel 應 0 critical violation;鍵盤完整可操作(無需滑鼠)。WCAG AA 對比 ≥ 4.5:1(文字)"}</p>
419
+ <p className="whitespace-pre-line">{"詳 「無障礙設計」段。摘要:\n\n ARIA / Pattern :繼承 Radix toggle-group 鍵盤模式(role / aria-* / 鍵盤導覽)。詳 [Radix Accessibility docs](https://www.radix-ui.com/primitives/docs/components/toggle-group#accessibility)。\n\n Keyboard 行為 :\n\n- Tab — 進入整組 chip(只有一個 Tab 停留點)\n- ←/→ — 在組內 chip 之間移動焦點\n- Tab — 再按一次離開整組\n- Enter / Space — 切換選取\n\n Focus :焦點環為 2px 的 --ring (ring-2 ring-ring ring-offset-1,以 box-shadow 實作並 outline-none;focus-visible 才顯示)。整組 chip 用方向鍵移動焦點、Tab 進出整組——這不是 Dialog 那種把焦點鎖在裡面的行為。\n\n 驗證 :Storybook a11y addon panel 應 0 critical violation;鍵盤完整可操作(無需滑鼠)。WCAG AA 對比 ≥ 4.5:1(文字)"}</p>
420
420
  </div>
421
421
  ),
422
422
  }
@@ -197,8 +197,8 @@ export const SelectedStyleRule: Story = {
197
197
  render: () => (
198
198
  <div>
199
199
  <Rule
200
- title="選中 = primary-hover text + border,底色不變"
201
- note="對齊 SegmentedControl / Tabs / Input 的設計準則 選中規則(primary-hover 同時染文字和線條)。用 bg 強調會讓 chip 看起來變成「按鈕」,混淆多選濾鏡的語意"
200
+ title="選中 = primary text + border,底色不變"
201
+ note="對齊 SegmentedControl / Tabs 的選中 canonical(primary base 同時染文字和線條;semantic.css SSOT,2026-07-06 拍板自 hover 階改 base——瞬時弱、持續強)。用 bg 強調會讓 chip 看起來變成「按鈕」,混淆多選濾鏡的語意"
202
202
  >
203
203
  <ChipGroup type="multiple" defaultValue={['react', 'ts']}>
204
204
  <Chip value="react">React</Chip>
@@ -206,7 +206,7 @@ export const SelectedStyleRule: Story = {
206
206
  <Chip value="ts">TypeScript</Chip>
207
207
  <Chip value="js">JavaScript</Chip>
208
208
  </ChipGroup>
209
- <Label>↑ 選中(React / TypeScript)= primary-hover 文字 + 邊框,底色仍 bg-surface</Label>
209
+ <Label>↑ 選中(React / TypeScript)= primary 文字 + 邊框,底色仍 bg-surface</Label>
210
210
  </Rule>
211
211
 
212
212
  <Rule