@qijenchen/design-system 0.1.0-beta.145 → 0.1.0-beta.147

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 (576) hide show
  1. package/dist/components/Accordion/accordion.d.ts +1 -1
  2. package/dist/components/Accordion/accordion.d.ts.map +1 -1
  3. package/dist/components/Accordion/accordion.js +8 -3
  4. package/dist/components/Accordion/accordion.js.map +1 -1
  5. package/dist/components/AccountMenu/account-menu.d.ts.map +1 -1
  6. package/dist/components/AccountMenu/account-menu.js.map +1 -1
  7. package/dist/components/AgentPanel/agent-panel-fab.d.ts +7 -1
  8. package/dist/components/AgentPanel/agent-panel-fab.d.ts.map +1 -1
  9. package/dist/components/AgentPanel/agent-panel-fab.js +36 -14
  10. package/dist/components/AgentPanel/agent-panel-fab.js.map +1 -1
  11. package/dist/components/AgentPanel/agent-panel-logo.d.ts +3 -3
  12. package/dist/components/AgentPanel/agent-panel-logo.js.map +1 -1
  13. package/dist/components/AgentPanel/agent-panel.d.ts +1 -1
  14. package/dist/components/AgentPanel/agent-panel.d.ts.map +1 -1
  15. package/dist/components/AgentPanel/agent-panel.js +28 -22
  16. package/dist/components/AgentPanel/agent-panel.js.map +1 -1
  17. package/dist/components/AppShell/app-shell.d.ts +1 -1
  18. package/dist/components/AppShell/app-shell.js +1 -1
  19. package/dist/components/AppShell/app-shell.js.map +1 -1
  20. package/dist/components/Avatar/avatar.d.ts +95 -1
  21. package/dist/components/Avatar/avatar.d.ts.map +1 -1
  22. package/dist/components/Avatar/avatar.js +106 -15
  23. package/dist/components/Avatar/avatar.js.map +1 -1
  24. package/dist/components/Avatar/index.js +14 -2
  25. package/dist/components/Breadcrumb/breadcrumb.d.ts +4 -2
  26. package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
  27. package/dist/components/Breadcrumb/breadcrumb.js +2 -2
  28. package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
  29. package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
  30. package/dist/components/BulkActionBar/bulk-action-bar.js +27 -2
  31. package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
  32. package/dist/components/Button/button.d.ts +2 -2
  33. package/dist/components/Button/button.d.ts.map +1 -1
  34. package/dist/components/Button/button.js +118 -22
  35. package/dist/components/Button/button.js.map +1 -1
  36. package/dist/components/Calendar/calendar.d.ts +34 -10
  37. package/dist/components/Calendar/calendar.d.ts.map +1 -1
  38. package/dist/components/Calendar/calendar.js +210 -36
  39. package/dist/components/Calendar/calendar.js.map +1 -1
  40. package/dist/components/Carousel/carousel.d.ts +1 -1
  41. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  42. package/dist/components/Carousel/carousel.js +3 -2
  43. package/dist/components/Carousel/carousel.js.map +1 -1
  44. package/dist/components/Chart/chart.d.ts +1 -0
  45. package/dist/components/Chart/chart.d.ts.map +1 -1
  46. package/dist/components/Chart/chart.js +8 -0
  47. package/dist/components/Chart/chart.js.map +1 -1
  48. package/dist/components/Checkbox/checkbox.d.ts +1 -1
  49. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  50. package/dist/components/Checkbox/checkbox.js +10 -2
  51. package/dist/components/Checkbox/checkbox.js.map +1 -1
  52. package/dist/components/Chip/chip.d.ts +1 -1
  53. package/dist/components/Chip/chip.d.ts.map +1 -1
  54. package/dist/components/Chip/chip.js +4 -2
  55. package/dist/components/Chip/chip.js.map +1 -1
  56. package/dist/components/Combobox/combobox.d.ts +1 -1
  57. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  58. package/dist/components/Combobox/combobox.js +37 -11
  59. package/dist/components/Combobox/combobox.js.map +1 -1
  60. package/dist/components/Command/command.d.ts +1 -1
  61. package/dist/components/Command/command.d.ts.map +1 -1
  62. package/dist/components/Command/command.js +106 -8
  63. package/dist/components/Command/command.js.map +1 -1
  64. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  65. package/dist/components/DataTable/cell-registry.js +3 -2
  66. package/dist/components/DataTable/cell-registry.js.map +1 -1
  67. package/dist/components/DataTable/data-table-column-visibility-panel.d.ts.map +1 -1
  68. package/dist/components/DataTable/data-table-column-visibility-panel.js +24 -24
  69. package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -1
  70. package/dist/components/DataTable/data-table-interaction-layer.d.ts +2 -1
  71. package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
  72. package/dist/components/DataTable/data-table-interaction-layer.js +3 -4
  73. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  74. package/dist/components/DataTable/data-table-sort-manager.d.ts.map +1 -1
  75. package/dist/components/DataTable/data-table-sort-manager.js +20 -20
  76. package/dist/components/DataTable/data-table-sort-manager.js.map +1 -1
  77. package/dist/components/DataTable/data-table.d.ts +8 -5
  78. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  79. package/dist/components/DataTable/data-table.js +197 -71
  80. package/dist/components/DataTable/data-table.js.map +1 -1
  81. package/dist/components/DateGrid/date-grid.d.ts +1 -1
  82. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  83. package/dist/components/DateGrid/date-grid.js +358 -225
  84. package/dist/components/DateGrid/date-grid.js.map +1 -1
  85. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  86. package/dist/components/DatePicker/date-picker.js +16 -1
  87. package/dist/components/DatePicker/date-picker.js.map +1 -1
  88. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  89. package/dist/components/Dialog/dialog.js +55 -43
  90. package/dist/components/Dialog/dialog.js.map +1 -1
  91. package/dist/components/DropdownMenu/dropdown-menu-keyboard.d.ts +86 -0
  92. package/dist/components/DropdownMenu/dropdown-menu-keyboard.d.ts.map +1 -0
  93. package/dist/components/DropdownMenu/dropdown-menu-keyboard.js +172 -0
  94. package/dist/components/DropdownMenu/dropdown-menu-keyboard.js.map +1 -0
  95. package/dist/components/DropdownMenu/dropdown-menu.d.ts +6 -1
  96. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  97. package/dist/components/DropdownMenu/dropdown-menu.js +50 -39
  98. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  99. package/dist/components/Field/field-context.d.ts +4 -0
  100. package/dist/components/Field/field-context.d.ts.map +1 -1
  101. package/dist/components/Field/field-context.js +1 -2
  102. package/dist/components/Field/field-context.js.map +1 -1
  103. package/dist/components/Field/field-edit-keys.d.ts +8 -0
  104. package/dist/components/Field/field-edit-keys.d.ts.map +1 -1
  105. package/dist/components/Field/field-edit-keys.js +3 -0
  106. package/dist/components/Field/field-edit-keys.js.map +1 -1
  107. package/dist/components/Field/field-wrapper.d.ts +18 -1
  108. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  109. package/dist/components/Field/field-wrapper.js +39 -10
  110. package/dist/components/Field/field-wrapper.js.map +1 -1
  111. package/dist/components/Field/field.d.ts +3 -2
  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/FileItem/file-item.d.ts +26 -2
  116. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  117. package/dist/components/FileItem/file-item.js +82 -25
  118. package/dist/components/FileItem/file-item.js.map +1 -1
  119. package/dist/components/FileItem/index.js +4 -2
  120. package/dist/components/FileUpload/file-upload.d.ts +1 -1
  121. package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
  122. package/dist/components/FileUpload/file-upload.js +132 -55
  123. package/dist/components/FileUpload/file-upload.js.map +1 -1
  124. package/dist/components/FileViewer/file-viewer.d.ts +1 -1
  125. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  126. package/dist/components/FileViewer/file-viewer.js +9 -4
  127. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  128. package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -1
  129. package/dist/components/InlineEdit/inline-edit.js +1 -1
  130. package/dist/components/InlineEdit/inline-edit.js.map +1 -1
  131. package/dist/components/Input/input.d.ts.map +1 -1
  132. package/dist/components/Input/input.js +6 -1
  133. package/dist/components/Input/input.js.map +1 -1
  134. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  135. package/dist/components/LinkInput/link-input.js +20 -4
  136. package/dist/components/LinkInput/link-input.js.map +1 -1
  137. package/dist/components/Notice/notice.js +1 -1
  138. package/dist/components/Notice/notice.js.map +1 -1
  139. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  140. package/dist/components/NumberInput/number-input.js +6 -1
  141. package/dist/components/NumberInput/number-input.js.map +1 -1
  142. package/dist/components/OverflowIndicator/index.js +2 -1
  143. package/dist/components/OverflowIndicator/overflow-indicator.d.ts +19 -1
  144. package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
  145. package/dist/components/OverflowIndicator/overflow-indicator.js +47 -27
  146. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  147. package/dist/components/Pagination/pagination.d.ts +1 -1
  148. package/dist/components/Pagination/pagination.js +1 -1
  149. package/dist/components/Pagination/pagination.js.map +1 -1
  150. package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts +7 -6
  151. package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts.map +1 -1
  152. package/dist/components/PeoplePicker/avatar-stack-overflow.js +6 -7
  153. package/dist/components/PeoplePicker/avatar-stack-overflow.js.map +1 -1
  154. package/dist/components/PeoplePicker/people-picker-helpers.d.ts.map +1 -1
  155. package/dist/components/PeoplePicker/people-picker-helpers.js +1 -1
  156. package/dist/components/PeoplePicker/people-picker-helpers.js.map +1 -1
  157. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  158. package/dist/components/PeoplePicker/people-picker.js +2 -2
  159. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  160. package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
  161. package/dist/components/PeoplePicker/person-display.js +26 -28
  162. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  163. package/dist/components/ProfileCard/profile-card.d.ts.map +1 -1
  164. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  165. package/dist/components/RadioGroup/radio-group.d.ts +1 -1
  166. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  167. package/dist/components/RadioGroup/radio-group.js +10 -4
  168. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  169. package/dist/components/Rating/rating.d.ts +28 -6
  170. package/dist/components/Rating/rating.d.ts.map +1 -1
  171. package/dist/components/Rating/rating.js +97 -80
  172. package/dist/components/Rating/rating.js.map +1 -1
  173. package/dist/components/ScrollArea/scroll-area.d.ts +1 -1
  174. package/dist/components/ScrollArea/scroll-area.js +1 -1
  175. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  176. package/dist/components/SegmentedControl/segmented-control.d.ts +1 -1
  177. package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
  178. package/dist/components/SegmentedControl/segmented-control.js +3 -2
  179. package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
  180. package/dist/components/Select/select.d.ts.map +1 -1
  181. package/dist/components/Select/select.js +15 -8
  182. package/dist/components/Select/select.js.map +1 -1
  183. package/dist/components/SelectMenu/index.js +2 -1
  184. package/dist/components/SelectMenu/index.js.map +1 -1
  185. package/dist/components/SelectMenu/select-menu-keyboard.d.ts +59 -0
  186. package/dist/components/SelectMenu/select-menu-keyboard.d.ts.map +1 -0
  187. package/dist/components/SelectMenu/select-menu-keyboard.js +107 -0
  188. package/dist/components/SelectMenu/select-menu-keyboard.js.map +1 -0
  189. package/dist/components/SelectMenu/select-menu.d.ts +1 -22
  190. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  191. package/dist/components/SelectMenu/select-menu.js +18 -34
  192. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  193. package/dist/components/SelectionControl/selection-item.d.ts.map +1 -1
  194. package/dist/components/SelectionControl/selection-item.js +1 -0
  195. package/dist/components/SelectionControl/selection-item.js.map +1 -1
  196. package/dist/components/Sidebar/sidebar.d.ts +7 -9
  197. package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
  198. package/dist/components/Sidebar/sidebar.js +171 -50
  199. package/dist/components/Sidebar/sidebar.js.map +1 -1
  200. package/dist/components/Slider/slider.d.ts +1 -1
  201. package/dist/components/Slider/slider.d.ts.map +1 -1
  202. package/dist/components/Slider/slider.js +9 -4
  203. package/dist/components/Slider/slider.js.map +1 -1
  204. package/dist/components/Steps/steps.d.ts.map +1 -1
  205. package/dist/components/Steps/steps.js +54 -26
  206. package/dist/components/Steps/steps.js.map +1 -1
  207. package/dist/components/Switch/switch.d.ts +1 -1
  208. package/dist/components/Switch/switch.d.ts.map +1 -1
  209. package/dist/components/Switch/switch.js +7 -2
  210. package/dist/components/Switch/switch.js.map +1 -1
  211. package/dist/components/Tabs/tabs.d.ts +1 -1
  212. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  213. package/dist/components/Tabs/tabs.js +39 -4
  214. package/dist/components/Tabs/tabs.js.map +1 -1
  215. package/dist/components/Tag/tag.d.ts +1 -1
  216. package/dist/components/Tag/tag.d.ts.map +1 -1
  217. package/dist/components/Tag/tag.js +3 -2
  218. package/dist/components/Tag/tag.js.map +1 -1
  219. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  220. package/dist/components/Textarea/textarea.js +8 -2
  221. package/dist/components/Textarea/textarea.js.map +1 -1
  222. package/dist/components/TimePicker/time-columns.d.ts.map +1 -1
  223. package/dist/components/TimePicker/time-columns.js +8 -4
  224. package/dist/components/TimePicker/time-columns.js.map +1 -1
  225. package/dist/components/TimePicker/time-picker.d.ts +1 -1
  226. package/dist/components/TimePicker/time-picker.js +1 -1
  227. package/dist/components/TimePicker/time-picker.js.map +1 -1
  228. package/dist/components/TreeView/tree-view.d.ts +9 -5
  229. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  230. package/dist/components/TreeView/tree-view.js +266 -214
  231. package/dist/components/TreeView/tree-view.js.map +1 -1
  232. package/dist/hooks/use-input-modality.d.ts +12 -17
  233. package/dist/hooks/use-input-modality.d.ts.map +1 -1
  234. package/dist/hooks/use-input-modality.js +4 -34
  235. package/dist/hooks/use-input-modality.js.map +1 -1
  236. package/dist/hooks/use-overflow-items.d.ts.map +1 -1
  237. package/dist/hooks/use-overflow-items.js +2 -0
  238. package/dist/hooks/use-overflow-items.js.map +1 -1
  239. package/dist/hooks/use-truncated.d.ts.map +1 -1
  240. package/dist/hooks/use-truncated.js.map +1 -1
  241. package/dist/index.d.ts +3 -0
  242. package/dist/index.d.ts.map +1 -1
  243. package/dist/index.js +23 -3
  244. package/dist/index.js.map +1 -1
  245. package/dist/lib/drag-announcements.d.ts +32 -0
  246. package/dist/lib/drag-announcements.d.ts.map +1 -1
  247. package/dist/lib/drag-announcements.js +39 -1
  248. package/dist/lib/drag-announcements.js.map +1 -1
  249. package/dist/lib/focus-after-trigger.d.ts +45 -0
  250. package/dist/lib/focus-after-trigger.d.ts.map +1 -0
  251. package/dist/lib/focus-after-trigger.js +66 -0
  252. package/dist/lib/focus-after-trigger.js.map +1 -0
  253. package/dist/lib/overlay-coexistence.js +1 -1
  254. package/dist/lib/roving-list-keyboard.d.ts +208 -0
  255. package/dist/lib/roving-list-keyboard.d.ts.map +1 -0
  256. package/dist/lib/roving-list-keyboard.js +174 -0
  257. package/dist/lib/roving-list-keyboard.js.map +1 -0
  258. package/dist/lib/utils.d.ts.map +1 -1
  259. package/dist/lib/utils.js +6 -1
  260. package/dist/lib/utils.js.map +1 -1
  261. package/dist/patterns/element-anatomy/item-anatomy.d.ts +4 -4
  262. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  263. package/dist/patterns/element-anatomy/item-anatomy.js +14 -4
  264. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  265. package/dist/patterns/horizontal-overflow/horizontal-overflow.d.ts.map +1 -1
  266. package/dist/patterns/horizontal-overflow/horizontal-overflow.js +25 -0
  267. package/dist/patterns/horizontal-overflow/horizontal-overflow.js.map +1 -1
  268. package/dist/patterns/overlay-surface/overlay-surface.d.ts.map +1 -1
  269. package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
  270. package/dist/tokens/categorical-color.d.ts +6 -1
  271. package/dist/tokens/categorical-color.d.ts.map +1 -1
  272. package/dist/tokens/categorical-color.js.map +1 -1
  273. package/dist/tokens/uiSize/icon-size.d.ts +1 -1
  274. package/dist/tokens/uiSize/stack-gap.d.ts +18 -0
  275. package/dist/tokens/uiSize/stack-gap.d.ts.map +1 -0
  276. package/dist/tokens/uiSize/stack-gap.js +5 -0
  277. package/dist/tokens/uiSize/stack-gap.js.map +1 -0
  278. package/ds-canonical/fork/codex/agents/skills/performance-audit/SKILL.md +1 -1
  279. package/ds-canonical/fork/codex/hooks.json +1 -1
  280. package/ds-canonical/fork/consumer/lock.json +17 -14
  281. package/ds-canonical/fork/consumer/managed-files/423729840acdd43c812f17c245b871716c6b60f76e4d3e94864d0e34a815bf8d.blob +9 -0
  282. package/ds-canonical/fork/consumer/managed-files/46f10bc55cdfdf834d0749eebff42ea0bb8576f361b36d2b6ba1851354c50150.blob +47 -9
  283. package/ds-canonical/fork/consumer/managed-files/5a0d134a2645114c8caac20458070b00a7f0fd55b3e8805b7325cb4cf52fa15e.blob +110 -0
  284. package/ds-canonical/fork/consumer/managed-files/5af0a46760af338d51fd0f7950675f8e97f7d2f09386b8818fcd7a58e6654e75.blob +790 -0
  285. package/ds-canonical/fork/consumer/managed-files/68bfcddff9c4a16388c248cd34fb66163e9e64b3d809a48ef3984f6ae4ff6d12.blob +211 -0
  286. package/ds-canonical/fork/consumer/managed-files/7eb6cc60abfdabaf6617c336058fad1454ccde08889295767bfd9b7fd153cc6e.blob +45 -6
  287. package/ds-canonical/fork/consumer/managed-files/92e4828cdcb96ff0d01daa16f6885f24045a8e72820665650f2a1890dc3a446c.blob +42 -2
  288. package/ds-canonical/fork/consumer/managed-files/ab072e7f2eca51f0e18a5e18c4b333260ee5d219a12cc6df5bb2d683f0b2734d.blob +27 -1
  289. package/ds-canonical/fork/consumer/managed-files/e71c65f45c6af7ce12b6b3c0d8028f185da5b719e2050c9b2a6b243d41ae4878.blob +62 -14
  290. package/ds-canonical/fork/governance.lock +41 -17
  291. package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +16 -4
  292. package/ds-canonical/fork/hooks/check_consumer_code_quality.sh +3 -1
  293. package/ds-canonical/fork/hooks/check_pattern_invariants.sh +13 -9
  294. package/ds-canonical/fork/launchers/settings-hooks.json +1 -1
  295. package/ds-canonical/fork/manifest.json +17 -11
  296. package/ds-canonical/fork/providers/claude/skills/performance-audit/SKILL.md +1 -1
  297. package/ds-canonical/fork/providers/codex/hook-config.json +1 -1
  298. package/ds-canonical/fork/providers/codex/skills/performance-audit/SKILL.md +1 -1
  299. package/ds-canonical/fork/skills/performance-audit/SKILL.md +1 -1
  300. package/ds-canonical/hooks/check_consumer_app_invariants.sh +16 -4
  301. package/ds-canonical/hooks/check_consumer_code_quality.sh +3 -1
  302. package/ds-canonical/hooks/check_datatable_invariants.sh +4 -1
  303. package/ds-canonical/hooks/check_pattern_invariants.sh +13 -9
  304. package/ds-canonical/hooks/check_plugin_fork_health.sh +8 -1
  305. package/ds-canonical/hooks/check_propose_discipline.sh +8 -1
  306. package/ds-canonical/hooks/check_storybook_addon_packaging.sh +8 -1
  307. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +64 -2
  308. package/ds-canonical/hooks/lib/_overlay_handcraft.sh +5 -2
  309. package/ds-canonical/hooks/registrations.json +12 -7
  310. package/ds-canonical/hooks/session_start_governance_check.sh +16 -0
  311. package/ds-canonical/hooks/tests/test_check_consumer_app_invariants.sh +25 -0
  312. package/ds-canonical/hooks/tests/test_check_consumer_code_quality.sh +4 -0
  313. package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +14 -0
  314. package/ds-canonical/hooks/tests/test_check_datatable_invariants.sh +24 -0
  315. package/ds-canonical/hooks/tests/test_check_overlay_handcraft.sh +32 -0
  316. package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +59 -0
  317. package/ds-canonical/hooks/tests/test_check_plugin_fork_health.sh +32 -1
  318. package/ds-canonical/hooks/tests/test_check_propose_discipline.sh +21 -0
  319. package/ds-canonical/hooks/tests/test_check_storybook_addon_packaging.sh +21 -0
  320. package/ds-canonical/hooks/tests/test_check_substantive_edit_approval_preflight.sh +97 -0
  321. package/ds-canonical/hooks/tests/test_session_start_governance_check.sh +49 -0
  322. package/ds-canonical/references/README.md +12 -6
  323. package/ds-canonical/references/drag-canonical.md +3 -1
  324. package/ds-canonical/references/focus-canonical.md +26 -13
  325. package/ds-canonical/references/governance-audit-coverage.md +1 -1
  326. package/ds-canonical/references/hit-area-canonical.md +361 -0
  327. package/ds-canonical/references/item-anatomy-recipe.md +4 -2
  328. package/ds-canonical/references/keyboard-model-canonical.md +559 -0
  329. package/ds-canonical/rules/meta-patterns.md +4 -4
  330. package/ds-canonical/rules/self-verify.md +4 -4
  331. package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +1 -1
  332. package/ds-canonical/skills/design-system-audit/references/historical-bugs.md +96 -0
  333. package/ds-canonical/skills/knowledge-prune/SKILL.md +4 -1
  334. package/ds-canonical/skills/performance-audit/SKILL.md +1 -1
  335. package/ds-story-manifest.json +19 -7
  336. package/llms-full.txt +2 -2
  337. package/llms.txt +2 -2
  338. package/package.json +3 -1
  339. package/src/components/Accordion/accordion.anatomy.stories.tsx +3 -3
  340. package/src/components/Accordion/accordion.spec.md +1 -1
  341. package/src/components/Accordion/accordion.tsx +8 -3
  342. package/src/components/AccountMenu/account-menu.spec.md +2 -3
  343. package/src/components/AccountMenu/account-menu.tsx +0 -1
  344. package/src/components/AgentPanel/agent-decision-card.spec.md +88 -0
  345. package/src/components/AgentPanel/agent-panel-fab.spec.md +151 -0
  346. package/src/components/AgentPanel/agent-panel-fab.tsx +67 -24
  347. package/src/components/AgentPanel/agent-panel-logo.spec.md +87 -0
  348. package/src/components/AgentPanel/agent-panel-logo.tsx +3 -3
  349. package/src/components/AgentPanel/agent-panel.anatomy.stories.tsx +1 -1
  350. package/src/components/AgentPanel/agent-panel.principles.stories.tsx +1 -1
  351. package/src/components/AgentPanel/agent-panel.spec.md +30 -275
  352. package/src/components/AgentPanel/agent-panel.stories.tsx +113 -8
  353. package/src/components/AgentPanel/agent-panel.tsx +59 -22
  354. package/src/components/Alert/alert.anatomy.stories.tsx +1 -1
  355. package/src/components/AppShell/app-shell.tsx +1 -1
  356. package/src/components/AspectRatio/aspect-ratio.anatomy.stories.tsx +10 -15
  357. package/src/components/Avatar/avatar.anatomy.stories.tsx +23 -26
  358. package/src/components/Avatar/avatar.principles.stories.tsx +10 -10
  359. package/src/components/Avatar/avatar.spec.md +45 -6
  360. package/src/components/Avatar/avatar.stories.tsx +8 -8
  361. package/src/components/Avatar/avatar.tsx +227 -21
  362. package/src/components/Badge/badge.anatomy.stories.tsx +16 -21
  363. package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +3 -3
  364. package/src/components/Breadcrumb/breadcrumb.spec.md +3 -3
  365. package/src/components/Breadcrumb/breadcrumb.tsx +5 -3
  366. package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +1 -1
  367. package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +1 -1
  368. package/src/components/BulkActionBar/bulk-action-bar.spec.md +1 -0
  369. package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +57 -2
  370. package/src/components/BulkActionBar/bulk-action-bar.tsx +32 -2
  371. package/src/components/Button/button.anatomy.stories.tsx +174 -33
  372. package/src/components/Button/button.spec.md +23 -8
  373. package/src/components/Button/button.tsx +116 -21
  374. package/src/components/Calendar/calendar.anatomy.stories.tsx +34 -11
  375. package/src/components/Calendar/calendar.principles.stories.tsx +44 -1
  376. package/src/components/Calendar/calendar.spec.md +97 -20
  377. package/src/components/Calendar/calendar.stories.tsx +101 -4
  378. package/src/components/Calendar/calendar.tsx +379 -74
  379. package/src/components/Carousel/carousel.anatomy.stories.tsx +16 -17
  380. package/src/components/Carousel/carousel.spec.md +48 -2
  381. package/src/components/Carousel/carousel.tsx +9 -3
  382. package/src/components/Chart/chart.anatomy.stories.tsx +7 -5
  383. package/src/components/Chart/chart.spec.md +32 -7
  384. package/src/components/Chart/chart.tsx +9 -0
  385. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +21 -28
  386. package/src/components/Checkbox/checkbox.spec.md +4 -2
  387. package/src/components/Checkbox/checkbox.tsx +10 -2
  388. package/src/components/Chip/chip.anatomy.stories.tsx +1 -1
  389. package/src/components/Chip/chip.spec.md +2 -1
  390. package/src/components/Chip/chip.tsx +4 -2
  391. package/src/components/Combobox/combobox.anatomy.stories.tsx +30 -37
  392. package/src/components/Combobox/combobox.spec.md +11 -3
  393. package/src/components/Combobox/combobox.tsx +43 -12
  394. package/src/components/Command/command.spec.md +11 -0
  395. package/src/components/Command/command.tsx +143 -10
  396. package/src/components/DataTable/cell-registry.tsx +5 -1
  397. package/src/components/DataTable/data-table-column-visibility-panel.tsx +5 -6
  398. package/src/components/DataTable/data-table-interaction-layer.tsx +27 -14
  399. package/src/components/DataTable/data-table-known-defects.spec.md +72 -0
  400. package/src/components/DataTable/data-table-scroll-performance.spec.md +295 -0
  401. package/src/components/DataTable/data-table-sort-manager.tsx +5 -6
  402. package/src/components/DataTable/data-table.anatomy.stories.tsx +1 -1
  403. package/src/components/DataTable/data-table.css +64 -0
  404. package/src/components/DataTable/data-table.spec.md +70 -346
  405. package/src/components/DataTable/data-table.stories.tsx +56 -9
  406. package/src/components/DataTable/data-table.tsx +392 -103
  407. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +2 -2
  408. package/src/components/DateGrid/date-grid.spec.md +163 -1
  409. package/src/components/DateGrid/date-grid.tsx +270 -7
  410. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +18 -27
  411. package/src/components/DatePicker/date-picker.spec.md +6 -2
  412. package/src/components/DatePicker/date-picker.tsx +20 -1
  413. package/src/components/Dialog/dialog.spec.md +6 -2
  414. package/src/components/Dialog/dialog.stories.tsx +18 -9
  415. package/src/components/Dialog/dialog.tsx +83 -48
  416. package/src/components/DropdownMenu/dropdown-menu-keyboard.ts +255 -0
  417. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +32 -33
  418. package/src/components/DropdownMenu/dropdown-menu.spec.md +18 -5
  419. package/src/components/DropdownMenu/dropdown-menu.stories.tsx +58 -1
  420. package/src/components/DropdownMenu/dropdown-menu.tsx +70 -33
  421. package/src/components/Empty/empty.spec.md +1 -2
  422. package/src/components/Field/field-context.ts +9 -4
  423. package/src/components/Field/field-controls.spec.md +5 -2
  424. package/src/components/Field/field-edit-keys.ts +12 -0
  425. package/src/components/Field/field-wrapper.tsx +76 -10
  426. package/src/components/Field/field.anatomy.stories.tsx +1 -1
  427. package/src/components/Field/field.spec.md +11 -6
  428. package/src/components/Field/field.stories.tsx +24 -1
  429. package/src/components/Field/field.tsx +10 -4
  430. package/src/components/FileItem/file-item.anatomy.stories.tsx +20 -10
  431. package/src/components/FileItem/file-item.principles.stories.tsx +14 -7
  432. package/src/components/FileItem/file-item.spec.md +50 -48
  433. package/src/components/FileItem/file-item.stories.tsx +41 -28
  434. package/src/components/FileItem/file-item.tsx +184 -50
  435. package/src/components/FileUpload/file-upload.anatomy.stories.tsx +11 -15
  436. package/src/components/FileUpload/file-upload.spec.md +27 -1
  437. package/src/components/FileUpload/file-upload.stories.tsx +33 -0
  438. package/src/components/FileUpload/file-upload.tsx +173 -43
  439. package/src/components/FileViewer/file-viewer.tsx +9 -4
  440. package/src/components/HoverCard/hover-card.stories.tsx +2 -2
  441. package/src/components/InlineEdit/inline-edit.spec.md +2 -0
  442. package/src/components/InlineEdit/inline-edit.tsx +8 -3
  443. package/src/components/Input/input.anatomy.stories.tsx +23 -31
  444. package/src/components/Input/input.spec.md +11 -0
  445. package/src/components/Input/input.tsx +6 -1
  446. package/src/components/LinkInput/link-input.anatomy.stories.tsx +18 -21
  447. package/src/components/LinkInput/link-input.principles.stories.tsx +4 -4
  448. package/src/components/LinkInput/link-input.spec.md +12 -1
  449. package/src/components/LinkInput/link-input.tsx +37 -4
  450. package/src/components/Menu/menu-item.anatomy.stories.tsx +50 -56
  451. package/src/components/Menu/menu-item.spec.md +1 -1
  452. package/src/components/Menu/menu-item.stories.tsx +48 -42
  453. package/src/components/Notice/notice.anatomy.stories.tsx +8 -16
  454. package/src/components/Notice/notice.spec.md +1 -1
  455. package/src/components/Notice/notice.tsx +1 -1
  456. package/src/components/NumberInput/number-input.anatomy.stories.tsx +21 -28
  457. package/src/components/NumberInput/number-input.spec.md +2 -0
  458. package/src/components/NumberInput/number-input.tsx +8 -1
  459. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +28 -48
  460. package/src/components/OverflowIndicator/overflow-indicator.spec.md +13 -1
  461. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +12 -9
  462. package/src/components/OverflowIndicator/overflow-indicator.tsx +32 -12
  463. package/src/components/Pagination/pagination.spec.md +1 -0
  464. package/src/components/Pagination/pagination.tsx +1 -1
  465. package/src/components/PeoplePicker/avatar-stack-overflow.ts +14 -13
  466. package/src/components/PeoplePicker/people-picker-helpers.ts +5 -2
  467. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +1 -1
  468. package/src/components/PeoplePicker/people-picker.spec.md +4 -2
  469. package/src/components/PeoplePicker/people-picker.tsx +1 -4
  470. package/src/components/PeoplePicker/person-display.tsx +45 -28
  471. package/src/components/Popover/popover.anatomy.stories.tsx +1 -1
  472. package/src/components/Popover/popover.principles.stories.tsx +3 -12
  473. package/src/components/Popover/popover.spec.md +7 -3
  474. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
  475. package/src/components/ProfileCard/profile-card.spec.md +1 -1
  476. package/src/components/ProfileCard/profile-card.tsx +0 -3
  477. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +11 -21
  478. package/src/components/ProgressBar/progress-bar.spec.md +1 -1
  479. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +17 -24
  480. package/src/components/RadioGroup/radio-group.spec.md +1 -1
  481. package/src/components/RadioGroup/radio-group.tsx +22 -4
  482. package/src/components/Rating/rating.anatomy.stories.tsx +131 -124
  483. package/src/components/Rating/rating.principles.stories.tsx +69 -43
  484. package/src/components/Rating/rating.spec.md +142 -63
  485. package/src/components/Rating/rating.stories.tsx +11 -15
  486. package/src/components/Rating/rating.tsx +131 -117
  487. package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
  488. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +6 -3
  489. package/src/components/ScrollArea/scroll-area.spec.md +3 -1
  490. package/src/components/ScrollArea/scroll-area.stories.tsx +5 -2
  491. package/src/components/ScrollArea/scroll-area.tsx +1 -1
  492. package/src/components/SegmentedControl/segmented-control.anatomy.stories.tsx +1 -1
  493. package/src/components/SegmentedControl/segmented-control.spec.md +2 -2
  494. package/src/components/SegmentedControl/segmented-control.tsx +3 -2
  495. package/src/components/Select/select.anatomy.stories.tsx +26 -35
  496. package/src/components/Select/select.spec.md +4 -3
  497. package/src/components/Select/select.tsx +11 -4
  498. package/src/components/SelectMenu/select-menu-keyboard.ts +218 -0
  499. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +6 -17
  500. package/src/components/SelectMenu/select-menu.spec.md +12 -4
  501. package/src/components/SelectMenu/select-menu.tsx +21 -56
  502. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +9 -33
  503. package/src/components/SelectionControl/selection-item.spec.md +2 -2
  504. package/src/components/SelectionControl/selection-item.tsx +3 -1
  505. package/src/components/Separator/separator.stories.tsx +4 -4
  506. package/src/components/Sheet/sheet.principles.stories.tsx +31 -13
  507. package/src/components/Sidebar/sidebar.anatomy.stories.tsx +1 -1
  508. package/src/components/Sidebar/sidebar.spec.md +73 -6
  509. package/src/components/Sidebar/sidebar.stories.tsx +82 -4
  510. package/src/components/Sidebar/sidebar.tsx +281 -64
  511. package/src/components/Skeleton/skeleton.spec.md +1 -1
  512. package/src/components/Slider/slider.anatomy.stories.tsx +1 -1
  513. package/src/components/Slider/slider.spec.md +6 -5
  514. package/src/components/Slider/slider.tsx +13 -8
  515. package/src/components/Steps/steps-state-visuals.spec.md +157 -0
  516. package/src/components/Steps/steps.anatomy.stories.tsx +1 -0
  517. package/src/components/Steps/steps.spec.md +59 -142
  518. package/src/components/Steps/steps.stories.tsx +1 -1
  519. package/src/components/Steps/steps.tsx +125 -33
  520. package/src/components/Switch/switch.spec.md +3 -3
  521. package/src/components/Switch/switch.tsx +6 -1
  522. package/src/components/Tabs/tabs.spec.md +5 -1
  523. package/src/components/Tabs/tabs.tsx +42 -3
  524. package/src/components/Tag/tag.anatomy.stories.tsx +24 -28
  525. package/src/components/Tag/tag.spec.md +10 -11
  526. package/src/components/Tag/tag.tsx +3 -3
  527. package/src/components/Textarea/textarea.tsx +7 -1
  528. package/src/components/TimePicker/time-columns.tsx +9 -4
  529. package/src/components/TimePicker/time-picker.spec.md +4 -1
  530. package/src/components/TimePicker/time-picker.tsx +1 -1
  531. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +6 -15
  532. package/src/components/Tooltip/tooltip.principles.stories.tsx +4 -16
  533. package/src/components/Tooltip/tooltip.spec.md +2 -1
  534. package/src/components/TreeView/tree-view.anatomy.stories.tsx +35 -13
  535. package/src/components/TreeView/tree-view.spec.md +67 -38
  536. package/src/components/TreeView/tree-view.stories.tsx +221 -12
  537. package/src/components/TreeView/tree-view.tsx +463 -356
  538. package/src/hooks/use-input-modality.ts +17 -46
  539. package/src/hooks/use-overflow-items.ts +4 -0
  540. package/src/hooks/use-truncated.ts +3 -2
  541. package/src/index.ts +17 -0
  542. package/src/lib/README.md +2 -0
  543. package/src/lib/drag-announcements.ts +78 -0
  544. package/src/lib/focus-after-trigger.ts +102 -0
  545. package/src/lib/roving-list-keyboard.ts +400 -0
  546. package/src/lib/utils.ts +5 -0
  547. package/src/patterns/action-bar/action-bar.spec.md +2 -0
  548. package/src/patterns/element-anatomy/inline-action.spec.md +16 -12
  549. package/src/patterns/element-anatomy/item-anatomy.spec.md +98 -6
  550. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +75 -70
  551. package/src/patterns/element-anatomy/item-anatomy.tsx +17 -6
  552. package/src/patterns/element-anatomy/truncated-text.spec.md +1 -0
  553. package/src/patterns/header-canonical/header-canonical.spec.md +3 -2
  554. package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +2 -0
  555. package/src/patterns/horizontal-overflow/horizontal-overflow.stories.tsx +36 -0
  556. package/src/patterns/horizontal-overflow/horizontal-overflow.tsx +28 -0
  557. package/src/patterns/overlay-surface/overlay-chrome-sizing.spec.md +180 -0
  558. package/src/patterns/overlay-surface/overlay-surface.spec.md +8 -162
  559. package/src/patterns/overlay-surface/overlay-surface.tsx +5 -3
  560. package/src/patterns/resize-handle/resize-handle.spec.md +3 -3
  561. package/src/styles/base.css +1 -1
  562. package/src/tokens/categorical-color.ts +6 -1
  563. package/src/tokens/color/color.spec.md +91 -24
  564. package/src/tokens/color/color.stories.tsx +50 -2
  565. package/src/tokens/color/primitives.css +41 -38
  566. package/src/tokens/color/semantic.css +105 -36
  567. package/src/tokens/layoutSpace/layoutSpace.spec.md +2 -0
  568. package/src/tokens/motion/motion.spec.md +31 -8
  569. package/src/tokens/orphan-tokens.spec.md +1 -1
  570. package/src/tokens/uiSize/icon-size.ts +1 -1
  571. package/src/tokens/uiSize/stack-gap.ts +17 -0
  572. package/src/tokens/uiSize/uiSize.css +12 -0
  573. package/src/tokens/uiSize/uiSize.spec.md +17 -16
  574. package/src/tokens/uiSize/uiSize.stories.tsx +1 -1
  575. package/dist/node_modules/aria-hidden/dist/es2015/index.js +0 -147
  576. package/dist/node_modules/aria-hidden/dist/es2015/index.js.map +0 -1
