@qijenchen/design-system 0.1.0-beta.74 → 0.1.0-beta.76

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 (203) hide show
  1. package/CLAUDE.md +1 -1
  2. package/dist/components/AppShell/app-shell.d.ts +2 -2
  3. package/dist/components/AppShell/app-shell.js.map +1 -1
  4. package/dist/components/Avatar/avatar.js.map +1 -1
  5. package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
  6. package/dist/components/BulkActionBar/bulk-action-bar.js +1 -1
  7. package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
  8. package/dist/components/Button/button.d.ts.map +1 -1
  9. package/dist/components/Button/button.js.map +1 -1
  10. package/dist/components/Chart/chart.d.ts +1 -1
  11. package/dist/components/Chart/chart.js.map +1 -1
  12. package/dist/components/Checkbox/checkbox.d.ts +1 -1
  13. package/dist/components/Checkbox/checkbox.js +1 -1
  14. package/dist/components/Checkbox/checkbox.js.map +1 -1
  15. package/dist/components/Combobox/combobox.d.ts +1 -1
  16. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  17. package/dist/components/Combobox/combobox.js +13 -10
  18. package/dist/components/Combobox/combobox.js.map +1 -1
  19. package/dist/components/Command/command.d.ts +1 -1
  20. package/dist/components/Command/command.js +3 -3
  21. package/dist/components/Command/command.js.map +1 -1
  22. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  23. package/dist/components/DataTable/cell-registry.js +2 -2
  24. package/dist/components/DataTable/cell-registry.js.map +1 -1
  25. package/dist/components/DataTable/data-table.d.ts +27 -6
  26. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  27. package/dist/components/DataTable/data-table.js +57 -34
  28. package/dist/components/DataTable/data-table.js.map +1 -1
  29. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  30. package/dist/components/DatePicker/date-picker.js +2 -2
  31. package/dist/components/DatePicker/date-picker.js.map +1 -1
  32. package/dist/components/DescriptionList/description-list.d.ts +1 -1
  33. package/dist/components/DescriptionList/description-list.js +2 -2
  34. package/dist/components/DescriptionList/description-list.js.map +1 -1
  35. package/dist/components/Empty/empty.d.ts +2 -0
  36. package/dist/components/Empty/empty.d.ts.map +1 -1
  37. package/dist/components/Empty/empty.js.map +1 -1
  38. package/dist/components/Field/field-wrapper.js +4 -4
  39. package/dist/components/Field/field-wrapper.js.map +1 -1
  40. package/dist/components/OverflowIndicator/overflow-indicator.d.ts +1 -1
  41. package/dist/components/OverflowIndicator/overflow-indicator.js +2 -2
  42. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  43. package/dist/components/PeoplePicker/people-picker.js +2 -2
  44. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  45. package/dist/components/ProfileCard/profile-card.d.ts +1 -1
  46. package/dist/components/ProfileCard/profile-card.js +2 -1
  47. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  48. package/dist/components/Rating/rating.js.map +1 -1
  49. package/dist/components/Select/select.js +4 -4
  50. package/dist/components/Select/select.js.map +1 -1
  51. package/dist/components/Textarea/textarea.d.ts +1 -1
  52. package/dist/components/Textarea/textarea.js +2 -2
  53. package/dist/components/Textarea/textarea.js.map +1 -1
  54. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  55. package/dist/components/TimePicker/time-picker.js +14 -23
  56. package/dist/components/TimePicker/time-picker.js.map +1 -1
  57. package/dist/components/TreeView/tree-view.d.ts +1 -1
  58. package/dist/components/TreeView/tree-view.js +1 -1
  59. package/dist/components/TreeView/tree-view.js.map +1 -1
  60. package/ds-canonical/fork/governance.lock +1 -1
  61. package/ds-canonical/fork/preamble.md +2 -2
  62. package/ds-canonical/hooks/check_field_controls_contracts.sh +30 -3
  63. package/ds-canonical/hooks/check_story_invariants.sh +26 -0
  64. package/ds-canonical/hooks/tests/test_check_story_invariants.sh +30 -0
  65. package/ds-canonical/references/props-naming.md +15 -1
  66. package/ds-canonical/rules/ui-development.md +2 -2
  67. package/llms-full.txt +7 -2
  68. package/llms.txt +3 -3
  69. package/package.json +1 -1
  70. package/src/components/Accordion/accordion.principles.stories.tsx +3 -3
  71. package/src/components/Alert/alert.anatomy.stories.tsx +4 -4
  72. package/src/components/Alert/alert.principles.stories.tsx +5 -5
  73. package/src/components/AppShell/app-shell.principles.stories.tsx +6 -6
  74. package/src/components/AppShell/app-shell.spec.md +4 -4
  75. package/src/components/AppShell/app-shell.tsx +2 -2
  76. package/src/components/AspectRatio/aspect-ratio.principles.stories.tsx +1 -1
  77. package/src/components/Avatar/avatar.principles.stories.tsx +3 -3
  78. package/src/components/Avatar/avatar.tsx +1 -1
  79. package/src/components/Badge/badge.principles.stories.tsx +3 -3
  80. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +3 -3
  81. package/src/components/Breadcrumb/breadcrumb.spec.md +11 -1
  82. package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +1 -1
  83. package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +3 -3
  84. package/src/components/BulkActionBar/bulk-action-bar.spec.md +4 -2
  85. package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +2 -2
  86. package/src/components/BulkActionBar/bulk-action-bar.tsx +3 -2
  87. package/src/components/Button/button.principles.stories.tsx +3 -3
  88. package/src/components/Button/button.tsx +0 -10
  89. package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
  90. package/src/components/Calendar/calendar.principles.stories.tsx +3 -3
  91. package/src/components/Carousel/carousel.principles.stories.tsx +2 -2
  92. package/src/components/Chart/chart.principles.stories.tsx +4 -4
  93. package/src/components/Chart/chart.tsx +1 -1
  94. package/src/components/Checkbox/checkbox.principles.stories.tsx +2 -2
  95. package/src/components/Checkbox/checkbox.tsx +1 -1
  96. package/src/components/Chip/chip.principles.stories.tsx +3 -3
  97. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +1 -1
  98. package/src/components/Coachmark/coachmark.principles.stories.tsx +3 -3
  99. package/src/components/Coachmark/coachmark.spec.md +2 -2
  100. package/src/components/Combobox/combobox.anatomy.stories.tsx +14 -14
  101. package/src/components/Combobox/combobox.principles.stories.tsx +6 -6
  102. package/src/components/Combobox/combobox.spec.md +1 -1
  103. package/src/components/Combobox/combobox.tsx +25 -14
  104. package/src/components/Command/command.anatomy.stories.tsx +2 -0
  105. package/src/components/Command/command.principles.stories.tsx +7 -7
  106. package/src/components/Command/command.tsx +2 -2
  107. package/src/components/DataTable/cell-registry.tsx +6 -2
  108. package/src/components/DataTable/data-table-filter-panel.tsx +3 -3
  109. package/src/components/DataTable/data-table.anatomy.stories.tsx +1 -1
  110. package/src/components/DataTable/data-table.css +1 -1
  111. package/src/components/DataTable/data-table.principles.stories.tsx +3 -3
  112. package/src/components/DataTable/data-table.spec.md +25 -17
  113. package/src/components/DataTable/data-table.stories.tsx +29 -25
  114. package/src/components/DataTable/data-table.tsx +92 -44
  115. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +1 -1
  116. package/src/components/DateGrid/date-grid.principles.stories.tsx +4 -4
  117. package/src/components/DateGrid/date-grid.spec.md +1 -1
  118. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +15 -11
  119. package/src/components/DatePicker/date-picker.principles.stories.tsx +5 -5
  120. package/src/components/DatePicker/date-picker.spec.md +1 -1
  121. package/src/components/DatePicker/date-picker.tsx +9 -6
  122. package/src/components/DescriptionList/description-list.principles.stories.tsx +5 -5
  123. package/src/components/DescriptionList/description-list.tsx +1 -1
  124. package/src/components/Dialog/dialog.anatomy.stories.tsx +1 -1
  125. package/src/components/Dialog/dialog.principles.stories.tsx +4 -4
  126. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
  127. package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +5 -5
  128. package/src/components/DropdownMenu/dropdown-menu.spec.md +1 -1
  129. package/src/components/Empty/empty.principles.stories.tsx +2 -2
  130. package/src/components/Empty/empty.tsx +2 -0
  131. package/src/components/Field/field-controls.spec.md +9 -6
  132. package/src/components/Field/field-wrapper.tsx +4 -4
  133. package/src/components/Field/field.principles.stories.tsx +4 -4
  134. package/src/components/FileItem/file-item.principles.stories.tsx +6 -5
  135. package/src/components/FileUpload/file-upload.principles.stories.tsx +6 -6
  136. package/src/components/FileUpload/file-upload.spec.md +1 -1
  137. package/src/components/FileViewer/file-viewer.principles.stories.tsx +5 -5
  138. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -6
  139. package/src/components/Input/input.anatomy.stories.tsx +3 -3
  140. package/src/components/Input/input.principles.stories.tsx +4 -4
  141. package/src/components/LinkInput/link-input.anatomy.stories.tsx +3 -3
  142. package/src/components/LinkInput/link-input.principles.stories.tsx +5 -5
  143. package/src/components/Menu/menu-item.principles.stories.tsx +7 -7
  144. package/src/components/Notice/notice.anatomy.stories.tsx +1 -1
  145. package/src/components/Notice/notice.principles.stories.tsx +7 -7
  146. package/src/components/NumberInput/number-input.anatomy.stories.tsx +8 -7
  147. package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
  148. package/src/components/NumberInput/number-input.spec.md +1 -1
  149. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +5 -5
  150. package/src/components/OverflowIndicator/overflow-indicator.tsx +1 -1
  151. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +3 -3
  152. package/src/components/PeoplePicker/people-picker.spec.md +3 -3
  153. package/src/components/PeoplePicker/people-picker.tsx +6 -6
  154. package/src/components/Popover/popover.principles.stories.tsx +5 -5
  155. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
  156. package/src/components/ProfileCard/profile-card.principles.stories.tsx +1 -1
  157. package/src/components/ProfileCard/profile-card.tsx +1 -1
  158. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +2 -2
  159. package/src/components/ProgressBar/progress-bar.spec.md +1 -1
  160. package/src/components/RadioGroup/radio-group.principles.stories.tsx +2 -2
  161. package/src/components/Rating/rating.anatomy.stories.tsx +2 -2
  162. package/src/components/Rating/rating.principles.stories.tsx +3 -3
  163. package/src/components/Rating/rating.spec.md +1 -1
  164. package/src/components/Rating/rating.tsx +1 -1
  165. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +4 -4
  166. package/src/components/Select/select.anatomy.stories.tsx +18 -18
  167. package/src/components/Select/select.principles.stories.tsx +5 -5
  168. package/src/components/Select/select.spec.md +1 -1
  169. package/src/components/Select/select.tsx +7 -7
  170. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +1 -1
  171. package/src/components/SelectMenu/select-menu.principles.stories.tsx +8 -8
  172. package/src/components/SelectionControl/selection-item.principles.stories.tsx +7 -7
  173. package/src/components/Separator/separator.principles.stories.tsx +4 -4
  174. package/src/components/Sheet/sheet.principles.stories.tsx +2 -2
  175. package/src/components/Sidebar/sidebar.principles.stories.tsx +4 -4
  176. package/src/components/Sidebar/sidebar.spec.md +2 -2
  177. package/src/components/Skeleton/skeleton.principles.stories.tsx +5 -5
  178. package/src/components/Slider/slider.anatomy.stories.tsx +1 -1
  179. package/src/components/Slider/slider.principles.stories.tsx +3 -3
  180. package/src/components/Steps/steps.principles.stories.tsx +4 -4
  181. package/src/components/Steps/steps.spec.md +2 -2
  182. package/src/components/Switch/switch.principles.stories.tsx +1 -1
  183. package/src/components/Tabs/tabs.principles.stories.tsx +3 -3
  184. package/src/components/Tabs/tabs.spec.md +1 -1
  185. package/src/components/Tag/tag.principles.stories.tsx +3 -3
  186. package/src/components/Textarea/textarea.principles.stories.tsx +2 -2
  187. package/src/components/Textarea/textarea.tsx +3 -3
  188. package/src/components/TimePicker/time-picker.principles.stories.tsx +5 -5
  189. package/src/components/TimePicker/time-picker.spec.md +1 -1
  190. package/src/components/TimePicker/time-picker.tsx +11 -12
  191. package/src/components/Toast/toast.principles.stories.tsx +2 -2
  192. package/src/components/Tooltip/tooltip.principles.stories.tsx +3 -3
  193. package/src/components/TreeView/tree-view.principles.stories.tsx +5 -5
  194. package/src/components/TreeView/tree-view.stories.tsx +1 -1
  195. package/src/components/TreeView/tree-view.tsx +1 -1
  196. package/src/patterns/element-anatomy/item-anatomy.spec.md +1 -1
  197. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +1 -1
  198. package/src/patterns/overlay-surface/overlay-surface.spec.md +1 -0
  199. package/src/patterns/resize-handle/resize-handle.spec.md +1 -1
  200. package/src/tokens/color/color.spec.md +2 -0
  201. package/src/tokens/color/semantic.css +1 -1
  202. package/src/tokens/uiSize/uiSize.css +5 -0
  203. package/src/tokens/uiSize/uiSize.spec.md +17 -3
