@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
@@ -345,6 +345,36 @@ export const P = () => (<div className="px-[var(--layout-space-loose)] border-b
345
345
  '
346
346
  expect_pass_silent "23. R9 skip FileViewer(isOverlay 家)→ 不檢查"
347
347
 
348
+ # 24. R10 link_canonical: text-primary hover:underline → P1 warn(非 block)
349
+ run_hook "PreToolUse" "Edit" "/foo/my-project/packages/design-system/src/components/Foo/foo.principles.stories.tsx" '
350
+ <span className="text-primary hover:underline font-medium cursor-pointer">查看詳情</span>
351
+ '
352
+ expect_warn "24. R10 text-primary hover:underline → P1 warn" "R10 link_canonical"
353
+
354
+ # 25. R10 canonical(hover:text-primary-hover 換色)→ silent
355
+ run_hook "PreToolUse" "Edit" "/foo/my-project/packages/design-system/src/components/Foo/foo.principles.stories.tsx" '
356
+ <span className="text-primary hover:text-primary-hover font-medium cursor-pointer">查看詳情</span>
357
+ '
358
+ expect_pass_silent "25. R10 canonical hover:text-primary-hover → silent"
359
+
360
+ # 26. R10 escape marker @link-style-allow → silent
361
+ run_hook "PreToolUse" "Edit" "/foo/my-project/packages/design-system/src/components/Foo/foo.principles.stories.tsx" '// @link-style-allow: legacy doc-nav underline
362
+ <span className="text-primary hover:underline">查看詳情</span>
363
+ '
364
+ expect_pass_silent "26. R10 @link-style-allow 豁免 → silent"
365
+
366
+ # 27. R10 broadened: class-separated(cursor-pointer 隔開)text-primary … hover:underline → warn
367
+ run_hook "PreToolUse" "Edit" "/foo/my-project/packages/design-system/src/components/Foo/foo.anatomy.stories.tsx" '
368
+ <button className="text-caption text-primary cursor-pointer hover:underline">重設</button>
369
+ '
370
+ expect_warn "27. R10 class-separated text-primary…hover:underline → warn" "R10 link_canonical"
371
+
372
+ # 28. R10 不誤判描述 label「text-primary · hover:underline」(· 非 class-char,如 LinkInput anatomy 註解)→ silent
373
+ run_hook "PreToolUse" "Edit" "/foo/my-project/packages/design-system/src/components/Foo/foo.anatomy.stories.tsx" '
374
+ <span className="text-fg-muted font-mono">text-primary · hover:underline · 點擊開啟連結</span>
375
+ '
376
+ expect_pass_silent "28. R10 描述 label(· 分隔)→ 不誤判"
377
+
348
378
  echo ""
349
379
  echo "=== Summary ==="
350
380
  echo "Passed: $PASS / $((PASS + FAIL))"
@@ -24,12 +24,26 @@ Badge 在不同 anchor 有兩種截然不同視覺 / 語意,prop 名區分:
24
24
  | `badge` | **Pill 內 inline badge** — label 右側 flex | Button(有 label)/ Tab item / Chip | inline count |
25
25
  | `overlayBadge` | **疊視覺重心** — absolute 於 icon/avatar 角 | iconOnly Button / pure Icon | top-right count overlay |
26
26
  | `badgeCount`(Avatar 專用) | count overlay,內部用 `<Badge variant="critical">`,貼 avatar 右上 | Avatar | overlay but Avatar 語意 |
27
- | `status`(Avatar 專用) | **非 Badge** — SVG presence dot,貼右下 | Avatar | presence indicator |
27
+ | `status`(presence dot — Avatar / ProfileCard / PeoplePicker) | **非 Badge** — SVG presence dot,貼右下 | Avatar | presence indicator |
28
28
 
29
29
  **禁止**:同 prop 名兼 inline + overlay。世界級 Material `BadgedBox`(overlay)vs `Chip.label`(inline)分開;Ant `<Badge overflowCount>` vs `<Tag>` 分開。
30
30
 
31
+ **`status` 兩個 value-set family(刻意 overload,非隱性衝突)**:(1) **presence** `'online' | 'away' | 'busy' | 'offline'` — Avatar / ProfileCard / PeoplePicker;(2) **lifecycle** `'uploading' | 'completed' | 'error'`(FileItem / FileUpload)/ `'inProgress' | 'success' | 'error'`(ProgressBar)。兩 family 各自 self-consistent,prop 名沿用同一 presence/lifecycle 軸。
32
+
31
33
  **禁止組合**:有 label 的 Button / Chip 疊 `overlayBadge`(badge 飄到 chrome 邊緣遠離 icon 語義) → 計數改用 `badge` inline。詳 `badge.spec.md`「Overlay 適用元件 canonical」。
32
34
 
35
+ ## `prefix` / `suffix` canonical(按「prop 型別」判,非按位置一刀切)
36
+
37
+ `prefix` / `suffix` 這對字在 DS 有 3 種身份,**只有當它是「放 ReactNode 的內容插槽 prop」時才禁**:
38
+
39
+ | 形態 | 判定 | 改用 / 對齊 |
40
+ |------|------|-----------|
41
+ | **ReactNode 內容插槽 prop**(`prefix?: ReactNode`)| ❌ **禁** — 位置名不傳達放什麼 | 內容描述名:`startIcon`/`endIcon`(icon)、`avatar`(ReactNode)、`endContent`/`endAction`(trailing slot)。對齊 Material `startIcon`/`endIcon`、Atlassian `iconBefore`/`iconAfter` |
42
+ | **值格式 affix 字串 prop**(`prefix?: string`)| ✅ **允許** — 黏在值上的貨幣/單位符號,屬格式化非插槽 | `NumberInput.prefix='$'`/`suffix='kg'`。對齊 Ant `InputNumber.prefix` |
43
+ | **結構 anatomy 詞彙 / 內部原語 / CSS 變數**(`ItemPrefix`/`ItemSuffix`、`--item-prefix-slot`、`data-prefix-type`、spec 的 `[prefix][content][suffix]` 骨架)| ✅ **允許** — 位置就是結構槽的身分(類比 CSS `::before`/`::after`),非 consumer 內容 prop | item-anatomy SSOT(`item-anatomy.spec.md`)|
44
+
45
+ **一句話判準**:**「是 ReactNode 內容插槽 consumer prop 嗎?是 → 禁(改內容名);否(格式字串 / 結構原語)→ 合法。」** `ui-development.md`「Props 命名」禁令的標的是**內容插槽**,非格式字串或結構詞彙——三者程式碼裡 0 衝突(全庫掃證實無任何元件暴露 ReactNode prefix/suffix 插槽 prop)。
46
+
33
47
  ## 常用 icon canonical
34
48
 
35
49
  | 語義 | Icon | 反例 |
@@ -74,12 +74,12 @@ paths:
74
74
  - slot 只接 icon → `startIcon` / `endIcon`(型別 `LucideIcon`,元件控尺寸)
75
75
  - slot 接任意視覺 → 描述內容類型(`avatar`,型別 `ReactNode`)
76
76
  - slot 是行為 → callback(`onDismiss`,元件渲染互動 + 樣式)
77
- - ❌ 禁 `prefix` / `suffix` / `left` / `right`(位置名不傳達本質)
77
+ - ❌ 禁 `prefix` / `suffix` / `left` / `right` 當**內容插槽** prop(位置名不傳達本質)→ 用 `startIcon`/`endContent`。**例外**:值格式 affix 字串(`NumberInput.prefix='$'`)+ 結構 anatomy 詞彙合法,判準見 `.claude/references/props-naming.md`「prefix/suffix canonical」
78
78
 
79
79
  **4 名關閉 / 移除 callback**(詳 `.claude/references/props-naming.md`):
80
80
  `onClose` / `onDismiss` / `onRemove` / `onClear` 各有語意不合併。
81
81
 
82
- **Badge 命名按放置**:`badge`(inline)/ `overlayBadge`(疊視覺重心 iconOnly)/ `badgeCount`(Avatar count)/ `status`(Avatar presence dot)。
82
+ **Badge 命名按放置**:`badge`(inline)/ `overlayBadge`(疊視覺重心 iconOnly)/ `badgeCount`(Avatar count)/ `status`(presence dot — Avatar / ProfileCard / PeoplePicker;另有 lifecycle value-set 用於 FileItem / FileUpload / ProgressBar,詳 `props-naming.md`)。
83
83
 
84
84
  **Icon canonical**:Overflow `MoreVertical` / Breadcrumb ellipsis `MoreHorizontal` / Close `X` / 成功 `Check` / 警告 `TriangleAlert` / 資訊 `Info`。
85
85
 
package/llms-full.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # @qijenchen/design-system — 完整設計參考(llms-full)
2
2
 
3
- > 全 component / pattern 的 variants / sizes / 禁止事項。build-time 從 spec.md frontmatter 生成,禁手改。v0.1.0-beta.74
3
+ > 全 component / pattern 的 variants / sizes / 禁止事項。build-time 從 spec.md frontmatter 生成,禁手改。v0.1.0-beta.76
4
4
 
5
5
  # Components
6
6
 
@@ -21,7 +21,7 @@ Source(AI 讀此看官方範例): src/components/Alert/
21
21
  - **warning**: 警告但非阻斷(方案即將到期、需更新付款方式);最高頻使用
22
22
  - **error**: 錯誤但非阻斷(系統錯誤但可重試、API 失敗摘要);搭配 aria-live=assertive
23
23
 
24
- ## app-shell(family composite)
24
+ ## AppShell(family composite)
25
25
 
26
26
  Storybook: https://ajenchen-design-system.netlify.app
27
27
  Source(AI 讀此看官方範例): src/components/AppShell/
@@ -52,6 +52,11 @@ Source(AI 讀此看官方範例): src/components/Badge/
52
52
  Storybook: https://ajenchen-design-system.netlify.app
53
53
  Source(AI 讀此看官方範例): src/components/Breadcrumb/
54
54
 
55
+ ### Sizes
56
+ - **sm**: Dialog / panel / drawer 內;text-body(14)+ icon 16,建議配 text-h4(20)title。對齊 BREADCRUMB_TEXT_CLASS.sm + BREADCRUMB_ICON_SIZE.sm(breadcrumb.tsx:124-136)
57
+ - **md**: 預設 — 一般頁面 header;text-body(14)+ icon 16,建議配 text-h3(24)title
58
+ - **lg**: Detail page hero / landing;text-body-lg(16)+ icon 20,建議配 text-h2(32)title
59
+
55
60
  ## BulkActionBar(family composite)
56
61
 
57
62
  Storybook: https://ajenchen-design-system.netlify.app
package/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # @qijenchen/design-system
2
2
 
3
3
  > World-class React design system(Radix/shadcn + Tailwind v4 + 自訂 design token)。
4
- > 54 components + 4 public patterns + design tokens。v0.1.0-beta.74
4
+ > 54 components + 4 public patterns + design tokens。v0.1.0-beta.76
5
5
 
6
6
  本檔由 source(spec.md frontmatter + Storybook index)build-time 自動生成,**禁手改**(CI --check drift gate 守)。
7
7
  每元件 / pattern 的完整 variants / sizes / 禁止事項 全文見 [llms-full.txt](./llms-full.txt)。
@@ -10,11 +10,11 @@
10
10
  ## Components
11
11
  - [Accordion](https://ajenchen-design-system.netlify.app): 見 Storybook / spec — src:components/Accordion
12
12
  - [Alert](https://ajenchen-design-system.netlify.app): variants:neutral/info/success/warning/error — src:components/Alert
13
- - [app-shell](https://ajenchen-design-system.netlify.app): 見 Storybook / spec — src:components/AppShell
13
+ - [AppShell](https://ajenchen-design-system.netlify.app): 見 Storybook / spec — src:components/AppShell
14
14
  - [AspectRatio](https://ajenchen-design-system.netlify.app): 見 Storybook / spec — src:components/AspectRatio
15
15
  - [Avatar](https://ajenchen-design-system.netlify.app): 見 Storybook / spec — src:components/Avatar
16
16
  - [Badge](https://ajenchen-design-system.netlify.app): variants:critical/high/medium/low — src:components/Badge
17
- - [Breadcrumb](https://ajenchen-design-system.netlify.app): 見 Storybook / spec — src:components/Breadcrumb
17
+ - [Breadcrumb](https://ajenchen-design-system.netlify.app): sizes:sm/md/lg — src:components/Breadcrumb
18
18
  - [BulkActionBar](https://ajenchen-design-system.netlify.app): 見 Storybook / spec — src:components/BulkActionBar
19
19
  - [Button](https://ajenchen-design-system.netlify.app): variants:primary/secondary/tertiary/text/link;sizes:xs/sm/md/lg — src:components/Button
20
20
  - [Calendar](https://ajenchen-design-system.netlify.app): 見 Storybook / spec — src:components/Calendar
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qijenchen/design-system",
3
- "version": "0.1.0-beta.74",
3
+ "version": "0.1.0-beta.76",
4
4
  "private": false,
5
5
  "description": "World-class design system — components, patterns, tokens, hooks (single source of truth for team distribution).",
6
6
  "type": "module",
@@ -69,13 +69,13 @@ export const UsageGuidance: Story = {
69
69
  >
70
70
  <ul className="space-y-1">
71
71
  <li>
72
- <LinkTo kind="Design System/Components/Accordion/展示" name="FAQ 常見問題"><span className="text-primary hover:underline font-medium cursor-pointer">FAQ 常見問題</span></LinkTo>
72
+ <LinkTo kind="Design System/Components/Accordion/展示" name="FAQ 常見問題"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">FAQ 常見問題</span></LinkTo>
73
73
  </li>
74
74
  <li>
75
- <LinkTo kind="Design System/Components/Accordion/展示" name="設定分組"><span className="text-primary hover:underline font-medium cursor-pointer">設定分組</span></LinkTo>
75
+ <LinkTo kind="Design System/Components/Accordion/展示" name="設定分組"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">設定分組</span></LinkTo>
76
76
  </li>
77
77
  <li>
78
- <LinkTo kind="Design System/Components/Accordion/展示" name="進階選項可隱藏"><span className="text-primary hover:underline font-medium cursor-pointer">進階選項可隱藏</span></LinkTo>
78
+ <LinkTo kind="Design System/Components/Accordion/展示" name="進階選項可隱藏"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">進階選項可隱藏</span></LinkTo>
79
79
  </li>
80
80
  </ul>
81
81
  </Rule>
@@ -128,10 +128,10 @@ export const ColorMatrix: Story = {
128
128
  <thead><tr><Th>Variant</Th><Th>Subtle</Th><Th>Solid</Th></tr></thead>
129
129
  <tbody>
130
130
  <tr><Td mono>neutral</Td><Td mono>bg-muted + border-border;無 status icon(VARIANT_ICON.neutral = null)</Td><Td>bg-surface-raised + data-theme inverse(跟頁面反)</Td></tr>
131
- <tr><Td mono>info</Td><Td mono>bg-info-subtle + border-info-hover + text-info-text</Td><Td mono>bg-info + data-theme="dark"(藍底白字)</Td></tr>
132
- <tr><Td mono>success</Td><Td mono>bg-success-subtle + border-success-hover + text-success-text</Td><Td mono>bg-success + data-theme="dark"(綠底白字)</Td></tr>
133
- <tr><Td mono>warning</Td><Td mono>bg-warning-subtle + border-warning-hover + text-warning-text</Td><Td mono>bg-warning + data-theme="light"(黃底深字)</Td></tr>
134
- <tr><Td mono>error</Td><Td mono>bg-error-subtle + border-error-hover + text-error-text</Td><Td mono>bg-error + data-theme="dark"(橘底白字)</Td></tr>
131
+ <tr><Td mono>info</Td><Td mono>bg-info-subtle + border-[var(--info-text)] + text-info-text</Td><Td mono>bg-info + data-theme="dark"(藍底白字)</Td></tr>
132
+ <tr><Td mono>success</Td><Td mono>bg-success-subtle + border-[var(--success-text)] + text-success-text</Td><Td mono>bg-success + data-theme="dark"(綠底白字)</Td></tr>
133
+ <tr><Td mono>warning</Td><Td mono>bg-warning-subtle + border-[var(--warning-text)] + text-warning-text</Td><Td mono>bg-warning + data-theme="light"(黃底深字)</Td></tr>
134
+ <tr><Td mono>error</Td><Td mono>bg-error-subtle + border-[var(--error-text)] + text-error-text</Td><Td mono>bg-error + data-theme="dark"(橘底白字)</Td></tr>
135
135
  </tbody>
136
136
  </table>
137
137
  </div>
@@ -41,19 +41,19 @@ export const UsageGuidance: Story = {
41
41
  <p>適合 Alert 的真實業務場景(點擊跳轉「展示」頁範例):</p>
42
42
  <ul className="space-y-1">
43
43
  <li>
44
- <LinkTo kind="Design System/Components/Alert/展示" name="低調單行"><span className="text-primary hover:underline font-medium cursor-pointer">低調單行</span></LinkTo>
44
+ <LinkTo kind="Design System/Components/Alert/展示" name="低調單行"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">低調單行</span></LinkTo>
45
45
  </li>
46
46
  <li>
47
- <LinkTo kind="Design System/Components/Alert/展示" name="實心單行"><span className="text-primary hover:underline font-medium cursor-pointer">實心單行</span></LinkTo>
47
+ <LinkTo kind="Design System/Components/Alert/展示" name="實心單行"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">實心單行</span></LinkTo>
48
48
  </li>
49
49
  <li>
50
- <LinkTo kind="Design System/Components/Alert/展示" name="低調含說明文字"><span className="text-primary hover:underline font-medium cursor-pointer">低調含說明文字</span></LinkTo>
50
+ <LinkTo kind="Design System/Components/Alert/展示" name="低調含說明文字"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">低調含說明文字</span></LinkTo>
51
51
  </li>
52
52
  <li>
53
- <LinkTo kind="Design System/Components/Alert/展示" name="實心含說明文字"><span className="text-primary hover:underline font-medium cursor-pointer">實心含說明文字</span></LinkTo>
53
+ <LinkTo kind="Design System/Components/Alert/展示" name="實心含說明文字"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">實心含說明文字</span></LinkTo>
54
54
  </li>
55
55
  <li>
56
- <LinkTo kind="Design System/Components/Alert/展示" name="右上角操作群組"><span className="text-primary hover:underline font-medium cursor-pointer">右上角操作群組</span></LinkTo>
56
+ <LinkTo kind="Design System/Components/Alert/展示" name="右上角操作群組"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">右上角操作群組</span></LinkTo>
57
57
  </li>
58
58
  </ul>
59
59
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見 <code>Vs*Rule</code> stories)。</p>
@@ -18,15 +18,15 @@ export const UsageGuidance: Story = {
18
18
  <ul className="text-body space-y-2">
19
19
  <li>
20
20
  • 多頁 web service 的主結構——Linear / Notion / Slack / GitHub / Asana 這類「左側導覽 + 中央工作區」產品。完整組合見{' '}
21
- <LinkTo kind="Design System/Components/AppShell/展示" name="主側欄佈局 — Linear 式議題追蹤"><span className="text-primary hover:underline font-medium cursor-pointer">展示 → 主側欄佈局 — Linear 式議題追蹤</span></LinkTo>
21
+ <LinkTo kind="Design System/Components/AppShell/展示" name="主側欄佈局 — Linear 式議題追蹤"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">展示 → 主側欄佈局 — Linear 式議題追蹤</span></LinkTo>
22
22
  </li>
23
23
  <li>
24
24
  • 需要 sidebar + main 持續共存——在議題列表、看板、報表等頁面間切換時,左側導覽不重渲染、捲動位置不丟失。見{' '}
25
- <LinkTo kind="Design System/Components/AppShell/展示" name="主側欄佈局 + 頁面分頁"><span className="text-primary hover:underline font-medium cursor-pointer">展示 → 主側欄佈局 + 頁面分頁</span></LinkTo>
25
+ <LinkTo kind="Design System/Components/AppShell/展示" name="主側欄佈局 + 頁面分頁"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">展示 → 主側欄佈局 + 頁面分頁</span></LinkTo>
26
26
  </li>
27
27
  <li>
28
28
  • 需要右側詳情面板(議題詳情 / inspector / 成員資料)跟 main 並存——如 Linear 點選議題後右側展開詳情。開合行為見{' '}
29
- <LinkTo kind="Design System/Components/AppShell/設計規格" name="右側面板開合行為(兩種模式)"><span className="text-primary hover:underline font-medium cursor-pointer">設計規格 → 右側面板開合行為</span></LinkTo>
29
+ <LinkTo kind="Design System/Components/AppShell/設計規格" name="右側面板開合行為(兩種模式)"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">設計規格 → 右側面板開合行為</span></LinkTo>
30
30
  </li>
31
31
  </ul>
32
32
  </section>
@@ -46,7 +46,7 @@ export const UsageGuidance: Story = {
46
46
  <p className="text-body">
47
47
  兩種佈局模式(primary-sidebar / primary-header)的選型決策樹獨立成「佈局模式怎麼選」story(含
48
48
  WorkspaceBrand 放置規則),見本頁側欄或{' '}
49
- <LinkTo kind="Design System/Components/AppShell/設計原則" name="佈局模式怎麼選"><span className="text-primary hover:underline font-medium cursor-pointer">設計原則 → 佈局模式怎麼選</span></LinkTo>
49
+ <LinkTo kind="Design System/Components/AppShell/設計原則" name="佈局模式怎麼選"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">設計原則 → 佈局模式怎麼選</span></LinkTo>
50
50
  </p>
51
51
  </section>
52
52
 
@@ -98,12 +98,12 @@ export const LayoutModeRule: Story = {
98
98
  服務整個 app(account / workspace switcher / 跨頁 search / notifications);local toolbar
99
99
  <em>仍在</em> main col 頂,只是上面多了 global header。WorkspaceBrand 改放 globalHeader 左側。
100
100
  參考 GitHub / Slack / Gmail。完整組合見{' '}
101
- <LinkTo kind="Design System/Components/AppShell/展示" name="主標頭佈局 — 全域+本地兩層(GitHub/Gmail/Slack 派)"><span className="text-primary hover:underline font-medium cursor-pointer">展示 → 主標頭佈局 — 全域+本地兩層</span></LinkTo>
101
+ <LinkTo kind="Design System/Components/AppShell/展示" name="主標頭佈局 — 全域+本地兩層(GitHub/Gmail/Slack 派)"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">展示 → 主標頭佈局 — 全域+本地兩層</span></LinkTo>
102
102
  </li>
103
103
  </ul>
104
104
  <p className="text-caption text-fg-secondary mt-2">
105
105
  兩 mode 是 product 角色表態——啟動時固定,不該在 runtime 切換。視覺對照圖見{' '}
106
- <LinkTo kind="Design System/Components/AppShell/設計規格" name="兩種布局模式對照圖"><span className="text-primary hover:underline font-medium cursor-pointer">設計規格 → 兩種布局模式對照圖</span></LinkTo>
106
+ <LinkTo kind="Design System/Components/AppShell/設計規格" name="兩種布局模式對照圖"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">設計規格 → 兩種布局模式對照圖</span></LinkTo>
107
107
  </p>
108
108
  </section>
109
109
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- pattern: app-shell
2
+ component: AppShell
3
3
  family: composite # 組合 sidebar+header+main+aside,非 Family 1-4 row element
4
4
  scope: web service page-level layout primitive (sidebar + header + main + aside composition)
5
5
  benchmark:
@@ -58,7 +58,7 @@ benchmark:
58
58
  asideOpen={open} onAsideOpenChange={setOpen} // ⌘. / Ctrl+. toggle (本 AppShell own)
59
59
  sidebar={<Sidebar>...</Sidebar>} // SSOT in components/Sidebar/sidebar.spec.md
60
60
  header={<ChromeHeader>...</ChromeHeader>} // SSOT in patterns/header-canonical/header-canonical.spec.md
61
- aside={<AppShellAside title="Detail">...</AppShellAside>} // 本 pattern own;modal mode 必 title(Sheet a11y)
61
+ aside={<AppShellAside title="Detail">...</AppShellAside>} // 本 composite own;modal mode 必 title(Sheet a11y)
62
62
  >
63
63
  {children} {/* <main> landmark, padding=0,內容遵循 layoutSpace 6 條規則 */}
64
64
  </AppShell>
@@ -288,7 +288,7 @@ Main 內塞什麼(table / field / card / page header / list)的 layout + spacing
288
288
 
289
289
  ## Future extension(目前不定義)
290
290
 
291
- **Multi-sidebar**(Notion / Linear 雙側欄派):**尚未實作**。目前 `sidebar` prop 僅 `React.ReactNode`(單一 slot,無 array signature、無取首位邏輯、無 dev warning)。未來若支援會擴充為 array,SSOT 放 `sidebar.spec.md`,不在本 pattern(per user 2026-05-19「AppShell 不該 customize Sidebar」)。
291
+ **Multi-sidebar**(Notion / Linear 雙側欄派):**尚未實作**。目前 `sidebar` prop 僅 `React.ReactNode`(單一 slot,無 array signature、無取首位邏輯、無 dev warning)。未來若支援會擴充為 array,SSOT 放 `sidebar.spec.md`,不在本 composite(per user 2026-05-19「AppShell 不該 customize Sidebar」)。
292
292
 
293
293
  **Footer**:不 expose slot(per user 2026-05-19「不用 footer」)。Web service 通常不用 footer,consumer 若需要自貼 Main 底。
294
294
 
@@ -342,7 +342,7 @@ Main 內塞什麼(table / field / card / page header / list)的 layout + spacing
342
342
 
343
343
  ## 相關 spec(per codex Layer B 比稿修正 path/case)
344
344
 
345
- - `components/Sidebar/sidebar.spec.md` — sidebar 視覺 + 鍵盤 + mobile + SidebarTrigger Pattern A/B SSOT(本 pattern 必消費)
345
+ - `components/Sidebar/sidebar.spec.md` — sidebar 視覺 + 鍵盤 + mobile + SidebarTrigger Pattern A/B SSOT(本 composite 必消費)
346
346
  - `patterns/header-canonical/header-canonical.spec.md` — header 視覺契約 SSOT
347
347
  - `patterns/overlay-surface/overlay-surface.spec.md` — Aside modal mode 上層 SSOT
348
348
  - `components/Sheet/sheet.spec.md` — modal fallback SSOT(`aria-labelledby` 強制 + `z-50`)
@@ -2,14 +2,14 @@
2
2
  /**
3
3
  * AppShell — web service page-level layout primitive。
4
4
  *
5
- * 組合 Sidebar + ChromeHeader + Aside + main 成完整 page shell。SSOT 邊界:本 pattern only
5
+ * 組合 Sidebar + ChromeHeader + Aside + main 成完整 page shell。SSOT 邊界:本 composite component only
6
6
  * own slot composition + layout mode + Aside responsive mode;不 own sidebar / header /
7
7
  * sheet 視覺(各自 spec own)。
8
8
  *
9
9
  * 對齊 Mantine AppShell compound API + Ant Layout slot 模式 + Material 3 standard/modal
10
10
  * drawer canonical(per spec.md frontmatter cite)。
11
11
  *
12
- * Spec SSOT:`patterns/app-shell/app-shell.spec.md`
12
+ * Spec SSOT:`components/AppShell/app-shell.spec.md`
13
13
  */
14
14
 
15
15
  import * as React from 'react'
@@ -197,7 +197,7 @@ export const RatioChoice: Story = {
197
197
  <p className="text-caption text-fg-muted max-w-[720px] leading-relaxed mb-8">
198
198
  先看內容方向(橫 / 方 / 直),再對到下方的判斷準則即可選定。五個 ratio 的並排視覺對照見{' '}
199
199
  <LinkTo kind="Design System/Components/AspectRatio/設計規格" name="標準 比例 視覺對照">
200
- <span className="text-primary hover:underline font-medium cursor-pointer">設計規格 → 標準 比例 視覺對照</span>
200
+ <span className="text-primary hover:text-primary-hover font-medium cursor-pointer">設計規格 → 標準 比例 視覺對照</span>
201
201
  </LinkTo>
202
202
  ,此處只講「怎麼選」的決策邏輯,不重畫圖。
203
203
  </p>
@@ -47,9 +47,9 @@ export const UsageGuidance: Story = {
47
47
  <div className="prose prose-sm max-w-prose mb-8">
48
48
  <p>Avatar 代表「誰」——人、團隊、組織、專案的視覺身份。適合的真實業務場景(點擊跳轉「展示」頁範例):</p>
49
49
  <ul className="space-y-1">
50
- <li>留言者、指派者、團隊成員列表的人員識別;workspace / 組織 / App 的身份標識 —— 見 <LinkTo kind="Design System/Components/Avatar/展示" name="四模式"><span className="text-primary hover:underline font-medium cursor-pointer">四模式</span></LinkTo></li>
51
- <li>成員沒上傳照片、或頭像圖片載入失敗時,以名字首字 + 色彩維持可辨識 —— 見 <LinkTo kind="Design System/Components/Avatar/展示" name="備援顯示"><span className="text-primary hover:underline font-medium cursor-pointer">備援顯示</span></LinkTo></li>
52
- <li>通訊錄、成員選單、chat 列表等列表項目的主視覺 prefix —— 見 <LinkTo kind="Design System/Components/Avatar/展示" name="情境用例"><span className="text-primary hover:underline font-medium cursor-pointer">情境用例</span></LinkTo></li>
50
+ <li>留言者、指派者、團隊成員列表的人員識別;workspace / 組織 / App 的身份標識 —— 見 <LinkTo kind="Design System/Components/Avatar/展示" name="四模式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">四模式</span></LinkTo></li>
51
+ <li>成員沒上傳照片、或頭像圖片載入失敗時,以名字首字 + 色彩維持可辨識 —— 見 <LinkTo kind="Design System/Components/Avatar/展示" name="備援顯示"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">備援顯示</span></LinkTo></li>
52
+ <li>通訊錄、成員選單、chat 列表等列表項目的主視覺 prefix —— 見 <LinkTo kind="Design System/Components/Avatar/展示" name="情境用例"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">情境用例</span></LinkTo></li>
53
53
  </ul>
54
54
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方 vs 近親 段)。</p>
55
55
  </div>
@@ -277,7 +277,7 @@ const AvatarInner = React.forwardRef<HTMLDivElement, AvatarProps>(
277
277
  style={{
278
278
  boxShadow: `0 0 0 2px var(--surface-raised, var(--canvas))`,
279
279
  }}
280
- aria-label={`${badgeCount} unread`}
280
+ aria-label={`${badgeCount} unread`} // i18n-allow: DS default(未覆寫英文 'N unread')
281
281
  />
282
282
  )}
283
283
  </div>
@@ -45,13 +45,13 @@ export const UsageGuidance: Story = {
45
45
  <p>適合 Badge 的真實業務場景(點擊跳轉「展示」頁範例):</p>
46
46
  <ul className="space-y-1">
47
47
  <li>
48
- <LinkTo kind="Design System/Components/Badge/展示" name="正圓 vs 膠囊"><span className="text-primary hover:underline font-medium cursor-pointer">正圓 vs 膠囊</span></LinkTo>
48
+ <LinkTo kind="Design System/Components/Badge/展示" name="正圓 vs 膠囊"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">正圓 vs 膠囊</span></LinkTo>
49
49
  </li>
50
50
  <li>
51
- <LinkTo kind="Design System/Components/Badge/展示" name="圓點模式"><span className="text-primary hover:underline font-medium cursor-pointer">Dot 模式</span></LinkTo>
51
+ <LinkTo kind="Design System/Components/Badge/展示" name="圓點模式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Dot 模式</span></LinkTo>
52
52
  </li>
53
53
  <li>
54
- <LinkTo kind="Design System/Components/Badge/展示" name="數量上限"><span className="text-primary hover:underline font-medium cursor-pointer">Max 上限</span></LinkTo>
54
+ <LinkTo kind="Design System/Components/Badge/展示" name="數量上限"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Max 上限</span></LinkTo>
55
55
  </li>
56
56
  </ul>
57
57
  <p className="text-fg-muted mt-3">判斷不確定時:先確認使用者真正需要的是「有沒有新東西」(用 Dot)還是「有多少」(用數字)。若兩者都不貼切,代表這裡可能不該放 Badge——改用文字標籤或其他狀態指示。</p>
@@ -65,15 +65,15 @@ export const UsageGuidance: Story = {
65
65
  <ul className="space-y-1">
66
66
  <li>
67
67
  電商多層分類、檔案管理器等深層頁面,路徑過長時中段折疊、點 ⋯ 可展開中間層 —— 見{' '}
68
- <LinkTo kind="Design System/Components/Breadcrumb/展示" name="可互動省略"><span className="text-primary hover:underline font-medium cursor-pointer">可互動省略</span></LinkTo>
68
+ <LinkTo kind="Design System/Components/Breadcrumb/展示" name="可互動省略"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">可互動省略</span></LinkTo>
69
69
  </li>
70
70
  <li>
71
71
  路徑由路由 / CMS 資料動態產生,直接傳資料陣列、超過層數自動收合中段 —— 見{' '}
72
- <LinkTo kind="Design System/Components/Breadcrumb/展示" name="宣告式 API + 自動收合"><span className="text-primary hover:underline font-medium cursor-pointer">宣告式 API + 自動收合</span></LinkTo>
72
+ <LinkTo kind="Design System/Components/Breadcrumb/展示" name="宣告式 API + 自動收合"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">宣告式 API + 自動收合</span></LinkTo>
73
73
  </li>
74
74
  <li>
75
75
  SPA 內點麵包屑要走前端路由、不整頁重載 —— 見{' '}
76
- <LinkTo kind="Design System/Components/Breadcrumb/展示" name="整合 React Router / Next.js Link"><span className="text-primary hover:underline font-medium cursor-pointer">整合 React Router / Next.js Link</span></LinkTo>
76
+ <LinkTo kind="Design System/Components/Breadcrumb/展示" name="整合 React Router / Next.js Link"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">整合 React Router / Next.js Link</span></LinkTo>
77
77
  </li>
78
78
  </ul>
79
79
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用相近的其他元件(見下方「跟相近元件怎麼分」的範例)。</p>
@@ -2,9 +2,19 @@
2
2
  component: Breadcrumb
3
3
  family: composite
4
4
  variants: {}
5
- sizes: {}
5
+ sizes:
6
+ sm:
7
+ when: "Dialog / panel / drawer 內;text-body(14)+ icon 16,建議配 text-h4(20)title。對齊 BREADCRUMB_TEXT_CLASS.sm + BREADCRUMB_ICON_SIZE.sm(breadcrumb.tsx:124-136)"
8
+ world-class: ["Atlassian Breadcrumb", "Ant Design Breadcrumb"]
9
+ md:
10
+ when: "預設 — 一般頁面 header;text-body(14)+ icon 16,建議配 text-h3(24)title"
11
+ world-class: ["Ant Design Breadcrumb default", "MUI Breadcrumbs", "Polaris Breadcrumbs"]
12
+ lg:
13
+ when: "Detail page hero / landing;text-body-lg(16)+ icon 20,建議配 text-h2(32)title"
14
+ world-class: ["Carbon Breadcrumb", "Material 3 breadcrumb"]
6
15
  traits:
7
16
  - hasInteractiveStates
17
+ - hasSizes
8
18
  - isStructural
9
19
  benchmark:
10
20
  - Ant Design Breadcrumb: github.com/ant-design/ant-design/tree/master/components/breadcrumb
@@ -76,7 +76,7 @@ export const Overview: Story = {
76
76
  title={
77
77
  <>
78
78
  已選取本頁全部 50 個。{' '}
79
- <button type="button" className="text-primary hover:underline">
79
+ <button type="button" className="text-primary hover:text-primary-hover">
80
80
  點此選取全部 5370 個項目
81
81
  </button>
82
82
  </>
@@ -40,8 +40,8 @@ export const UsageGuidance: Story = {
40
40
  <div className="prose prose-sm max-w-prose">
41
41
  <p>BulkActionBar 是「選取狀態驅動」的批次操作列,跟 selection state 生命週期綁定。真實業務場景:</p>
42
42
  <ul className="space-y-1">
43
- <li><LinkTo kind="Design System/Components/BulkActionBar/展示" name="基本"><span className="text-primary hover:underline cursor-pointer">基本</span></LinkTo>(收件匣多選郵件後,一次封存 / 加標籤 / 刪除)</li>
44
- <li><LinkTo kind="Design System/Components/BulkActionBar/展示" name="提示擴選整個資料集"><span className="text-primary hover:underline cursor-pointer">提示擴選整個資料集</span></LinkTo>(資料集共 5370 筆、本頁只顯示 50 筆:本頁全選後浮出提示,可一鍵把選取範圍擴大到全部 5370 筆)</li>
43
+ <li><LinkTo kind="Design System/Components/BulkActionBar/展示" name="基本"><span className="text-primary hover:text-primary-hover cursor-pointer">基本</span></LinkTo>(收件匣多選郵件後,一次封存 / 加標籤 / 刪除)</li>
44
+ <li><LinkTo kind="Design System/Components/BulkActionBar/展示" name="提示擴選整個資料集"><span className="text-primary hover:text-primary-hover cursor-pointer">提示擴選整個資料集</span></LinkTo>(資料集共 5370 筆、本頁只顯示 50 筆:本頁全選後浮出提示,可一鍵把選取範圍擴大到全部 5370 筆)</li>
45
45
  </ul>
46
46
  <p className="text-fg-muted mt-3">判斷不確定:對照 spec.md「何時用 / 何時不用」段。</p>
47
47
  </div>
@@ -201,7 +201,7 @@ export const HintBannerRule: Story = {
201
201
  title={
202
202
  <>
203
203
  已選取本頁全部 50 個。{' '}
204
- <button type="button" className="text-primary hover:underline">
204
+ <button type="button" className="text-primary hover:text-primary-hover">
205
205
  點此選取全部 5370 個項目
206
206
  </button>
207
207
  </>
@@ -76,7 +76,7 @@ benchmark:
76
76
  - `gap-2`(8px)+ `<ButtonDivider />`(自帶 mx-1 = 12px 視覺距離)
77
77
  - `px-[var(--layout-space-loose)] py-[var(--layout-space-tight)]`
78
78
  - 自然高度 56md / 68lg(md Button `--field-height-md` 32/36 + `py-[var(--layout-space-tight)]` 12/16 ×2;對齊 SurfaceFooter / DataTable toolbar canonical)
79
- - `selection.length === 0` → 回 null 不佔 layout
79
+ - `selection.length === 0` **且** `totalSelected` 為 0/未設 → 回 null 不佔 layout(反向選取 all 模式 `totalSelected > 0` 但可見列全 excluded 時仍顯示,見「Extend dataset pattern」)
80
80
  - **寬度邊界**:actions 為單行 flex 排列(`gap-2`),無折行、無內建 overflow 收納;寬度受限場景由 consumer 控制 action 數量
81
81
 
82
82
  ### Slot
@@ -170,6 +170,8 @@ interface BulkActionBarLabels {
170
170
 
171
171
  「本頁全選 → hint 點擊 → 擴選整個 dataset」2-step 後,consumer 把 `totalSelected` 設為 dataset 真總數,count 區改顯示該值(否則 fallback `selection.length`)——避免 Alert 顯「已選 5370」但 bar 仍顯「已選 50」的不同步(2026-05-13 ship)。對齊 Gmail / Linear / Notion 全選 dataset hint pattern。
172
172
 
173
+ **可見性與反向選取(DataTable all 模式,2026-06-22)**:顯示判準 = `selection.length > 0 || (totalSelected ?? 0) > 0`。反向選取(`{ mode:'all', excluded }`)下若可見列全被 excluded、`selection`(可見代表)為空但 `totalSelected > 0`(全集仍有選取),bar **仍顯示**(否則「已選 N 個但 bar 消失」矛盾)。對應 `data-table.spec.md`「L2 選取」inverted 模型。
174
+
173
175
  ---
174
176
 
175
177
  ## a11y 預設
@@ -185,7 +187,7 @@ interface BulkActionBarLabels {
185
187
 
186
188
  ## 視覺與動畫
187
189
 
188
- - **出現 / 消失**:`selection.length` 0→>0 直接 mount;>0→0 null 直接 unmount(無 fade 動畫)。inline composition 下自然 reflow;consumer 需固定高度時自擺 placeholder(見「禁止事項」)
190
+ - **出現 / 消失**:有選取(`selection.length > 0` **或** `totalSelected > 0`)直接 mount;歸零回 null 直接 unmount(無 fade 動畫)。inline composition 下自然 reflow;consumer 需固定高度時自擺 placeholder(見「禁止事項」)
189
191
  - **底色**:**無底色 contrast**,跟 page 同色(`bg-canvas` / `bg-surface` 視 placement 繼承)。對齊 Notion / Linear minimalist — 用文字內容切換呈現「mode」,**不**用底色 highlight。**不像 Polaris 那種顯著底色變化** <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
190
192
  - **邊界**:**無外框邊界**(融入 page chrome)— 恆 **`border-top` border-divider 切割 layout**(bar 是 page 結構,不是 floating overlay,不用 box-shadow 製造「浮層」誤導)。top-toolbar 變體為未來項(見「Size canonical」)
191
193
  - **與 table 的關係**:inline composition — bar 接在 DataTable 下方,toolbar 永遠保留(見「Placement」)
@@ -83,7 +83,7 @@ export const WithExtendDatasetHint: Story = {
83
83
  <button
84
84
  type="button"
85
85
  onClick={() => { setSelection([]); setAllSelected(false) }}
86
- className="text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
86
+ className="text-primary hover:text-primary-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
87
87
  >
88
88
  清除選取項目
89
89
  </button>
@@ -94,7 +94,7 @@ export const WithExtendDatasetHint: Story = {
94
94
  <button
95
95
  type="button"
96
96
  onClick={() => setAllSelected(true)}
97
- className="text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
97
+ className="text-primary hover:text-primary-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
98
98
  >
99
99
  點此選取全部 {TOTAL} 個項目
100
100
  </button>
@@ -82,8 +82,9 @@ const BulkActionBar = React.forwardRef<HTMLDivElement, BulkActionBarProps>(
82
82
  [labelsOverride]
83
83
  )
84
84
 
85
- // selection.length === 0 自動藏(對齊 spec 禁止事項 #3)
86
- if (selection.length === 0) return null
85
+ // selection.length === 0 自動藏(對齊 spec 禁止事項 #3)
86
+ // 反向選取(DataTable all 模式)例外:visible 全被 excluded 但 totalSelected>0 仍有選取 → 顯示(2026-06-22)
87
+ if (selection.length === 0 && (totalSelected ?? 0) === 0) return null
87
88
 
88
89
  return (
89
90
  <div
@@ -51,13 +51,13 @@ export const UsageGuidance: Story = {
51
51
  <p>適合 Button 的真實業務場景(點擊跳轉「展示」頁範例):</p>
52
52
  <ul className="space-y-1">
53
53
  <li>
54
- <LinkTo kind="Design System/Components/Button/展示" name="危險 語意"><span className="text-primary hover:underline font-medium cursor-pointer">Danger 語意</span></LinkTo>
54
+ <LinkTo kind="Design System/Components/Button/展示" name="危險 語意"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Danger 語意</span></LinkTo>
55
55
  </li>
56
56
  <li>
57
- <LinkTo kind="Design System/Components/Button/展示" name="滑鼠移過 / 鍵盤聚焦"><span className="text-primary hover:underline font-medium cursor-pointer">滑鼠移過 / 鍵盤聚焦</span></LinkTo>
57
+ <LinkTo kind="Design System/Components/Button/展示" name="滑鼠移過 / 鍵盤聚焦"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">滑鼠移過 / 鍵盤聚焦</span></LinkTo>
58
58
  </li>
59
59
  <li>
60
- <LinkTo kind="Design System/Components/Button/展示" name="純圖示加 Tooltip"><span className="text-primary hover:underline font-medium cursor-pointer">純圖示加 Tooltip</span></LinkTo>
60
+ <LinkTo kind="Design System/Components/Button/展示" name="純圖示加 Tooltip"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">純圖示加 Tooltip</span></LinkTo>
61
61
  </li>
62
62
  </ul>
63
63
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見 <code>Vs*Rule</code> stories)。</p>
@@ -313,16 +313,6 @@ export interface ButtonProps
313
313
  fullWidth?: boolean
314
314
  }
315
315
 
316
- /**
317
- * Icon-only padding — calc `(field-height - icon-size) / 2` per size。
318
- *
319
- * 設計:startIcon 到左邊距離 = padding = `(height - icon) / 2`。
320
- * 純 icon-only 時 width = 2*padding + icon = height → **自然正方形**,不需要 aspect-square。
321
- * 有 suffix(badge / endIcon)時 width = 2*pad + icon + gap + suffix > height → **自然長方形**。
322
- * StartIcon 到左邊距離始終不變,形狀自動適應內容。
323
- *
324
- * 用 CSS var 讓 density 切換時 padding 自動跟著算(field-height 會變)。
325
- */
326
316
  // IconOnly 用 padding-free + aspect-square + flex-center 的 Polaris/Atlassian idiom
327
317
  // (M17 SSOT 必可傳播 — 取代 4 個 size 的 magic-number 公式):
328
318
  // - aspect-square 鎖 width=height(來自 h-field-X)
@@ -124,7 +124,7 @@ export const ColorMatrix: Story = {
124
124
  <tr>
125
125
  <Td>Today cell(日期數字)</Td>
126
126
  <Td mono>bg-info · text-on-emphasis · rounded-full · min-w-6 h-6 px-2 · text-body font-medium</Td>
127
- <Td>info-filled pill,固定 h-6 + min-w-6 做圓形 badge(對齊 Google Calendar today pill)</Td>
127
+ <Td>info-filled pill,固定 h-6 + min-w-6 pill badge(對齊 Google Calendar today pill)</Td>
128
128
  </tr>
129
129
  <tr>
130
130
  <Td>Outside day cell</Td>
@@ -41,13 +41,13 @@ export const UsageGuidance: Story = {
41
41
  <p>適合 Calendar 的真實業務場景(點擊跳轉「展示」頁範例):</p>
42
42
  <ul className="space-y-1">
43
43
  <li>
44
- <LinkTo kind="Design System/Components/Calendar/展示" name="團隊行事曆"><span className="text-primary hover:underline font-medium cursor-pointer">團隊行事曆</span></LinkTo>
44
+ <LinkTo kind="Design System/Components/Calendar/展示" name="團隊行事曆"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">團隊行事曆</span></LinkTo>
45
45
  </li>
46
46
  <li>
47
- <LinkTo kind="Design System/Components/Calendar/展示" name="內容發佈月曆"><span className="text-primary hover:underline font-medium cursor-pointer">內容發佈月曆</span></LinkTo>
47
+ <LinkTo kind="Design System/Components/Calendar/展示" name="內容發佈月曆"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">內容發佈月曆</span></LinkTo>
48
48
  </li>
49
49
  <li>
50
- <LinkTo kind="Design System/Components/Calendar/展示" name="空行事曆"><span className="text-primary hover:underline font-medium cursor-pointer">空行事曆</span></LinkTo>
50
+ <LinkTo kind="Design System/Components/Calendar/展示" name="空行事曆"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">空行事曆</span></LinkTo>
51
51
  </li>
52
52
  </ul>
53
53
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方「vs 近親」)。</p>
@@ -72,8 +72,8 @@ export const UsageGuidance: Story = {
72
72
  <div className="prose prose-sm max-w-prose mb-8">
73
73
  <p>適合 Carousel 的真實業務場景(點擊跳轉「展示」頁範例):</p>
74
74
  <ul className="space-y-1">
75
- <li><LinkTo kind="Design System/Components/Carousel/展示" name="首頁主視覺橫幅"><span className="text-primary hover:underline font-medium cursor-pointer">首頁 Hero Banner</span></LinkTo></li>
76
- <li><LinkTo kind="Design System/Components/Carousel/展示" name="商品圖片輪播"><span className="text-primary hover:underline font-medium cursor-pointer">商品圖片輪播</span></LinkTo></li>
75
+ <li><LinkTo kind="Design System/Components/Carousel/展示" name="首頁主視覺橫幅"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">首頁 Hero Banner</span></LinkTo></li>
76
+ <li><LinkTo kind="Design System/Components/Carousel/展示" name="商品圖片輪播"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">商品圖片輪播</span></LinkTo></li>
77
77
  </ul>
78
78
  <p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方 vs 近親 段)。</p>
79
79
  </div>