@@ -36,12 +36,24 @@ import {
36
36
  ItemLabel,
37
37
  ItemInlineAction,
38
38
  ItemInlineActionButton,
39
+ type ItemInlineActionButtonProps,
40
+ ItemSuffix,
39
41
  RowSizeProvider,
40
42
  getUniformPrefixSlotStyle,
41
43
  ROW_PADDING_BY_SIZE,
42
44
  type RowSize,
43
45
  type InlineActionConfig,
44
46
  } from "@/design-system/patterns/element-anatomy/item-anatomy"
47
+ // 「列上有小按鈕的一串」鍵盤路線的唯一判定與執行(與 TreeView / FileUpload / Command 共用;見下方 SidebarMenu)
48
+ import {
49
+ applyRovingAction,
50
+ isRovingControlReachable,
51
+ isTextEntryElement,
52
+ pickRovingTabStop,
53
+ removeRovingControlsFromTabOrder,
54
+ resolveRovingKey,
55
+ setRovingTabIndex,
56
+ } from "@/design-system/lib/roving-list-keyboard"
45
57
 
46
58
  /**
47
59
  * Sidebar
@@ -613,14 +625,14 @@ const SidebarContent = React.forwardRef<
613
625
  // SidebarContent 用 ScrollArea 處理長列表 scroll——跨 OS 一致不吃寬度(macOS
614
626
  // overlay vs Windows/Linux always-visible 差異見 scroll-area.tsx 註解)。
615
627
  // 呼吸空間和分隔線由 SidebarGroup 自己處理(對齊 MenuGroup 的 py-2 + [&+&]:border-t)。
616
- // ScrollArea Root 本身 overflow-hidden,icon-collapsed 時不會露出 scroll chrome。
628
+ // ScrollArea Root 本身 overflow-hidden,icon-collapsed 時不會露出 scroll chrome。捲動區本身不當 Tab 停靠點(viewportTabIndex={-1};待辦總帳 B9,依 `../ScrollArea/scroll-area.spec.md:103`「內容已有自帶 focusable + 鍵盤處理的元素」時傳 -1):內容是一串串自帶方向鍵的 SidebarMenu,焦點落進任何一項都會把它捲進視野;先前 Tab 第一站是這個捲動區(「混合內容」實測第 1 站),停在上面按 ↓ 焦點不動。
617
629
  className={cn(
618
630
  "flex min-h-0 flex-1 flex-col",
619
631
  className
620
632
  )}
621
633
  {...props}
622
634
  >
623
- <ScrollArea className="flex-1">
635
+ <ScrollArea className="flex-1" viewportTabIndex={-1}>
624
636
  <div className="flex flex-col">{children}</div>
625
637
  </ScrollArea>
626
638
  </div>
@@ -785,7 +797,10 @@ const SidebarGroupLabel = React.forwardRef<
785
797
  //
786
798
  // **為什麼 chevron 是 inline action 而非整個 label 是 trigger**:
787
799
  // 1. 同 sidebar 內的 inline action 視覺必須一致(fg-muted → fg-secondary、hover bg、圓角)
788
- // 2. Linear / Notion / Finder 等世界級 sidebar 都是「label 裝飾、chevron 互動」
800
+ // 2. Linear / Finder 等世界級 sidebar 是「label 裝飾、chevron 互動」。**Notion 不是**(2026-09-29 出處更正,待辦總帳 N46):
801
+ // Notion 的側欄說明頁逐字 "Click on the name of a sidebar section — Teamspaces, Shared, or Private — to collapse it"
802
+ // (https://www.notion.com/help/navigate-with-the-sidebar)—— 點區段名稱就收合;本 DS 沒有跟,理由是第 1 與第 3 點。
803
+ // 2026-09-29 前這裡把 Notion 列在同一邊,是憑印象寫的(M22)。
789
804
  // 3. 整個 label 當 trigger 會把 label 升格為 button,跟非 collapsible group 的 label
790
805
  // 語意不一致(一個是 div、一個是 button),無障礙與視覺焦點都會跳動
791
806
  if (group?.collapsible) {
@@ -838,25 +853,35 @@ const SidebarGroupLabel = React.forwardRef<
838
853
  })
839
854
  SidebarGroupLabel.displayName = "SidebarGroupLabel"
840
855
 
856
+ // ⚠️ 2026-09-24:這兩顆先前是 shadcn 原樣帶進來的**手刻品**(`b7b34721` 把 DS 搬進 npm workspace
857
+ // 時一起進來,之後從未跟著本 DS 的行內動作 canonical 遷移)。它們自己寫死
858
+ // `aspect-square w-5 ... [&>svg]:size-4`,也就是 **16 圖示裝在 20 盒裡** —— 而
859
+ // `patterns/element-anatomy/inline-action.spec.md` 的尺寸表只有兩種組合(16 圖示配 18 底色、
860
+ // 20 圖示配 22 底色),**20 兩種都不是**。同一個檔案 :826 的收合箭頭早就在消費
861
+ // `ItemInlineActionButton`,於是同一個元件裡兩套幾何並存。
862
+ // 依 M23(DS 既有 canonical 優先)與 M30(wrapper 必須繼承 primitive,不得平行宣告)改為委派。
863
+ // API 隨之從 children 改成 `icon` prop —— 這是 breaking change,刻意不留 children 後備:
864
+ // M23(f)「『向後相容』不是把新裁示變成可選的理由」。
865
+ //
866
+ // 命中區 ≡ 懸停底色由 primitive 自己保證(hit-area-canonical「一-4 細則」:行內動作平時沒有可視邊界,
867
+ // 判準是懸停回饋形狀,不是「可視形狀」;`item-anatomy.tsx:730` 那塊**刻意沒有**
868
+ // `pointer-events-none` 的懸停底色 span)。先前這裡另有一圈 `after:-inset-2 after:md:hidden`,
869
+ // 只在 <md 生效、每邊多 8px,實測上下各越出宿主 `<li>` 2px 並蓋掉緊貼的下一列列鈕,已於同日移除。
841
870
  const SidebarGroupAction = React.forwardRef<
842
871
  HTMLButtonElement,
843
- React.ComponentProps<"button"> & { asChild?: boolean }
844
- >(({ className, asChild = false, ...props }, ref) => {
845
- const Comp = asChild ? Slot : "button"
846
- return (
847
- <Comp
848
- ref={ref}
849
- data-sidebar="group-action"
850
- className={cn(
851
- "absolute right-[var(--layout-space-loose)] top-2 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-fg-muted hover:bg-neutral-hover hover:text-fg-secondary [&>svg]:size-4 [&>svg]:shrink-0",
852
- "after:absolute after:-inset-2 after:md:hidden",
853
- "group-data-[collapsible=icon]:hidden",
854
- className
855
- )}
856
- {...props}
857
- />
858
- )
859
- })
872
+ Omit<ItemInlineActionButtonProps, "size">
873
+ >(({ className, ...props }, ref) => (
874
+ <ItemInlineActionButton
875
+ ref={ref}
876
+ data-sidebar="group-action"
877
+ className={cn(
878
+ "absolute right-[var(--layout-space-loose)] top-2",
879
+ "group-data-[collapsible=icon]:hidden",
880
+ className
881
+ )}
882
+ {...props}
883
+ />
884
+ ))
860
885
  SidebarGroupAction.displayName = "SidebarGroupAction"
861
886
 
862
887
  // ── Menu ───────────────────────────────────────────────────────────────────
@@ -868,18 +893,175 @@ SidebarGroupAction.displayName = "SidebarGroupAction"
868
893
  // 固定槽,跨 menu 跨 group 對齊。要啟用,在 `<SidebarProvider uniformPrefix>` 全域 opt-in
869
894
  // (explicit > implicit,見 sidebar.spec.md「Sidebar 全域 prefix 對齊」段)。SidebarMenu 沒有
870
895
  // per-menu 覆寫——沒有真實 use case,YAGNI。
896
+ //
897
+ // ── 鍵盤:一串 SidebarMenu = 一個 Tab 停靠點(2026-09-25,待辦總帳 B9 乙)──
898
+ // 決策出處 = governance/planning/2026-09-25-interaction-and-hover-remediation.md B9,user 逐字:
899
+ // 「確定建議符合我們一致的設計語言且不違背世界級的設計就照建議」(附條件同意;條件查證成立記在同檔 B9)。
900
+ // 規格 SSOT = sidebar.spec.md「鍵盤:一串 SidebarMenu = 一個 Tab 停靠點」;按鍵判定與執行 = lib/roving-list-keyboard.ts
901
+ // (2026-09-26 與 TreeView / FileUpload / Command 四份合一,同檔〇節「按鍵規則合併」),此處只剩側欄自己的 DOM:誰是列、誰是動作鈕。
902
+ //
903
+ // 實作 = roving tabindex,**直接改 DOM 的 tabindex 屬性**,不經 React props:
904
+ // - 列上的動作鈕有兩個出口不歸本元件渲染 —— `ItemInlineAction`(item-anatomy.tsx,沒有 tabIndex prop)
905
+ // 與 consumer 的 `inlineActionsSlot`,只能在 DOM 上把它們收成 -1。同一件事只用一種機制(M17),
906
+ // 所以列鈕的 0 / -1 也在同一個函式裡寫(AI 推導的工程取捨)。
907
+ // - 沒有 JS(SSR 首屏、hydration 前)時什麼都不寫 → 退回瀏覽器原生:每顆都可 Tab,不會變成到不了。
908
+ // - ARIA 角色維持 ul / li / button,不改成 grid / tree:狀態是「你現在在這一頁」(aria-current),
909
+ // 不是「在控件裡被挑中」(aria-selected)。
910
+ const SIDEBAR_MENU_BUTTON = '[data-sidebar="menu-button"]'
911
+ // 列上的動作鈕 = 宣告式 inlineActions / inlineActionsSlot(包在 menu-inline-actions 裡)+ SidebarMenuAction 本身
912
+ const SIDEBAR_ROW_ACTIONS =
913
+ '[data-sidebar="menu-inline-actions"] :is(button, a[href], [role="button"]), [data-sidebar="menu-action"]'
914
+
915
+ type SidebarMenuRow = { button: HTMLElement; actions: HTMLElement[] }
916
+
917
+ /** 這一串的每一列(只看直接子層 li,依畫面順序)與它自己的動作鈕(依畫面順序;含此刻走不到的,收 -1 時一併收)。 */
918
+ function getSidebarMenuRows(menu: HTMLElement): SidebarMenuRow[] {
919
+ const rows: SidebarMenuRow[] = []
920
+ for (const child of Array.from(menu.children)) {
921
+ if (child.tagName !== "LI") continue
922
+ const button = child.querySelector<HTMLElement>(SIDEBAR_MENU_BUTTON)
923
+ if (!button) continue
924
+ rows.push({ button, actions: Array.from(child.querySelectorAll<HTMLElement>(SIDEBAR_ROW_ACTIONS)) })
925
+ }
926
+ return rows
927
+ }
928
+
929
+ // 原生 disabled 本來就聚焦不了 → 跳過(sidebar.spec.md「Disabled item」)。aria-disabled 仍可聚焦,照舊。
930
+ const isFocusableSidebarTarget = (el: HTMLElement) => !(el as HTMLButtonElement).disabled
931
+ // 畫得出來才走得到:icon 模式的動作鈕、被收起的整組都是 display:none(opacity-0 的滑過才出現鈕不算隱藏)
932
+ const isRenderedSidebarTarget = (el: HTMLElement) => el.getClientRects().length > 0
933
+
934
+ /**
935
+ * 哪些列能當落點(方向鍵與 Tab 停靠點共用)。整串看得見時,略過個別被藏起來的列;整串被藏(icon 模式收起的群組)時
936
+ * 不看可見性,免得重新展開後整串 0 顆可 Tab(展開是祖先屬性變化,本串收不到 mutation)。
937
+ */
938
+ function sidebarRowNavigability(menu: HTMLElement, rows: SidebarMenuRow[]) {
939
+ const checkRendered =
940
+ isRenderedSidebarTarget(menu) &&
941
+ rows.some((row) => isFocusableSidebarTarget(row.button) && isRenderedSidebarTarget(row.button))
942
+ return (button: HTMLElement) =>
943
+ isFocusableSidebarTarget(button) && (!checkRendered || isRenderedSidebarTarget(button))
944
+ }
945
+
946
+ /**
947
+ * 整串只留一顆 tabindex=0:上次焦點停過的那一列 → 當前頁(aria-current="page")→ 第一個可用列(X1;
948
+ * 判定 = lib/roving-list-keyboard.ts `pickRovingTabStop`)。動作鈕一律 -1(別項的與本項的都不在 Tab 路上;→ 才進得去)。
949
+ */
950
+ function syncSidebarMenuTabStops(menu: HTMLElement, remembered: HTMLElement | null) {
951
+ const rows = getSidebarMenuRows(menu)
952
+ const stop = pickRovingTabStop(rows.map((row) => row.button), {
953
+ remembered,
954
+ isCurrent: (button) => button.getAttribute("aria-current") === "page",
955
+ isNavigable: sidebarRowNavigability(menu, rows),
956
+ })
957
+ for (const row of rows) {
958
+ setRovingTabIndex(row.button, row.button === stop ? 0 : -1)
959
+ removeRovingControlsFromTabOrder(row.actions)
960
+ }
961
+ }
962
+
871
963
  const SidebarMenu = React.forwardRef<