@@ -149,16 +149,16 @@ export const UsageGuidance: Story = {
149
149
  <p>適合 Chart 的真實業務場景(點擊跳轉「展示」頁範例):</p>
150
150
  <ul className="space-y-1">
151
151
  <li>
152
- <LinkTo kind="Design System/Components/Chart/展示" name="長條 Chart — 月營收"><span className="text-primary hover:underline font-medium cursor-pointer">長條 Chart — 月營收</span></LinkTo>
152
+ <LinkTo kind="Design System/Components/Chart/展示" name="長條 Chart — 月營收"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">長條 Chart — 月營收</span></LinkTo>
153
153
  </li>
154
154
  <li>
155
- <LinkTo kind="Design System/Components/Chart/展示" name="折線 Chart — 伺服器回應時間"><span className="text-primary hover:underline font-medium cursor-pointer">折線 Chart — 伺服器回應時間</span></LinkTo>
155
+ <LinkTo kind="Design System/Components/Chart/展示" name="折線 Chart — 伺服器回應時間"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">折線 Chart — 伺服器回應時間</span></LinkTo>
156
156
  </li>
157
157
  <li>
158
- <LinkTo kind="Design System/Components/Chart/展示" name="環圈 Chart — 流量來源分布"><span className="text-primary hover:underline font-medium cursor-pointer">環圈 Chart — 流量來源分布</span></LinkTo>
158
+ <LinkTo kind="Design System/Components/Chart/展示" name="環圈 Chart — 流量來源分布"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">環圈 Chart — 流量來源分布</span></LinkTo>
159
159
  </li>
160
160
  <li>
161
- <LinkTo kind="Design System/Components/Chart/展示" name="堆疊 面積 — 部門支出"><span className="text-primary hover:underline font-medium cursor-pointer">堆疊 面積 — 部門支出</span></LinkTo>
161
+ <LinkTo kind="Design System/Components/Chart/展示" name="堆疊 面積 — 部門支出"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">堆疊 面積 — 部門支出</span></LinkTo>
162
162
  </li>
163
163
  </ul>
164
164
  <p className="text-fg-muted mt-3">不確定該不該用 Chart 時:先確認資料是否真的需要視覺化(單一數字、2-3 個小數字或可排序篩選的清單,通常用文字、描述清單或表格更清楚);若資料量夠多且要看趨勢、比例或分布,才用 Chart。</p>
@@ -15,7 +15,7 @@ import { cn } from '@/lib/utils'
15
15
  * 預設建議使用 --chart-1..5(本 DS 提供的 5 色類別 token)
16
16
  *
17
17
  * ── 視覺 token ──
18
- * Tooltip: bg-surface-raised / border-border / shadow-[elevation-200] / rounded-md
18
+ * Tooltip: bg-surface-raised / border-border / shadow-[var(--elevation-200)] / rounded-md
19
19
  * Legend text: text-fg-secondary / text-caption
20
20
  * Grid: stroke-divider
21
21
  * Axis tick: text-fg-muted / text-caption
@@ -52,10 +52,10 @@ export const UsageGuidance: Story = {
52
52
  >
53
53
  <ul className="space-y-1">
54
54
  <li>
55
- <LinkTo kind="Design System/Components/Checkbox/展示" name="直式群組"><span className="text-primary hover:underline font-medium cursor-pointer">商品類別的複選清單(直式群組)</span></LinkTo>
55
+ <LinkTo kind="Design System/Components/Checkbox/展示" name="直式群組"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">商品類別的複選清單(直式群組)</span></LinkTo>
56
56
  </li>
57
57
  <li>
58
- <LinkTo kind="Design System/Components/Checkbox/展示" name="水平排列"><span className="text-primary hover:underline font-medium cursor-pointer">篩選列的少量選項複選(水平排列)</span></LinkTo>
58
+ <LinkTo kind="Design System/Components/Checkbox/展示" name="水平排列"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">篩選列的少量選項複選(水平排列)</span></LinkTo>
59
59
  </li>
60
60
  </ul>
61
61
  </Rule>
@@ -281,7 +281,7 @@ export const checkboxMeta = {
281
281
  // 2026-06-10 修 stale meta:iconSize 對齊 checkIconSize 真值(L49 sm/md=12, lg=16;deep-audit A.1b 抓 metadata drift)
282
282
  sm: { fieldHeight: 28, iconSize: 12, typography: 'body' },
283
283
  md: { fieldHeight: 32, iconSize: 12, typography: 'body' },
284
- lg: { fieldHeight: 36, iconSize: 16, typography: 'body' },
284
+ lg: { fieldHeight: 36, iconSize: 16, typography: 'body-lg' },
285
285
  },
286
286
  states: ['default', 'hover', 'focus-visible', 'disabled'],
287
287
  tokens: {
@@ -51,13 +51,13 @@ export const UsageGuidance: Story = {
51
51
  <p>適合 Chip 的真實業務場景(點擊跳轉「展示」頁範例):</p>
52
52
  <ul className="space-y-1">
53
53
  <li>
54
- <LinkTo kind="Design System/Components/Chip/展示" name="預設"><span className="text-primary hover:underline font-medium cursor-pointer">技術文章列表的語言標籤濾鏡(多選)</span></LinkTo>
54
+ <LinkTo kind="Design System/Components/Chip/展示" name="預設"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">技術文章列表的語言標籤濾鏡(多選)</span></LinkTo>
55
55
  </li>
56
56
  <li>
57
- <LinkTo kind="Design System/Components/Chip/展示" name="帶 Badge"><span className="text-primary hover:underline font-medium cursor-pointer">任務列表的狀態篩選 — Badge 顯示各狀態筆數</span></LinkTo>
57
+ <LinkTo kind="Design System/Components/Chip/展示" name="帶 Badge"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">任務列表的狀態篩選 — Badge 顯示各狀態筆數</span></LinkTo>
58
58
  </li>
59
59
  <li>
60
- <LinkTo kind="Design System/Components/Chip/展示" name="單選"><span className="text-primary hover:underline font-medium cursor-pointer">程式語言擇一的單選濾鏡(type="single")</span></LinkTo>
60
+ <LinkTo kind="Design System/Components/Chip/展示" name="單選"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">程式語言擇一的單選濾鏡(type="single")</span></LinkTo>
61
61
  </li>
62
62
  </ul>
63
63
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方「vs 近親元件」段)。</p>
@@ -167,7 +167,7 @@ export const Inspector: Story = {
167
167
  <tr><Td>Previous / Skip variant</Td><Td mono>tertiary</Td></tr>
168
168
  <tr><Td>Next variant</Td><Td mono>primary</Td></tr>
169
169
  <tr><Td>sideOffset</Td><Td mono>8px(對齊 Popover DS 設計準則)</Td></tr>
170
- <tr><Td>Density</Td><Td mono>繼承 Popover(鎖 md)</Td></tr>
170
+ <tr><Td>Layout-space</Td><Td mono>繼承 Popover(鎖 layout-space md;ui-size 繼承 page)</Td></tr>
171
171
  </tbody>
172
172
  </table>
173
173
  </div>
@@ -70,9 +70,9 @@ export const UsageGuidance: Story = {
70
70
  <div className="prose prose-sm max-w-prose mb-8">
71
71
  <p>適合 Coachmark 的真實業務場景(點擊跳轉「展示」頁範例):</p>
72
72
  <ul className="space-y-1">
73
- <li><LinkTo kind="Design System/Components/Coachmark/展示" name="單步驟新功能介紹"><span className="text-primary hover:underline font-medium cursor-pointer">首次推出 AI 助理時 anchor 到入口按鈕介紹(單步驟)</span></LinkTo></li>
74
- <li><LinkTo kind="Design System/Components/Coachmark/展示" name="多步 新手導覽"><span className="text-primary hover:underline font-medium cursor-pointer">新用戶首登的三步功能導覽(多步 Onboarding)</span></LinkTo></li>
75
- <li><LinkTo kind="Design System/Components/Coachmark/展示" name="多步提示"><span className="text-primary hover:underline font-medium cursor-pointer">進階快捷鍵的漸進式提示(多步 Tips)</span></LinkTo></li>
73
+ <li><LinkTo kind="Design System/Components/Coachmark/展示" name="單步驟新功能介紹"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">首次推出 AI 助理時 anchor 到入口按鈕介紹(單步驟)</span></LinkTo></li>
74
+ <li><LinkTo kind="Design System/Components/Coachmark/展示" name="多步 新手導覽"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">新用戶首登的三步功能導覽(多步 Onboarding)</span></LinkTo></li>
75
+ <li><LinkTo kind="Design System/Components/Coachmark/展示" name="多步提示"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">進階快捷鍵的漸進式提示(多步 Tips)</span></LinkTo></li>
76
76
  </ul>
77
77
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方 vs 近親 段)。</p>
78
78
  </div>
@@ -193,7 +193,7 @@ Previous(可選)→ Skip(可選)→ Next / Done。對齊 Ant Tour / Intercom con
193
193
  - ❌ **不用 Coachmark 做確認框**——確認破壞性動作必須阻斷流程,改用 `Dialog`
194
194
  - ❌ **Media 區不放互動元件**——按鈕 / 輸入 / checkbox 一律走 footer。Media 是視覺說明不是互動區
195
195
  - ❌ **Description 不寫超過 3 行**——Coachmark 是快速說明,超過 3 行改用 Dialog + 完整 body 或連結到說明文件
196
- - ❌ **不強迫完成 tour**——永遠提供退出機制(Esc / header Close / 第一步 Skip)。沒有退出的 onboarding 讓使用者感到被綁架,反而降低完成率
196
+ - ❌ **不強迫完成 tour**——永遠提供退出機制(Esc / header Close / 第一步 Skip)。沒有退出的 onboarding 讓使用者感到被綁架,反而降低完成率。註:header Close 僅在傳 `kind`(有 header)時才 render;無 `kind` 的 multi-step tour 第 2+ 步退出靠 Esc(永遠可用),multi-step 務必傳 `kind` 以提供 header Close 退出入口
197
197
  - ❌ **單步驟 CTA 不叫 "Next"**——沒有下一步就不用 Next 字眼,用 `'知道了' / 'Got it' / 'Start'`
198
198
  - ❌ **有 Prev 時不同時顯示 Skip**——使用者投入進度後兩個退出路徑衝突(見 CTA 語義表)
199
199
  - ❌ **不自包視覺 token**——bg / shadow / radius / padding 一律繼承 Popover,改視覺就改 Popover
@@ -212,7 +212,7 @@ Previous(可選)→ Skip(可選)→ Next / Done。對齊 Ant Tour / Intercom con
212
212
 
213
213
  ## 邊界狀態
214
214
 
215
- disabled / density 繼承 Popover(density 鎖 md,見 `../Popover/popover.spec.md`);empty(title + description 都不傳)則 Body 不渲染(已於「Props 結構」規定);loading 由 consumer 決定。
215
+ disabled / layout-space 繼承 Popover(鎖 `data-layout-space="md"` 保持 header py-tight 精簡;ui-size / 控件大小繼承 page density),SSOT 見 `tokens/density/density.spec.md` L47;empty(title + description 都不傳)則 Body 不渲染(已於「Props 結構」規定);loading 由 consumer 決定。
216
216
 
217
217
  - **步數 > 5**:元件不機械限制 — `step.total` 照實顯示(如「6 of 8」);≤ 5 是設計建議(見 Multi-step Tour),超過應改靜態 onboarding 頁
218
218
  - **無 `image`**:整個 Media 區不渲染(非 skeleton 佔位,見「Props 結構」)
@@ -39,15 +39,15 @@ const TOKEN_MAP: Record<ModeKey, Record<StateKey, ColorSpec>> = {
39
39
  },
40
40
  readonly: {
41
41
  default: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '—' },
42
- hover: { bg: '--bg-disabled', text: '--foreground', border: 'transparent', icon: '—' },
43
- focus: { bg: '--bg-disabled', text: '--foreground', border: 'transparent', icon: '—' },
44
- disabled: { bg: '--bg-disabled', text: '--foreground', border: 'transparent', icon: '—' },
42
+ hover: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '—' },
43
+ focus: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '—' },
44
+ disabled: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '—' },
45
45
  },