872
964
  HTMLUListElement,
873
965
  React.ComponentProps<"ul">
874
- >(({ className, ...props }, ref) => (
875
- <ul
876
- ref={ref}
877
- data-sidebar="menu"
878
- // 無 gap:items 連續緊貼(對齊 DropdownMenu / TreeView 的視覺節奏)
879
- className={cn("flex w-full min-w-0 flex-col", className)}
880
- {...props}
881
- />
882
- ))
966
+ >(({ className, onKeyDownCapture, onFocus, ...props }, ref) => {
967
+ const menuRef = React.useRef<HTMLUListElement | null>(null)
968
+ const rememberedRef = React.useRef<HTMLElement | null>(null)
969
+ // 收合 / 展開、手機抽屜切換時重算一次(可見性在這些時刻改變,但本串自己的 DOM 沒變)。
970
+ // 用 useContext 而非 useSidebar():SidebarMenu 單獨渲染(沒有 Provider)時不該丟例外。
971
+ const sidebarContext = React.useContext(SidebarContext)
972
+ const sidebarState = sidebarContext?.state
973
+ const sidebarIsMobile = sidebarContext?.isMobile
974
+
975
+ const setRefs = React.useCallback(
976
+ (el: HTMLUListElement | null) => {
977
+ menuRef.current = el
978
+ if (typeof ref === "function") ref(el)
979
+ else if (ref) (ref as React.MutableRefObject<HTMLUListElement | null>).current = el
980
+ },
981
+ [ref]
982
+ )
983
+
984
+ // 掛上時、以及收合 / 展開 / 手機切換後同步一次;本串 DOM 的其他變化交給下方 MutationObserver
985
+ React.useLayoutEffect(() => {
986
+ if (menuRef.current) syncSidebarMenuTabStops(menuRef.current, rememberedRef.current)
987
+ }, [sidebarState, sidebarIsMobile])
988
+
989
+ // 列新增 / 移除、disabled 改變、子層自己重繪而本串沒重繪(例:當前頁換了 → 列鈕的 aria-current 變,
990
+ // 但 SidebarMenu 不訂閱 activeId)
991
+ React.useEffect(() => {
992
+ const menu = menuRef.current
993
+ if (!menu || typeof MutationObserver === "undefined") return
994
+ const observer = new MutationObserver(() => syncSidebarMenuTabStops(menu, rememberedRef.current))
995
+ observer.observe(menu, {
996
+ childList: true,
997
+ subtree: true,
998
+ attributes: true,
999
+ attributeFilter: ["disabled", "aria-current"],
1000
+ })
1001
+ return () => observer.disconnect()
1002
+ }, [])
1003
+
1004
+ // 焦點用任何方式進到某一列(Tab 進來 / 方向鍵 / 滑鼠點 / 選單關閉後還焦點)→ 停靠點跟過去,
1005
+ // 下次 Tab 回來就回到這裡(同 calendar.tsx 日期鈕的 onFocus 同步)。
1006
+ const handleFocus = (event: React.FocusEvent<HTMLUListElement>) => {
1007
+ onFocus?.(event)
1008
+ const menu = event.currentTarget
1009
+ const target = event.target as HTMLElement
1010
+ const row = getSidebarMenuRows(menu).find(
1011
+ (r) => r.button === target || r.actions.includes(target)
1012
+ )
1013
+ if (!row) return
1014
+ rememberedRef.current = row.button
1015
+ syncSidebarMenuTabStops(menu, row.button)
1016
+ }
1017
+
1018
+ // 用 capture:方向鍵整串歸清單,要搶在列鈕 / 動作鈕自己的處理之前 —— 帳號列是 `DropdownMenuTrigger asChild`、
1019
+ // 動作鈕可能是選單觸發鈕(inlineActionsSlot),Radix 會拿 ↓ 開選單;這裡先 preventDefault,Radix 的
1020
+ // composeEventHandlers 看到 defaultPrevented 就不執行,開選單照樣用 Enter / Space(AI 推導,見 spec;X6 以此為全宿主的做法)。
1021
+ const handleKeyDownCapture = (event: React.KeyboardEvent<HTMLUListElement>) => {
1022
+ onKeyDownCapture?.(event)
1023
+ const menu = event.currentTarget
1024
+ const target = event.target as HTMLElement
1025
+ const rows = getSidebarMenuRows(menu)
1026
+ const row = rows.find((r) => r.button === target || r.actions.includes(target))
1027
+ // 焦點不在列或動作鈕上(consumer 自己放進列裡的其他控件)→ 不接管,讓它自己的鍵盤照舊
1028
+ if (!row) return
1029
+ const onRow = row.button === target
1030
+ const controls = row.actions.filter(isRovingControlReachable)
1031
+ const action = resolveRovingKey({
1032
+ key: event.key,
1033
+ focus: onRow ? "item" : "control",
1034
+ controlCount: controls.length,
1035
+ controlIndex: onRow ? -1 : controls.indexOf(target),
1036
+ controlIsTextEntry: !onRow && isTextEntryElement(target),
1037
+ defaultPrevented: event.defaultPrevented,
1038
+ metaKey: event.metaKey,
1039
+ ctrlKey: event.ctrlKey,
1040
+ altKey: event.altKey,
1041
+ shiftKey: event.shiftKey,
1042
+ })
1043
+ const isNavigable = sidebarRowNavigability(menu, rows)
1044
+ applyRovingAction(action, {
1045
+ event,
1046
+ item: row.button,
1047
+ controls,
1048
+ items: rows.map((r) => r.button),
1049
+ isNavigable,
1050
+ })
1051
+ }
1052
+
1053
+ return (
1054
+ <ul
1055
+ ref={setRefs}
1056
+ data-sidebar="menu"
1057
+ // 無 gap:items 連續緊貼(對齊 DropdownMenu / TreeView 的視覺節奏)
1058
+ className={cn("flex w-full min-w-0 flex-col", className)}
1059
+ onFocus={handleFocus}
1060
+ onKeyDownCapture={handleKeyDownCapture}
1061
+ {...props}
1062
+ />
1063
+ )
1064
+ })
883
1065
  SidebarMenu.displayName = "SidebarMenu"
884
1066
 
885
1067
  const SidebarMenuItem = React.forwardRef<
@@ -937,13 +1119,28 @@ const sidebarMenuButtonVariants = cva(
937
1119
  // 對齊 MUI MiniDrawer + shadcn canonical(width morph + overflow clip + row geometry 不變)。
938
1120
  // hover 底色瞬間切換,不做過渡(user 2026-09-10 拍板「第三題改成全部瞬間」;SSOT = tokens/motion/motion.spec.md「hover 回饋不做過渡」)
939
1121
  "hover:bg-neutral-hover hover:text-foreground",
1122
+ // 巢狀 hover(2026-09-25,user 選「卡片保留、按鈕再亮一層」;SSOT = sidebar.spec.md「Inline actions」段「指標在動作鈕上時(巢狀 hover)」):
1123
+ // 指標移到同一列的動作鈕(inlineActions 的 suffix span / SidebarMenuAction)上,列保留上一行的 hover,動作鈕再亮自己那層。
1124
+ // 需要這兩行是因為動作鈕是列鈕的**同層兄弟**(避免 button 包 button,見下方 suffixNode 註解)—— 指標在兄弟上時列鈕不算 :hover。
1125
+ // - `~` 只往後找:動作必須排在列鈕後面(兩個內建出口都是;SidebarMenuAction 的 peer-data 也早就要求這個順序)。
1126
+ // - 外層包 `:where()` 把特異性壓回 (0,1,0),下方 `data-[active=true]` 選中色 (0,2,0) 照樣釘住;不包會是 (0,3,0),選中列指到動作鈕時選中色被 hover 色蓋掉(2026-09-25 Chromium 實測)。
1127
+ // - 不掛在 li 的 `group-hover/menu-item`:li 收任意子元素,指到 li 裡的子項目時父列會誤亮(同日實測)。
1128
+ // - `[@media(hover:hover)]` 對齊上一行 `hover:` 的媒體條件(Tailwind 4 的 hover: 只在能懸停的裝置生效),觸控裝置不留殘亮。
1129
+ // 值與上一行 `hover:` 是同一對;改那一行必須同步改這兩行。
1130
+ "[@media(hover:hover)]:[&:where(:has(~:is([data-sidebar=menu-inline-actions],[data-sidebar=menu-action]):hover))]:bg-neutral-hover",
1131
+ "[@media(hover:hover)]:[&:where(:has(~:is([data-sidebar=menu-inline-actions],[data-sidebar=menu-action]):hover))]:text-foreground",
940
1132
  // 鍵盤游標一律畫框、不上底色(focus-canonical 規則二,user 2026-09-09 拍板);選單鈕撐滿側欄寬度 → 內描邊。
941
1133
  // 2026-09-09 之前非當前項的焦點用 hover 同色底(AI 推導,user 撤回);hover 的底色與文字色只屬於滑鼠。
942
1134
  "focus-visible:focus-ring-inset",
943
1135
  // 2026-05-31 M24:SidebarMenuButton 主要為 icon+label(currentColor 可改寫)→ 用 semantic
944
1136
  // text-fg-disabled 對齊 MenuItem primitive(menu-item.tsx:198/221/248 + item-anatomy.tsx:374),
945
1137
  // 非 opacity-disabled(opacity 保留給圖片/avatar/Switch 等無法改寫內部色者,per color.spec.md:103/118/701)。
946
- "disabled:pointer-events-none disabled:text-fg-disabled",
1138
+ // 原生 disabled 的 button 本來就不會發 click,不再 pointer-events-none:留著接指標只為了掛禁止符號
1139
+ //(hit-area-canonical.md 滑過原則一-3「停用的用禁止符號」;2026-09-29 全站掃出:「混合內容」的「delta(已封存)」游標是箭頭,
1140
+ // 因為 pointer-events-none 讓 cursor 由 li 決定)。上方 hover 的底色 / 字色由 disabled:hover 釘住(:disabled:hover 特異性高過 :hover)。
1141
+ "disabled:text-fg-disabled disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:text-fg-disabled",
1142
+ // aria-disabled(asChild 的 <a> 等非 button 宿主)仍靠 pointer-events-none 擋掉點擊 / 導覽 —— 那是它唯一的擋法;
1143
+ // 代價是游標由外層決定(箭頭)。要拿掉這裡的 pointer-events-none,得先在 asChild 宿主補 click guard(另案)。
947
1144
  "aria-disabled:pointer-events-none aria-disabled:text-fg-disabled",
948
1145
  "data-[active=true]:bg-neutral-selected data-[active=true]:text-foreground",
949
1146
  // 2026-08-11 user 拍板(SSOT = item-anatomy「選中 × 互動疊加」):滑鼠 hover 當前項釘住不變
@@ -1045,7 +1242,8 @@ const SidebarMenuButton = React.forwardRef<
1045
1242
  /**
1046
1243
  * Inline actions 的顯示模式:
1047
1244
  * - `false`(預設):永遠顯示
1048
- * - `"hover"`:row hover 時才淡入(TreeView 模式)
1245
+ * - `"hover"`:滑過這一列、或鍵盤焦點在這一列時才出現,瞬間出現、不淡入(TreeView 模式;
1246
+ * 待辦總帳 L9,sidebar.spec.md「Inline actions」出現時機列)
1049
1247
  */
1050
1248
  actionsReveal?: false | "hover"
1051
1249
  } & VariantProps<typeof sidebarMenuButtonVariants>