46
46
  disabled: {
47
- default: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent', icon: '' },
48
- hover: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent', icon: '' },
49
- focus: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent', icon: '' },
50
- disabled: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent', icon: '' },
47
+ default: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent', icon: '--fg-disabled' },
48
+ hover: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent', icon: '--fg-disabled' },
49
+ focus: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent', icon: '--fg-disabled' },
50
+ disabled: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent', icon: '--fg-disabled' },
51
51
  },
52
52
  }
53
53
 
@@ -270,7 +270,7 @@ export const Overview = {
270
270
  <div className="flex flex-col gap-4">
271
271
  <div className="flex flex-col gap-1">
272
272
  <H3>結構(Anatomy)— readonly / disabled</H3>
273
- <Desc>Tag 沒有 dismiss 按鈕、沒有 clear;ChevronDown 保留為類型身份 indicator(pointer-events-none,readonly fg-muted / disabled fg-disabled)。溢出行為與 edit 相同(+N 指示器)。</Desc>
273
+ <Desc>Tag 沒有 dismiss 按鈕、沒有 clear;readonly 不顯示 ChevronDown(純值、不可開下拉),disabled 保留為類型身份 indicator(pointer-events-none,fg-disabled)。溢出行為與 edit 相同(+N 指示器)。</Desc>
274
274
  </div>
275
275
  <div className="flex gap-8">
276
276
  <div className="flex flex-col gap-2 items-start">
@@ -285,7 +285,7 @@ export const Overview = {
285
285
  style={{ borderColor: `var(--${s.color})`, backgroundColor: `var(--${s.color}-subtle)`, color: `var(--${s.color})` }}>{s.name}</span>
286
286
  ))}