@@ -1139,6 +1337,19 @@ const SidebarMenuButton = React.forwardRef<
1139
1337
  ref={ref}
1140
1338
  data-sidebar="menu-button"
1141
1339
  data-active={isActive}
1340
+ // 2026-09-24:先前這裡**只有** data-active 這個純樣式 attribute,螢幕閱讀器讀不到
1341
+ // 「你現在人在這一頁」—— 當前項在視覺上有底色,對 SR 卻跟其他項完全一樣。
1342
+ // aria-current="page" 是這個狀態的世界級標準寫法,三份一手來源一致:
1343
+ // W3C APG Disclosure Navigation 範例(連結上帶 aria-current="page")、
1344
+ // GitHub repo 導覽(Code / Issues / PRs 那排)、
1345
+ // Primer TreeView(aria-current={isCurrentItem ? 'true' : undefined})。
1346
+ // 它與 aria-selected 是兩件事,WAI-ARIA 1.2 aria-current 的 Note 明文說兩者可以並存:
1347
+ // current = 你現在人在這一頁(已發生的事實);selected = 這個控件裡被挑中的那一項。
1348
+ // 側欄導覽要表達的是「你現在在這一頁」,不是「控件裡被挑中的那一項」,所以用 current 不用 selected。
1349
+ // 2026-09-25 起一串 SidebarMenu 只佔一個 Tab 停靠點、方向鍵在裡面走(待辦總帳 B9,見上方 SidebarMenu),
1350
+ // 但 ARIA 角色仍是 ul / li / button、沒有宣告 grid / tree 這類 composite 角色 → aria-selected 不適用,這條不變。
1351
+ // 純 SR 語意,**零視覺變化**。
1352
+ aria-current={isActive ? 'page' : undefined}
1142
1353
  className={cn(sidebarMenuButtonVariants({ size, variant }), className)}
1143
1354
  style={hasActions ? { paddingRight: buttonPaddingRight } : undefined}
1144
1355
  onClick={handleClick}
@@ -1150,20 +1361,26 @@ const SidebarMenuButton = React.forwardRef<
1150
1361
 
1151
1362
  // Suffix inline actions——絕對定位在 menu-item 右邊,
1152
1363
  // 跟 button 同層(不是 button 的 child,避免巢狀 button)。
1364
+ // 容器消費 `ItemSuffix`(item-anatomy.tsx):滑過才出現(`hoverReveal`)的規則只住那一份 ——
1365
+ // 2026-09-26 之前這裡另抄了一份同字串,出現方式要跟著 SSOT 改時會漏(M17);它也是「滑過造成的變化一律瞬間」
1366
+ //(待辦總帳 L9 / N4(3),motion.spec.md「hover 回饋不做過渡」)的唯一落點。group 用 `menu-item`(上方 SidebarMenuItem 的 group/menu-item)。
1367
+ // `:has(:focus-visible)` 而非 `:focus-within`:滑鼠點動作鈕不該讓它常駐,只有鍵盤走到時才顯示(ItemSuffix 同註解)。
1153
1368
  const suffixNode = hasActions ? (
1154
- <span
1369
+ <ItemSuffix
1155
1370
  data-sidebar="menu-inline-actions"
1371
+ hoverReveal={actionsReveal === "hover"}
1372
+ hoverGroup="menu-item"
1156
1373
  className={cn(
1157
- "absolute top-1/2 -translate-y-1/2 flex items-center gap-2",
1374
+ "absolute top-1/2 -translate-y-1/2",
1158
1375
  "right-[var(--layout-space-loose)]",
1159
1376
  // Icon 模式隱藏(跟 SidebarMenuBadge / SidebarMenuAction 一致)
1160
1377
  "group-data-[collapsible=icon]:hidden",
1161
- // hover-reveal:滑鼠 hover 或鍵盤 focus(但不是 mouse click 的 focus)時顯示。
1162
- // 用 `:has(:focus-visible)` 而非 `:focus-within`——focus-within 會被
1163
- // mouse click 觸發,導致 click 之後 actions 永久顯示直到焦點移走;
1164
- // focus-visible 只在鍵盤 tab 時啟動,mouse click 不會觸發,符合使用者直覺。
1165
- actionsReveal === "hover" &&
1166
- "opacity-0 group-hover/menu-item:opacity-100 group-has-[:focus-visible]/menu-item:opacity-100 transition-opacity duration-150 motion-reduce:duration-0"
1378
+ // 兩顆動作鈕之間的空隙歸列(2026-09-26,待辦總帳 N53 ①,user:「這些看起來幾乎都是bug」;修法 = R20「讓亮著的地方點得到」):
1379
+ // 空隙在滑過時讓列亮著(sidebar.spec.md「Inline actions」段「兩顆動作鈕之間的空隙也算在內」),
1380
+ // 點下去卻落在這個容器、列不導覽 —— 看得到亮卻點不到(hit-area-canonical.md「看到亮起來卻點不到」)。
1381
+ // 容器本身不接指標,只有裡面可以按的東西接;空隙、以及插槽裡不能按的裝飾都穿到底下的列鈕(列鈕 paddingRight 已讓出這一段)。
1382
+ // 指到按鈕上時容器仍是 :hover 的祖先,列的巢狀滑過(上方 cva 的 `~` 選擇器)照舊。
1383
+ "pointer-events-none [&_:is(button,a[href],[role=button],[tabindex])]:pointer-events-auto",
1167
1384
  )}
1168
1385
  >
1169
1386
  {hasSlot
@@ -1171,7 +1388,7 @@ const SidebarMenuButton = React.forwardRef<
1171
1388
  : inlineActions!.map((action, i) => (
1172
1389
  <ItemInlineAction key={action.label + i} action={action} />
1173
1390
  ))}
1174
- </span>
1391
+ </ItemSuffix>
1175
1392
  ) : null