287
287
  </div>
288
- <span className="text-[10px] text-fg-muted font-mono">無 dismiss · 無 clear · chevron=類型 indicator · tagPadding</span>
288
+ <span className="text-[10px] text-fg-muted font-mono">無 dismiss · 無 clear · chevron:readonly 不顯示 / disabled 保留 · tagPadding</span>
289
289
  </div>
290
290
  <div className="flex flex-col gap-2 items-start">
291
291
  <span className="text-[11px] text-fg-secondary font-medium">空值</span>
@@ -466,7 +466,7 @@ const InspectorInner = () => {
466
466
  <PropRow label="Fill"><TokenValue value={colors.bg} /></PropRow>
467
467
  <PropRow label="Text"><TokenValue value={colors.text} /></PropRow>
468
468
  <PropRow label="Stroke"><TokenValue value={colors.border} /></PropRow>
469
- {isEdit && <PropRow label="Icon"><TokenValue value={colors.icon} /></PropRow>}
469
+ {mode !== 'readonly' && <PropRow label="Icon"><TokenValue value={colors.icon} /></PropRow>}
470
470
  </div>
471
471
 
472
472
  {/* LAYOUT */}
@@ -479,7 +479,7 @@ const InspectorInner = () => {
479
479
  }
480
480
  </PropRow>
481
481
  <PropRow label="tagPadding" dot={Z.pad.text}><TkVal token="calc()" value={s.tagPaddingCalc} /></PropRow>
482
- <PropRow label="右側內距">0.75rem (12px)</PropRow>
482
+ <PropRow label="右側內距">var(--field-px) (12px)</PropRow>
483
483
  <PropRow label="Tag 間距" dot={Z.gap.text}>{s.tagGap}</PropRow>
484
484
  <PropRow label="Icon 尺寸" dot={Z.icon.text}>{s.icon}px</PropRow>
485
485
  <PropRow label="Tag 高度" dot={Z.tag.text}>{s.tagHeight} ({s.tagSize})</PropRow>
@@ -677,7 +677,7 @@ export const SizeMatrix = {
677
677
  {SIZES.map((sz) => (
678
678
  <Td key={sz} mono>
679
679
  <div className="text-fg-secondary">paddingRight</div>
680
- <div className="text-fg-muted text-[10px]">0.75rem (12px)</div>
680
+ <div className="text-fg-muted text-[10px]">var(--field-px) (12px)</div>
681
681
  </Td>
682
682
  ))}
683
683
  </tr>
@@ -850,7 +850,7 @@ export const StateBehavior = {
850
850
  <button
851
851
  type="button"
852
852
  onClick={() => setClearV(['electronics', 'food', 'lifestyle'])}
853
- className="text-caption text-primary cursor-pointer hover:underline"
853
+ className="text-caption text-primary cursor-pointer hover:text-primary-hover"
854
854
  >
855
855
  重設
856
856
  </button>
@@ -876,7 +876,7 @@ export const StateBehavior = {
876
876
  <button
877
877
  type="button"
878
878
  onClick={() => setDismissV(['electronics', 'food', 'lifestyle'])}
879
- className="text-caption text-primary cursor-pointer hover:underline"
879
+ className="text-caption text-primary cursor-pointer hover:text-primary-hover"
880
880
  >
881
881
  重設
882
882
  </button>
@@ -57,19 +57,19 @@ export const UsageGuidance: Story = {
57
57
  <p>適合 Combobox 的真實業務場景(點擊跳轉「展示」頁範例):</p>
58
58
  <ul className="space-y-1">
59
59
  <li>
60
- <LinkTo kind="Design System/Components/Combobox/展示" name="四模式"><span className="text-primary hover:underline font-medium cursor-pointer">四模式</span></LinkTo>
60
+ <LinkTo kind="Design System/Components/Combobox/展示" name="四模式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">四模式</span></LinkTo>
61
61
  </li>
62
62
  <li>
63
- <LinkTo kind="Design System/Components/Combobox/展示" name="尺寸與 Button 對齊"><span className="text-primary hover:underline font-medium cursor-pointer">尺寸與 Button 對齊</span></LinkTo>
63
+ <LinkTo kind="Design System/Components/Combobox/展示" name="尺寸與 Button 對齊"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">尺寸與 Button 對齊</span></LinkTo>
64
64
  </li>
65
65
  <li>
66
- <LinkTo kind="Design System/Components/Combobox/展示" name="單行 vs 換行"><span className="text-primary hover:underline font-medium cursor-pointer">單行 vs 換行</span></LinkTo>
66
+ <LinkTo kind="Design System/Components/Combobox/展示" name="單行 vs 換行"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">單行 vs 換行</span></LinkTo>
67
67
  </li>
68
68
  <li>
69
- <LinkTo kind="Design System/Components/Combobox/展示" name="搜尋"><span className="text-primary hover:underline font-medium cursor-pointer">搜尋</span></LinkTo>
69
+ <LinkTo kind="Design System/Components/Combobox/展示" name="搜尋"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">搜尋</span></LinkTo>
70
70
  </li>
71
71
  <li>
72
- <LinkTo kind="Design System/Components/Combobox/展示" name="DataTable 整合"><span className="text-primary hover:underline font-medium cursor-pointer">DataTable 整合</span></LinkTo>
72
+ <LinkTo kind="Design System/Components/Combobox/展示" name="DataTable 整合"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">DataTable 整合</span></LinkTo>
73
73
  </li>
74
74
  </ul>
75
75
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見 <code>Vs*Rule</code> stories)。</p>
@@ -195,7 +195,7 @@ export const TagOperationRule: Story = {
195
195
 
196
196
  <Rule
197
197
  title="readonly / disabled 的 Tag 沒有任何互動"
198
- note="沒有 dismiss X、沒有 clear;ChevronDown 保留為類型身份 indicator(不可開啟)。Tag 變純顯示,溢出規則仍然套用(+N 指示器可 hover 查看完整)"
198
+ note="沒有 dismiss X、沒有 clear;readonly 不顯示 ChevronDown(純值、不可開啟),disabled 才保留為類型身份 indicator。Tag 變純顯示,溢出規則仍然套用(+N 指示器可 hover 查看完整)"
199
199
  >
200
200
  <Combobox mode="readonly" options={categoryOptions} value={ro} />
201
201
  <Label>↑ 不可移除、不可新增、不可清空——整個 field 變「顯示」</Label>
@@ -116,7 +116,7 @@ value 軸 controlled-only;open 軸方向相反 — **uncontrolled-only**:`defaul
116
116
  ## readonly / disabled 的 Tag
117
117
 
118
118
  - 沒有 dismiss 按鈕——不可操作
119
- - ChevronDown 保留——類型身份 indicator(2026-06-10;`pointer-events-none` 不可開啟新增、readonly `fg-muted` / disabled `fg-disabled`、naked cell 依 `showDisplayEndIcon`)
119
+ - ChevronDown:**readonly 不顯示**(純值、不可開下拉)/ **disabled 保留**(類型身份 indicator,`fg-disabled`,`pointer-events-none`)/ naked cell 依 `showDisplayEndIcon`(2026-06-26)
120
120
  - 沒有 clear 按鈕——不可清除
121
121
  - 溢出行為與 edit 模式相同(+N 指示器)
122
122
 
@@ -448,7 +448,7 @@ export interface ComboboxProps {
448
448
  * **2026-05-13 v2 deprecate path**:原 PeoplePicker pass `{8}` 假設「Combobox tagPadding=4px,4+8=12」
449
449
  * 但 `tagPadding[size]` 是 density-dependent calc `(field-height - icon-size) / 2`,只在 md size +
450
450
  * default density 才 = 4px;其他 size/density 漂 6/8px → 4+8=12 公式破。改 PeoplePicker 直接 inject
451
- * `!px-3` className 到 Combobox Field wrapper(per people-picker.spec.md:94 v2),`tagAreaPaddingLeftPx`
451
+ * `!px-[var(--field-px)]` className 到 Combobox Field wrapper(per people-picker.spec.md:94 v2),`tagAreaPaddingLeftPx`
452
452
  * 走 undefined。Future 仍保留此 prop 給其他 consumer 精準調整 padding,但 PeoplePicker 已不再用。
453
453
  */
454
454
  tagAreaPaddingLeftPx?: number
@@ -488,6 +488,7 @@ function ReadonlyMultiSelect({
488
488
  const variant = variantProp ?? 'default'
489
489
  const sz = size ?? 'md'
490
490
  const iconSize = sz === 'lg' ? 20 : 16
491
+ const tagHeight = sz === 'sm' ? 20 : 24
491
492
  const containerRef = React.useRef<HTMLDivElement>(null)
492
493
  const hasTags = (value?.length ?? 0) > 0
493
494
 
@@ -527,8 +528,10 @@ function ReadonlyMultiSelect({
527
528
  // 2026-05-18 #6A Round 1 Step 1/4(per user 拍板「決策6選a」+ codex M31 Step 5 verdict cite combobox.tsx:451):
528
529
  // readonly/disabled path 對齊 L293 display wrapper 已 ship 的 overflow-hidden fix。
529
530
  // M10 propagation:原 overflow-visible 讓 readonly tag 越界蓋 indicator,跟 display 不對稱。
530
- wrap ? 'flex-wrap py-1' : 'overflow-hidden', className)}
531
- style={{ gap: GAP, ...(wrap ? { height: 'auto' } : undefined) }} data-field-mode={resolvedMode}
531
+ // 2026-06-27 對齊 edit path(L598-617):wrap 時 items-start + chevron self-start/tagHeight 鎖第一行;
532
+ // paddingRight: var(--field-px) re-assert 右緣 12px(tagPadding 對稱 calc 會吃掉右緣,跟 edit 一致)
533
+ wrap ? 'flex-wrap items-start py-1' : 'overflow-hidden', className)}
534
+ style={{ gap: GAP, paddingRight: 'var(--field-px)', ...(wrap ? { height: 'auto' } : undefined) }} data-field-mode={resolvedMode}
532
535
  aria-disabled={resolvedMode === 'disabled' ? true : undefined}>
533
536
  {hasTags ? (
534
537
  <ComboboxTagStack value={value} options={options} tagSize={sz} wrap={wrap}
@@ -536,9 +539,9 @@ function ReadonlyMultiSelect({
536
539
  ) : (
537
540
  <span className="text-fg-muted">{EMPTY_DISPLAY}</span>
538
541
  )}
539
- {/* 2026-06-10 類型身份 indicator 規則:readonly/disabled 保留 chevron(naked cell 依 showDisplayEndIcon);disabled → fg-disabled */}
540
- {(variant === 'naked' ? !!showDisplayEndIcon : true) && (
541
- <ItemSuffix className="pointer-events-none">
542
+ {/* 2026-06-26 類型身份 indicator:edit 顯示 / readonly 不顯示(純值、不可開下拉) / disabled 保留(fg-disabled,對齊原生 <select disabled>);naked cell 依 showDisplayEndIcon */}
543
+ {(variant === 'naked' ? !!showDisplayEndIcon : resolvedMode === 'disabled') && (
544
+ <ItemSuffix className={cn('pointer-events-none', wrap && 'self-start')} style={wrap ? { height: tagHeight } : undefined}>
542
545
  <ChevronDown size={iconSize} className={cn('shrink-0', resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted')} aria-hidden />
543
546
  </ItemSuffix>
544
547
  )}
@@ -575,14 +578,18 @@ function NativeCombobox({
575
578
  const handleRemove = (v: string) => onChange?.(value.filter(x => x !== v))
576
579
  const handleAdd = (v: string) => { if (!value.includes(v)) onChange?.([...value, v]) }
577
580
 
581
+ // React #310 fix(對齊 select.tsx):hooks 必在 conditional early-return 前無條件呼叫。
582
+ // resolvedMode 在 edit↔非edit 切換(<Field mode/disabled> cascade / DataTable cell 進出編輯)時
583
+ // hook 數量不可變動,否則 Rules of Hooks violation → React #310 「rendered fewer/more hooks」crash。
584
+ const selectRef = React.useRef<HTMLSelectElement>(null)
585
+ const tagAreaRef = React.useRef<HTMLDivElement>(null)
586
+
578
587
  if (resolvedMode !== 'edit') {
579
588
  return <ReadonlyMultiSelect mode={resolvedMode} variant={variant} size={size} options={options} value={value} wrap={wrap} className={className} showDisplayEndIcon={showDisplayEndIcon} />
580
589
  }
581
590
 
582
591
  const items = value.map(v => ({ value: v, label: options.find(o => o.value === v)?.label ?? v }))
583
592
  const unselected = options.filter(o => !value.includes(o.value))
584
- const selectRef = React.useRef<HTMLSelectElement>(null)
585
- const tagAreaRef = React.useRef<HTMLDivElement>(null)
586
593
  const tagHeight = size === 'sm' ? 20 : 24
587
594
 
588
595
  const selectDropdown = unselected.length > 0 ? (
@@ -597,7 +604,7 @@ function NativeCombobox({
597
604
  return (
598
605
  <div ref={__triggerRef} className={cn(fieldWrapperStyles({ mode: 'edit', variant: variant, size }), value.length > 0 && tagPadding[size], 'relative',
599
606
  wrap && 'items-start py-1', error && ['border-error hover:border-error-hover', 'focus-within:border-error focus-within:hover:border-error'], className)}
600
- style={{ paddingRight: '0.75rem', ...(wrap ? { height: 'auto' } : undefined) }} data-field-mode="edit" data-error={error ? '' : undefined}
607
+ style={{ paddingRight: 'var(--field-px)', ...(wrap ? { height: 'auto' } : undefined) }} data-field-mode="edit" data-error={error ? '' : undefined}
601
608
  onClick={(e) => { if (e.target === e.currentTarget) { selectRef.current?.showPicker?.(); selectRef.current?.focus() } }}>
602
609
  {/* 2026-05-18 F2 sync(per user verbatim「modifying 修好 PeoplePicker stack 後改壞 Combobox tag display」
603
610
  + 「tag 應該要判斷所在空間最多可以呈現幾個tag(包括+n)去自動判斷何時要變成+n」):
@@ -671,10 +678,10 @@ function CustomCombobox({
671
678
 
672
679
  React.useEffect(() => { if (!open) setSearch('') }, [open])
673
680
 
674
- if (resolvedMode !== 'edit') {
675
- return <ReadonlyMultiSelect mode={resolvedMode} variant={variant} size={size} options={options} value={value} wrap={wrap} className={className} showDisplayEndIcon={showDisplayEndIcon} />
676
- }
677
-
681
+ // React #310 fix(對齊 select.tsx):以下 hooks(useMemo/useRef)必在 conditional early-return 前
682
+ // 無條件呼叫。resolvedMode edit↔非edit 切換時 hook 數量不可變動,否則 Rules of Hooks
683
+ // violation → React #310 「rendered fewer/more hooks」crash(ReadonlyMultiSelect 用不到這些值,
684
+ // 多算無害,對齊 select.tsx hoist pattern)。
678
685
  const items = React.useMemo(
679
686
  () => value.map(v => ({ value: v, label: options.find(o => o.value === v)?.label ?? v })),
680
687
  [value, options]
@@ -708,6 +715,10 @@ function CustomCombobox({
708
715
  [filteredOptions]
709
716
  )
710
717
 
718
+ if (resolvedMode !== 'edit') {
719
+ return <ReadonlyMultiSelect mode={resolvedMode} variant={variant} size={size} options={options} value={value} wrap={wrap} className={className} showDisplayEndIcon={showDisplayEndIcon} />
720
+ }
721
+
711
722
  const chevronEl = <ChevronDown size={iconSize} className={cn('shrink-0 text-fg-muted transition-transform', open && 'rotate-180')} aria-hidden />
712
723
 
713
724
  const trigger = (
@@ -725,7 +736,7 @@ function CustomCombobox({
725
736
  // 2026-05-06 v13.3 SSOT retire:per-control `open && 'border-primary'` 移除。Field default
726
737
  // 統一處理 — open=灰深(data-state)/ focus=藍(focus-within !important)。改一處全 control 跟動。
727
738
  error && ['border-error hover:border-error-hover', 'focus-within:border-error focus-within:hover:border-error'], className)}
728
- style={{ paddingRight: '0.75rem', ...(wrap ? { height: 'auto' } : undefined) }}
739
+ style={{ paddingRight: 'var(--field-px)', ...(wrap ? { height: 'auto' } : undefined) }}
729
740
  data-field-mode="edit" data-error={error ? '' : undefined}
730
741
  // WAI-ARIA APG combobox 鍵盤開啟語意 — 對齊 sibling Select(select.tsx:593-598)。
731
742
  // <div role=combobox> 不像 native <button> 自動 synthesize Enter/Space click,故顯式補:
@@ -14,6 +14,7 @@ import {
14
14
  CommandGroup,
15
15
  CommandItem,
16
16
  CommandSeparator,
17
+ CommandShortcut,
17
18
  } from './command'
18
19
  import { H3, Desc, Td, Th } from '@/design-system/stories-helpers/anatomy/anatomy-utils'
19
20
 
@@ -44,6 +45,7 @@ export const Overview: Story = {
44
45
  <CommandItem>
45
46
  <Settings className="mr-2 h-4 w-4" />
46
47
  前往設定
48
+ <CommandShortcut>⌘K</CommandShortcut>
47
49
  </CommandItem>
48
50
  </CommandGroup>
49
51
  <CommandSeparator />
@@ -24,16 +24,16 @@ export const UsageGuidance: Story = {
24
24
  <p>適合 Command 的真實業務場景(點擊跳轉「展示」頁範例):</p>
25
25
  <ul className="space-y-1">
26
26
  <li>
27
- <LinkTo kind="Design System/Internal/Command/展示" name="全域指令面板"><span className="text-primary hover:underline font-medium cursor-pointer">全域指令面板(⌘K)— Linear / VS Code 式跨頁搜尋與快速動作</span></LinkTo>
27
+ <LinkTo kind="Design System/Internal/Command/展示" name="全域指令面板"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">全域指令面板(⌘K)— Linear / VS Code 式跨頁搜尋與快速動作</span></LinkTo>
28
28
  </li>
29
29
  <li>
30
- <LinkTo kind="Design System/Internal/Command/展示" name="行內搜尋清單"><span className="text-primary hover:underline font-medium cursor-pointer">行內搜尋清單 — Gmail 式 sidebar 信件資料夾過濾</span></LinkTo>
30
+ <LinkTo kind="Design System/Internal/Command/展示" name="行內搜尋清單"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">行內搜尋清單 — Gmail 式 sidebar 信件資料夾過濾</span></LinkTo>
31
31
  </li>
32
32
  <li>
33
- <LinkTo kind="Design System/Internal/Command/展示" name="外觀切換器"><span className="text-primary hover:underline font-medium cursor-pointer">外觀切換器 — 選中立即執行的純動作清單</span></LinkTo>
33
+ <LinkTo kind="Design System/Internal/Command/展示" name="外觀切換器"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">外觀切換器 — 選中立即執行的純動作清單</span></LinkTo>
34
34
  </li>
35
35
  <li>
36
- <LinkTo kind="Design System/Internal/Command/展示" name="無結果狀態"><span className="text-primary hover:underline font-medium cursor-pointer">搜尋無結果時的空狀態文案(CommandEmpty)</span></LinkTo>
36
+ <LinkTo kind="Design System/Internal/Command/展示" name="無結果狀態"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">搜尋無結果時的空狀態文案(CommandEmpty)</span></LinkTo>
37
37
  </li>
38
38
  </ul>
39
39
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方「vs 近親」段)。</p>
@@ -64,9 +64,9 @@ export const CompositionRules: Story = {
64
64
  <h4>Pattern 1 — SelectMenu 內嵌 Command(searchable form input)</h4>
65
65
  <p>需要「搜尋 + 選值寫回 form」→ 用 <code>SelectMenu</code>(內部已組合 <code>Popover + Command</code>),<strong>不要</strong>自己組合:</p>
66
66
  <ul>
67
- <li><LinkTo kind="Design System/Components/Combobox/展示" name="四模式"><span className="text-primary hover:underline font-medium cursor-pointer">Combobox</span></LinkTo>——可搜尋多選(如商品類別多選標籤)</li>
68
- <li><LinkTo kind="Design System/Components/Select/展示" name="搜尋"><span className="text-primary hover:underline font-medium cursor-pointer">Select(searchable)</span></LinkTo>——可搜尋單選(如從長國家清單打字篩選)</li>
69
- <li><LinkTo kind="Design System/Components/PeoplePicker/展示" name="單人"><span className="text-primary hover:underline font-medium cursor-pointer">PeoplePicker</span></LinkTo>——人員搜尋(如指派任務負責人)</li>
67
+ <li><LinkTo kind="Design System/Components/Combobox/展示" name="四模式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Combobox</span></LinkTo>——可搜尋多選(如商品類別多選標籤)</li>
68
+ <li><LinkTo kind="Design System/Components/Select/展示" name="搜尋"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Select(searchable)</span></LinkTo>——可搜尋單選(如從長國家清單打字篩選)</li>
69
+ <li><LinkTo kind="Design System/Components/PeoplePicker/展示" name="單人"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">PeoplePicker</span></LinkTo>——人員搜尋(如指派任務負責人)</li>
70
70
  </ul>
71
71
 
72
72
  <h4>Pattern 2 — Popover + Command 組成 Command Palette(Cmd+K)</h4>
@@ -135,7 +135,7 @@ const CommandItem = React.forwardRef<
135
135
  <CommandPrimitive.Item
136
136
  ref={ref}
137
137
  className={cn(
138
- "relative flex cursor-default gap-2 select-none items-center rounded-md px-2 py-1.5 text-body outline-none data-[disabled=true]:pointer-events-none data-[selected='true']:bg-neutral-hover data-[selected=true]:text-foreground data-[disabled=true]:text-fg-disabled [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
138
+ "relative flex cursor-default gap-2 select-none items-center rounded-md px-2 py-1.5 text-body outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-neutral-hover data-[selected=true]:text-foreground data-[disabled=true]:text-fg-disabled [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
139
139
  className
140
140
  )}
141
141
  {...props}
@@ -164,7 +164,7 @@ CommandShortcut.displayName = "CommandShortcut"
164
164
  // Phase 2 fill needed: purpose descriptions + when rationale + world-class refs
165
165
  export const commandMeta = {
166
166
  component: 'Command',
167
- family: null, // non-family composite / overlay / layout
167
+ family: 'composite', // 對齊 command.spec.md frontmatter family: composite(SSOT)
168
168
  variants: {
169
169
 
170
170
  },
@@ -174,6 +174,11 @@ function NumberCell({ value, meta, mode, size, isDisabled, onCommit, onCancel, o
174
174
  // currency 透過 columnType-aware prefix:type='currency' → 預設 '$'(可 override)
175
175
  const isCurrency = meta?.type === 'currency'
176
176
  const prefix = isCurrency ? (meta?.prefix ?? '$') : meta?.prefix
177
+ // React #310 fix:useState 必在 display early-return 前無條件呼叫。同一 memo'd cell instance
178
+ // 在 display↔edit 切換時被重用(render site 無 key={mode},data-table.tsx:1352),hook 數量不可變,
179
+ // 否則 Rules of Hooks violation → React #310 crash。對齊 combobox/select hoist pattern。
180
+ const initial = typeof value === 'number' ? value : null
181
+ const [localValue, setLocalValue] = React.useState<number | null>(initial)
177
182
  if (mode === 'display') {
178
183
  return (
179
184
  <NumberInput
@@ -193,8 +198,7 @@ function NumberCell({ value, meta, mode, size, isDisabled, onCommit, onCancel, o
193
198
  // (`value={value ?? ''}`)— 若 NumberCell 以 `defaultValue` 傳入,NumberInput value=undefined → ''
194
199
  // empty。對齊 cell-as-input「edit mode 自動帶入 display 值」(對齊 Notion / Airtable 共識),
195
200
  // 改用 local state controlled。User typing → setLocalValue;blur/Enter → onCommit(localValue)。
196
- const initial = typeof value === 'number' ? value : null
197
- const [localValue, setLocalValue] = React.useState<number | null>(initial)
201
+ // (initial + useState hoist display-return 見上方 React #310 fix。)
198
202
  return (
199
203
  <NumberInput
200
204
  autoFocus
@@ -650,13 +650,13 @@ function ConjunctionLabel({
650
650
  // index === 1:**唯一可改**的 AND/OR Select(連動整 group conjunction)
651
651
  // index ≥ 2:被連動的 row,read-only 顯示當前 conjunction 文字(同 Where 視覺,A6 canonical)
652
652
  // 對齊 Airtable / Notion / Linear 共識 @benchmark-unverified(non-OSS)
653
- // px-3 對齊 Field 內部 padding 12px(Q13)
653
+ // px-[var(--field-px)] 對齊 Field 內部 padding 12px(Q13)
654
654
  if (index === 0) {
655
- return <div className="w-20 shrink-0 text-body text-fg-muted px-3 self-center">Where</div>
655
+ return <div className="w-20 shrink-0 text-body text-fg-muted px-[var(--field-px)] self-center">Where</div>
656
656
  }
657
657
  if (index >= 2) {
658
658
  const label = conjunction === 'and' ? 'And' : 'Or'
659
- return <div className="w-20 shrink-0 text-body text-fg-muted px-3 self-center">{label}</div>
659
+ return <div className="w-20 shrink-0 text-body text-fg-muted px-[var(--field-px)] self-center">{label}</div>
660
660
  }
661
661
  // index === 1:可切換的 AND/OR Select
662
662
  // minRows={2} — And/Or 2 選項,顯式縮 menu 高度避免 reserve 3 row 空白(Q5)
@@ -131,7 +131,7 @@ export const Inspector: Story = {
131
131
  bordered: { control: 'boolean', description: '外框(嵌入已帶框容器時可設 false 避免雙重邊框)' },
132
132
  pinnedLeft: { control: 'boolean', description: 'Pin 產品名稱欄到左側,橫向捲動時保持可見' },
133
133
  pinnedRight: { control: 'boolean', description: 'Pin 上架日期欄到右側' },
134
- inlineEdit: { control: 'boolean', description: 'inline edit 視覺:cell 間加垂直分隔線 + select 欄顯示 chevron' },
134
+ inlineEdit: { control: 'boolean', description: 'inline edit 視覺:cell 間加垂直分隔線(dtCellGrid)。Select chevron column meta.editable 控制(showDisplayEndIcon),非本 prop' },
135
135
  height: {
136
136
  control: 'select',
137
137
  options: ['auto', '300px'],
@@ -7,7 +7,7 @@
7
7
  ───────────────────────────────────────────────────────── */
8
8
 
9
9
  :root {
10
- --table-cell-px: 0.75rem;
10
+ --table-cell-px: var(--field-px); /* 預設取自 --field-px(form/cell 同 12px content gutter SSOT);仍是 DataTable-scoped named token、可獨立 override(spec field-controls.spec.md:330 scoped 決策不變) */
11
11
  }
12
12
 
13
13
  [data-table-size="sm"] {
@@ -71,13 +71,13 @@ export const UsageGuidance: Story = {
71
71
  <p>適合 DataTable 的真實業務場景(點擊跳轉「展示」頁範例):</p>
72
72
  <ul className="space-y-1">
73
73
  <li>
74
- <LinkTo kind="Design System/Components/DataTable/展示" name="數字靠右對齊"><span className="text-primary hover:underline font-medium cursor-pointer">商品庫存後台 — 單價 / 金額數字靠右對齊</span></LinkTo>
74
+ <LinkTo kind="Design System/Components/DataTable/展示" name="數字靠右對齊"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">商品庫存後台 — 單價 / 金額數字靠右對齊</span></LinkTo>
75
75
  </li>
76
76
  <li>
77
- <LinkTo kind="Design System/Components/DataTable/展示" name="自動行高"><span className="text-primary hover:underline font-medium cursor-pointer">商品備註長短不一 — 自動行高撐開列高</span></LinkTo>
77
+ <LinkTo kind="Design System/Components/DataTable/展示" name="自動行高"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">商品備註長短不一 — 自動行高撐開列高</span></LinkTo>
78
78
  </li>
79
79
  <li>
80
- <LinkTo kind="Design System/Components/DataTable/展示" name="空狀態"><span className="text-primary hover:underline font-medium cursor-pointer">目錄尚無商品 — 空狀態引導新增</span></LinkTo>
80
+ <LinkTo kind="Design System/Components/DataTable/展示" name="空狀態"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">目錄尚無商品 — 空狀態引導新增</span></LinkTo>
81
81
  </li>
82
82
  </ul>
83
83
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見 <code>Vs*Rule</code> stories)。</p>