1176
1393
 
1177
1394
  // code-quality-allow: long-function — helper fn 結構緊密,拆 sub-fn 會跨 fn 傳 state 反而複雜
@@ -1206,31 +1423,31 @@ const SidebarMenuButton = React.forwardRef<
1206
1423
  )
1207
1424
  SidebarMenuButton.displayName = "SidebarMenuButton"
1208
1425
 
1426
+ // 同上(見 SidebarGroupAction 上方的遷移說明)。
1209
1427
  const SidebarMenuAction = React.forwardRef<
1210
1428
  HTMLButtonElement,
1211
- React.ComponentProps<"button"> & {
1212
- asChild?: boolean
1213
- showOnHover?: boolean
1214
- }
1215
- >(({ className, asChild = false, showOnHover = false, ...props }, ref) => {
1216
- const Comp = asChild ? Slot : "button"
1217
-
1218
- return (
1219
- <Comp
1220
- ref={ref}
1221
- data-sidebar="menu-action"
1222
- className={cn(
1223
- "absolute right-[var(--layout-space-loose)] top-1/2 -translate-y-1/2 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-fg-muted hover:bg-neutral-hover hover:text-fg-secondary [&>svg]:size-4 [&>svg]:shrink-0",
1224
- "after:absolute after:-inset-2 after:md:hidden",
1225
- "group-data-[collapsible=icon]:hidden",
1226
- showOnHover &&
1227
- "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-foreground md:opacity-0",
1228
- className
1229
- )}
1230
- {...props}
1231
- />
1232
- )
1233
- })
1429
+ Omit<ItemInlineActionButtonProps, "size"> & { showOnHover?: boolean }
1430
+ >(({ className, showOnHover = false, ...props }, ref) => (
1431
+ <ItemInlineActionButton
1432
+ ref={ref}
1433
+ data-sidebar="menu-action"
1434
+ className={cn(
1435
+ "absolute right-[var(--layout-space-loose)] top-1/2 -translate-y-1/2",
1436
+ "group-data-[collapsible=icon]:hidden",
1437
+ showOnHover &&
1438
+ "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-foreground md:opacity-0",
1439
+ // 當前項旁的動作鈕:靜止已是 foreground(上一行),滑過 / 按下**釘住 foreground**,不讓 ItemInlineActionButton 的
1440
+ // `hover:/active:text-fg-secondary` (0,2,0) 把它降回一階(2026-09-25 合成情境量測:淺 #252525→#545454、深 #FFFFFF→#E1E1E1;目前 DS 內無 showOnHover 消費者)。
1441
+ // 依據:inline-action.spec.md「Icon 色彩」的階梯 root invariant `fg-muted → fg-secondary → foreground`,滑過只准往前一階;
1442
+ // 這顆的靜止已站在最高一階 foreground,沒有下一階可升就留在原地,不得往回掉 ——
1443
+ // 同列鈕「選中 × 滑過釘住」的寫法(上方 cva 的 `data-[active=true]:hover:…`)。特異性 (0,3,0) > hover:/active: 的 (0,2,0)。
1444
+ showOnHover &&
1445
+ "peer-data-[active=true]/menu-button:hover:text-foreground peer-data-[active=true]/menu-button:active:text-foreground",
1446
+ className
1447
+ )}
1448
+ {...props}
1449
+ />
1450
+ ))
1234
1451
  SidebarMenuAction.displayName = "SidebarMenuAction"
1235
1452
 
1236
1453
  // SidebarMenuBadge — 重用專案的 Badge 元件,絕對定位在 menu item 右側
@@ -1333,7 +1550,7 @@ export const sidebarMeta = {
1333
1550
  tokens: {
1334
1551
  bg: ['bg-neutral-hover', 'bg-neutral-selected', 'bg-surface', 'bg-transparent'],
1335
1552
  fg: ['text-fg-muted', 'text-fg-secondary', 'text-foreground'],
1336
- ring: ['ring-ring'],
1553
+ ring: ['focus-ring-inset'],
1337
1554
  },
1338
1555
  defaultSize: 'md',
1339
1556
  } as const
@@ -86,7 +86,7 @@ Skeleton 是**純視覺 loading placeholder**,預設 ARIA 行為:
86
86
  - **無內建 ARIA**:Skeleton 實作只是一個套用 pulse 動畫的 div(`{...props}` 原樣傳遞),不會自動設任何 ARIA 屬性。SR 不該朗讀 N 條 placeholder shape,因此 consumer 若需要對 SR 隱藏這些佔位塊,應自行在容器設 `aria-hidden="true"`
87
87
  - **`aria-busy="true"` 由 consumer 容器 own**:loading region 的容器(如 DataTable / Card / Sidebar group)應設 `aria-busy="true"`,告知 SR「該區域 loading 中」;data ready 後 consumer 移除 `aria-busy` 同時 unmount Skeleton
88
88
  - **不取得 focus**:Skeleton 永遠 non-interactive,無鍵盤行為
89
- - **動畫**:`prefers-reduced-motion: reduce` 用戶自動 stop pulse 動畫(`skeleton.tsx:10` 的 `motion-reduce:animate-none`)。**例外(2026-09-09)**:一次數百格、只活幾十到幾百毫秒的佔位(DataTable 快速捲動的列殼,`data-table.spec.md`「快速捲動的列殼」)傳 `className="animate-none"` 關掉脈動 —— 幾百個透明度動畫會讓光柵每幀重畫、合成器追不上(真實呈現幀消融實測空白幀 42 → 20);形狀、底色、元件都仍是本 Skeleton,只關動畫。pulse 是自動播放的循環動畫,歸 WCAG 2.2.2 Pause, Stop, Hide(Level A);honor `prefers-reduced-motion` 即滿足。注意非 2.3.3 Animation from Interactions(該 SC 是 Level AAA,僅涵蓋由互動 / 捲動觸發的位移動畫,不適用 auto-play loop)
89
+ - **動畫**:`prefers-reduced-motion: reduce` 用戶自動 stop pulse 動畫(`skeleton.tsx:10` 的 `motion-reduce:animate-none`)。**例外(2026-09-09)**:一次數百格、只活幾十到幾百毫秒的佔位(DataTable 快速捲動的列殼,`../DataTable/data-table-scroll-performance.spec.md`「快速捲動的列殼」)傳 `className="animate-none"` 關掉脈動 —— 幾百個透明度動畫會讓光柵每幀重畫、合成器追不上(真實呈現幀消融實測空白幀 42 → 20);形狀、底色、元件都仍是本 Skeleton,只關動畫。pulse 是自動播放的循環動畫,歸 WCAG 2.2.2 Pause, Stop, Hide(Level A);honor `prefers-reduced-motion` 即滿足。注意非 2.3.3 Animation from Interactions(該 SC 是 Level AAA,僅涵蓋由互動 / 捲動觸發的位移動畫,不適用 auto-play loop)
90
90
  - **替代文字**:長時間 loading(>3s)consumer 應 fallback 為 `<Empty icon={<CircularProgress/>} description="載入中..." />`(SR-friendly text)— Skeleton 適合 < 3s 短暫 placeholder(3s 為建議換手值;> 10s 仍用 Skeleton 屬「禁止事項」硬性違規)
91
91
  - **驗證**:Storybook a11y addon panel 0 critical violation。Skeleton 本身無文字內容、無互動角色,axe 不會要求 label;若 consumer 把 Skeleton 包進有語意的容器,label 由該容器負責
92
92
 
@@ -101,7 +101,7 @@ export const ColorMatrix: Story = {
101
101
  <tbody>
102
102
  <tr><Td>Track 厚度</Td><Td mono>4px(h-1)</Td><Td>—</Td></tr>
103
103
  <tr><Td>Track 底色 default</Td><Td><TokenCell token="--secondary" display="bg-secondary" /></Td><Td><TokenCell token="--secondary" display="--secondary(neutral-3)" /></Td></tr>
104
- <tr><Td>Track 底色 disabled</Td><Td><TokenCell token="--muted" display="bg-muted" /></Td><Td><TokenCell token="--muted" display="--muted(neutral-2)" /></Td></tr>
104
+ <tr><Td>Track 底色 disabled</Td><Td><TokenCell token="--bg-disabled" display="bg-disabled" /></Td><Td><TokenCell token="--bg-disabled" display="--bg-disabled(neutral-2,元件停用狀態色)" /></Td></tr>
105
105
  <tr><Td>Range 填滿色 default</Td><Td><TokenCell token="--primary" display="bg-primary" /></Td><Td><TokenCell token="--primary" /></Td></tr>
106
106
  <tr><Td>Range 填滿色 disabled</Td><Td><TokenCell token="--border" display="bg-border" /></Td><Td><TokenCell token="--border" display="--border(neutral-5)" /></Td></tr>
107
107
  <tr><Td>Thumb 直徑</Td><Td mono>16px(h-4 w-4)</Td><Td>—</Td></tr>
@@ -76,7 +76,7 @@ benchmark:
76
76
  |---|---|---|
77
77
  | **Track** 厚度 | 4px | — |
78
78
  | **Track** 底色 rest | `bg-secondary` | `--secondary`(neutral-3,跟 Tag neutral / Badge low 同級「微淡可辨」)|
79
- | **Track** 底色 disabled | `bg-muted` | `--muted`(neutral-2,disabled-like 退化)|
79
+ | **Track** 底色 disabled | `bg-disabled` | `--bg-disabled`(neutral-2,元件停用狀態色;2026-09-25 由 `bg-muted` 改,理由見下方「為什麼 Track 底色維持灰色凹槽身分」段)|
80
80
  | **Range** 填滿色 rest | `bg-primary` | `--primary` |
81
81
  | **Range** 填滿色 disabled | `bg-border`(neutral-5)| `--border` |
82
82
  | **Thumb** 直徑 | 16px | — |
@@ -95,7 +95,7 @@ benchmark:
95
95
 
96
96
  ### 為什麼 Track 底色維持「灰色凹槽」身分,不跟 enabled state 變動
97
97
 
98
- Track 的視覺角色是「可滑動範圍的凹槽底線」,語意不因 enabled / disabled 改變——rest `bg-secondary`(neutral-3)、disabled 退一階 `bg-muted`(neutral-2),都在最淡 subtle bg 層級、凹槽身分不變(disable 時 Range / Thumb border 同步降級 primary → border)。semantic token 優先、不直接引 primitive;**若覺得 track 在 white canvas 上太淡**,那是 `--secondary` / `--muted` 的系統級議題(Badge / Skeleton 一起調),不是在 Slider 裡用 primitive 繞過的理由。
98
+ Track 的視覺角色是「可滑動範圍的凹槽底線」,語意不因 enabled / disabled 改變——rest `bg-secondary`(neutral-3)、disabled 退一階 `bg-disabled`(neutral-2),都在最淡 subtle bg 層級、凹槽身分不變(disable 時 Range / Thumb border 同步降級 primary → border)。**停用底用 `--bg-disabled` 不用 `--muted`(2026-09-25,待辦總帳 C2 / R12 W3)**:這層底只在元件停用時出現,是元件的停用狀態色;owner 取 `../../tokens/color/color.spec.md`「Static Subtle Background」段的邊界句「component disabled bg 不走 muted」(`--muted` 是靜態非互動 surface 的 token,例如 Skeleton)。兩者同值 neutral-2,畫面零變化。semantic token 優先、不直接引 primitive;**若覺得 track 在 white canvas 上太淡**,那是 `--secondary` / `--bg-disabled` 的系統級議題,不是在 Slider 裡用 primitive 繞過的理由。
99
99
 
100
100
  ### Range 色 ↔ Thumb border 色的綁定規則
101
101
 
@@ -176,7 +176,7 @@ Slider 不是 button——它是「當前位置指示器」,底色不該動(動
176
176
 
177
177
  ### Disabled 視覺階層(三階,不是四階)
178
178
 
179
- `track(muted, n-2)< range = thumb border(border, n-5)< text(n-7+)`——Range 與 Thumb border 刻意同 token 不拆階(見綁定規則);thumb fill(rest 白 / disabled canvas)與 range fill 對比、位置由 range 長度段決定;text 永遠最上層。
179
+ `track(bg-disabled, n-2)< range = thumb border(border, n-5)< text(n-7+)`——Range 與 Thumb border 刻意同 token 不拆階(見綁定規則);thumb fill(rest 白 / disabled canvas)與 range fill 對比、位置由 range 長度段決定;text 永遠最上層。
180
180
 
181
181
  ### 為什麼 range disabled 用 `--border` 不用 `--fg-disabled`
182
182
 
@@ -207,7 +207,7 @@ Opacity 會同時削弱 track、range、thumb 與邊框,讓元件所有層級
207
207
 
208
208
  ### 常見錯誤(避免)
209
209
 
210
- **不要把 thumb 的 disabled bg 改成 `bg-muted`**——`--muted` 和 `--bg-disabled` 在這個系統都等於 `var(--color-neutral-2)`,同一個顏色。Thumb `bg-muted` 會跟 track `bg-muted` 完全融色,只剩 border 可見,失去 thumb 形狀辨識(真實踩過的 bug)。Disabled 用 **`bg-canvas`**(不透明頁面背景色,與 track 的 muted 不同值且隔 n-5 邊框,不融色)。2026-06-12 補:rest 態原為 `bg-surface`,但 `--surface` 深色 = 8% 白半透明 → thumb 在深色變破洞且 track 穿透,故改 `bg-on-emphasis`(固定白不反轉)。
210
+ **不要把 thumb 的 disabled bg 改成 `bg-muted` 或 `bg-disabled`**——`--muted` 和 `--bg-disabled` 在這個系統都等於 `var(--color-neutral-2)`,同一個顏色,也就是 track 停用底的顏色。Thumb 用它會跟 track 完全融色,只剩 border 可見,失去 thumb 形狀辨識(真實踩過的 bug)。Disabled 用 **`bg-canvas`**(不透明頁面背景色,與 track 停用底不同值且隔 n-5 邊框,不融色)。2026-06-12 補:rest 態原為 `bg-surface`,但 `--surface` 深色 = 8% 白半透明 → thumb 在深色變破洞且 track 穿透,故改 `bg-on-emphasis`(固定白不反轉)。
211
211
 
212
212
  **不要同時套 opacity + 灰階 swap**——兩個策略互斥,同時用會導致「灰階 swap 後再打 opacity 一層」,視覺雙重降級,整個 slider 褪色過度。選一條路走到底。
213
213
 
@@ -275,7 +275,7 @@ Inspector 提供 `min` / `max` / `step` / `defaultValue` × `size` 即時調整(
275
275
  - `../SegmentedControl/segmented-control.spec.md` — 離散少量選項的對應元件
276
276
  - `../../tokens/uiSize/uiSize.spec.md` — `field-height-*` token family
277
277
  - `../../tokens/elevation/elevation.spec.md` — Elevation hover / active 語意
278
- - `../../tokens/color/color.spec.md` — Primary / muted / fg-disabled token
278
+ - `../../tokens/color/color.spec.md` — Primary / bg-disabled / fg-disabled token
279
279
  - `../Field/field.spec.md` — Field 容器整合規則
280
280
  - Radix Slider primitive API — `@radix-ui/react-slider`
281
281
 
@@ -300,4 +300,5 @@ Inspector 提供 `min` / `max` / `step` / `defaultValue` × `size` 即時調整(
300
300
  > 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
301
301
 
302
302
  - `number-input.spec.md`
303
+ - `rating.spec.md`
303
304
  - `switch.spec.md`
@@ -99,14 +99,16 @@ const Slider = React.forwardRef<
99
99
  data-readonly={fieldReadonly || undefined}
100
100
  >
101
101
  {/*
102
- Track — rest 用 bg-secondary(n-3,「微淡可辨」),disabled 用 bg-muted(n-2,退化)。
103
- 跟 Tag neutral / Badge low 同家族。
102
+ Track — rest 用 bg-secondary(n-3,「微淡可辨」),disabled 用 bg-disabled(n-2,元件停用狀態色)。
103
+ rest 跟 Tag neutral / Badge low 同家族。
104
104
  */}
105
105
  <SliderPrimitive.Track className={cn(
106
106
  'relative grow overflow-hidden rounded-full h-1',
107
107
  // Rest:bg-secondary(n-3,「微淡可辨」的 subtle fill,跟 Tag neutral / Badge low 同級)
108
- // Disabled:bg-muted(n-2,「disabled-like 退化」底色)
109
- 'bg-secondary data-[disabled]:bg-muted',
108
+ // Disabled:bg-disabled(n-2)。2026-09-25 由 bg-muted 改(待辦總帳 C2 / R12 W3):這層底只在元件停用時出現,
109
+ // 是元件的停用狀態色,依 color.spec.md「Static Subtle Background」段邊界句「component disabled bg 不走 muted」
110
+ // 走 --bg-disabled;--muted 是靜態非互動 surface 的 token。兩者同值 neutral-2,畫面零變化。
111
+ 'bg-secondary data-[disabled]:bg-disabled',
110
112
  )}>
111
113
  {/*
112
114
  Range — 填滿段。
@@ -145,8 +147,8 @@ const Slider = React.forwardRef<
145
147
  = 8% 白半透明 → thumb 變深色破洞且 track 穿透。
146
148
  - Disabled:`bg-canvas`(不透明頁面背景色)— 沉回背景表達「不可動」,
147
149
  = Radix disabled thumb 用 gray-1(app background)同款;不透明故 track
148
- 不穿透。不可用 bg-muted(曾踩 thumb 跟 track 同色融合 bug;canvas 與
149
- track 的 muted 隔 n-5 邊框 + 不同值,不融)。
150
+ 不穿透。不可用 bg-muted / bg-disabled(兩者與 track 停用底同值 n-2,曾踩 thumb 跟 track
151
+ 同色融合 bug;canvas 與 track 的停用底隔 n-5 邊框 + 不同值,不融)。
150
152
  */}
151
153
  {Array.from({ length: thumbCount }).map((_, i) => (
152
154
  <SliderPrimitive.Thumb
@@ -163,7 +165,10 @@ const Slider = React.forwardRef<
163
165
  className={cn(
164
166
  'block h-4 w-4 shrink-0 rounded-full cursor-grab',
165
167
  'bg-on-emphasis border-2 border-primary',
166
- 'transition-all duration-150 motion-reduce:duration-0',
168
+ // 只過渡陰影(hover / 按壓時的高度),不再過渡外框色:hover 外框一律瞬間(tokens/motion/motion.spec.md「hover 回饋不做過渡」;
169
+ // 2026-09-26 由底色延伸到外框,待辦總帳 L9 / N4(3));按壓的外框是同一組屬性,一併瞬間(同 Button 按壓,button.tsx 無過渡)。
170
+ // 原 transition-all 連外框與停用底色一起過渡。陰影升級不在 2026-09-26 同意範圍(motion.spec.md「不在本規則」),維持 150ms。
171
+ 'transition-shadow duration-150 motion-reduce:duration-0',
167
172
  // Hover:border 升 hover 階(light mode 淺一階 lift,= Button primary hover 變色邏輯)
168
173
  // + elevation 陰影。2026-07-06 修語:原註解寫「加深」但 primary-hover 比 base 淺,
169
174
  // 措辭與 token 相反 — token 不遷就錯註解,註解修正。
@@ -224,7 +229,7 @@ export const sliderMeta = {
224
229
  },
225
230
  states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
226
231
  tokens: {
227
- bg: ['bg-muted', 'bg-primary', 'bg-secondary', 'bg-on-emphasis', 'bg-canvas'],
232
+ bg: ['bg-disabled', 'bg-primary', 'bg-secondary', 'bg-on-emphasis', 'bg-canvas'],
228
233
  fg: [],
229
234
  ring: [],
230
235
  },