@qijenchen/design-system 0.1.0-beta.65 → 0.1.0-beta.66

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.
@@ -36,6 +36,12 @@
36
36
  "regex": "<SidebarHeader[^>]*>[[:space:]]*<div[^>]*flex-col",
37
37
  "severity": "block",
38
38
  "rationale": "SidebarHeader brand 必單行(Fixed-h chrome 不可成長,sidebar.spec.md SidebarHeader 段 2026-06-12 明文)。flex-col 直接子層 = logo name 下加副標的結構簽名 — shadcn TeamSwitcher demo prior 滲漏;plan/org 資訊歸 workspace switcher dropdown row,非常駐 header"
39
+ },
40
+ {
41
+ "id": "sidebar-footer-two-line",
42
+ "regex": "<SidebarFooter[^>]*>.{0,400}(text-xs|leading-tight)",
43
+ "severity": "block",
44
+ "rationale": "SidebarFooter user 區必單行(名字 only;職稱/email 等身份資訊歸 ItemAvatar hoverCard 的 ProfileCard,sidebar.stories.tsx UserFooter canonical)。shadcn nav-user 的「name + email text-xs 兩行」是強 LLM 先驗,且 text-xs 違反 chrome typography canonical(sidebar.spec『chrome 內 text 一律 text-body-lg』);400 字元窗限制 greediness"
39
45
  }
40
46
  ]
41
47
  },
@@ -88,6 +94,12 @@
88
94
  "regex": "<ChromeHeader[^>]*>.{0,160}<span[[:space:]]+className=\"[^\"]*flex-1[^\"]*\">",
89
95
  "severity": "block",
90
96
  "rationale": "ChromeHeader title 該用 <h1 className=\"text-body-lg font-medium\"> 緊鄰 SidebarTrigger,不該用 <span flex-1> 撐空間造成 toggle + title 巨大間距"
97
+ },
98
+ {
99
+ "id": "chromeheader-subtitle-second-line",
100
+ "regex": "<(ChromeHeader|PageHeader)[^>]*>[[:space:]]*<div[^>]*flex-col",
101
+ "severity": "block",
102
+ "rationale": "Chrome 家族永遠 single-row(header-canonical.spec.md:47,剛性 --chrome-header-height);title 下加副標/description = LLM 先驗回填(Polaris Page subtitle / Tailwind UI page heading 先驗),頁面說明文字歸 content 區,非 chrome。flex-col 直接子層 = 兩行結構簽名"
91
103
  }
92
104
  ]
93
105
  }
@@ -188,7 +188,7 @@ User 2026-05-15 verbatim 抓「DS 深度稽核漏 storybook content quality」+
188
188
  | 68 | **Stories-vs-spec canonical drift**(2026-05-27 codify per user「DS 自己 stories 教錯 = consumer 抄 stories 抄錯」root cause)| 對每 component `*.stories.tsx` + `*.anatomy.stories.tsx` + `*.principles.stories.tsx` grep:含 `<SidebarHeader>` 內 `<ItemAvatar>` / 同類 spec 明文禁止的 pattern → 違反(DS 教 consumer 錯 pattern,類似 2026-05-27 sidebar 3 stories 在 WorkspaceBrand 用 ItemAvatar 違反 chrome header canonical)。**主防線 = R8 registry**(`.claude/references/story-baseline-registry.json` requiredHelpers / antiPatterns + `check_story_invariants.sh` R8/R9 機械攔)+ `check_chrome_header_avatar_canonical.sh` / `check_sidebar_menu_button_implicit_wrap.sh` 具體錨例 hook;`// @canonical-pattern:` / `// @anti-pattern:` 文字 marker **降格為建議性輔助**(2026-06-12 R2 拍板 — 全庫僅 sidebar.stories 2 處採用且無機械強制 = 紙防線,不再「必加」;存在時 audit 順驗其與 spec 一致)。配 Dim 53(spec-vs-code)+ 本 dim(spec-vs-stories)雙向 drift verify;系統性 stories drift audit run via design-system-audit Dim 68 batch sweep |
189
189
  | 69 | **Consumer no-DS-catalog enforcement**(2026-05-27 M31 codex synthesis per user「眼不見為淨」+「做產品真的能使用跟 ds repo 一模一樣的元件做產品嗎?」)| 對 consumer `apps/**/*.stories.tsx` grep:basename 為 `EveryDsComponent` / `AllDsComponents` / catalog naming + `Object.keys(DS).map` iterate-render + mass hand-mock(≥5 distinct `<DS.X>` 單 file)→ 違反(DS catalog 是 DS Storybook 唯一 SSOT,consumer 重寫必 drift,2026-05-27 錨例 7 bug)。Hook `check_consumer_app_invariants.sh(r1,2026-06-11 merge)` PostToolUse Write/Edit BLOCKER,escape `// @consumer-catalog-allow:`。允許 portal proxy(iframe to DS Storybook)|
190
190
  | 70 | **Consumer @story-baseline enforcement**(2026-05-27 M31 codex synthesis)| 對 consumer `apps/**/*.stories.tsx` grep:用高風險 DS primitive(DataTable / Dialog / Sheet / Popover / DropdownMenu / Tooltip / HoverCard / LinkInput / RadioGroup / CircularProgress / AppShell / Sidebar)但無 `// @story-baseline: <DS-story-path>#<exportName>` marker → 違反(consumer 必 reference DS canonical story 才 enable visual diff CI)。Hook `check_consumer_app_invariants.sh(r2,2026-06-11 merge)` PostToolUse BLOCKER,escape `// @story-baseline-allow:`。SSOT mapping → `ds-story-manifest.json`(DS package ship)|
191
- | 71 | **Consumer DS primitive misuse anti-pattern**(2026-05-27 per user「做產品真的要能使用跟 ds repo 一模一樣的元件」)| 對 consumer `apps/**/*.{tsx,ts}` production + stories grep:`<CircularProgress size={N}>` literal number 覆蓋 default 24 / `<RadioGroupItem>` 無 `<SelectionItem control={...}>` wrap / `<DataTable columns={[single-col]}>` minimal / `<LinkInput placeholder=...>` 無 `value` prop / `<Empty title=...>` 無 icon AND 無 description / Overlay story 無 `defaultOpen` 視覺 snapshot 看不到 content / **硬寫色值-字級-shadow 繞 token(`bg-[#hex]` / `text-[14px]` / `shadow-md`,2026-06-02 Pattern 8,CF conformance-model 主防線)**。Hook `check_consumer_app_invariants.sh(r3,2026-06-11 merge)` BLOCKER。Per-violation cite spec.md file:line。Escape `// @ds-misuse-allow:` |
191
+ | 71 | **Consumer DS primitive misuse anti-pattern**(2026-05-27 per user「做產品真的要能使用跟 ds repo 一模一樣的元件」)| 對 consumer `apps/**/*.{tsx,ts}` production + stories grep:`<CircularProgress size={N}>` literal number 覆蓋 default 24 / `<RadioGroupItem>` 無 `<SelectionItem control={...}>` wrap / `<DataTable columns={[single-col]}>` minimal / `<LinkInput placeholder=...>` 無 `value` prop / `<Empty title=...>` 無 icon AND 無 description / Overlay story 無 `defaultOpen` 視覺 snapshot 看不到 content / **硬寫色值-字級-shadow 繞 token(`bg-[#hex]` / `text-[14px]` / `shadow-md`,2026-06-02 Pattern 8,CF conformance-model 主防線)** / **AppShell slot 餵 raw element(`sidebar={<div>}` 等,2026-06-12 Pattern 9,app-shell.spec:296-299)**。**判斷層補掃(2026-06-12 fork 四不像 + 先驗滲漏 anchor;hook 零誤判原則不上 regex)**:(a) page-level entry 手拼 raw 殼而無 `<DS.AppShell>`(對照 app-shell.spec「何時用/不用」表;愈不用 DS 愈不觸 hook = 反直覺洞)(b) 手刻 right panel(`w-[Npx] border-l` div,該走 `aside={<AppShellAside>}`)(c) ChromeHeader/頁頂 tabs 沒走 `withTabs` lockstep(header-canonical.spec「withTabs 連動」段)(d) BulkActionBar 內 primary 鈕 / 浮層 shadow(bulk-action-bar.spec 禁止事項 — regex 無法 bound 到 actions slot,判斷層接)(e) FileViewer toolbar 加鈕不走 `onCapabilitiesChange`(file-viewer.spec:183-194;R9 skip-list 豁免該家族故無寫入網)(f) chrome 面(ChromeHeader/SidebarFooter)title 下第二行(registry 簽名涵蓋 stories;fork production code 由本 dim 接)。Hook `check_consumer_app_invariants.sh(r3,2026-06-11 merge)` BLOCKER。Per-violation cite spec.md file:line。Escape `// @ds-misuse-allow:` |
192
192
  | 72 | **DS API surface tightening**(2026-05-27 — 治標 vs 治本)| Hook 71 偵測 anti-pattern 是 lint 層攔截;治本要 DS API design 強到 misuse 即 fail tsc。Audit:逐 component review API surface — `size?: number` 該改 `'sm'\|'md'\|'lg'` enum / `columns: Column[]` 該加 min length runtime check / `title` + `description` 該有 type-level XOR / Overlay primitive `defaultOpen` 該 require explicit。配套 codify in `tightening-roadmap.md`(若存在;不存在則以 `props-naming.md` + 各 spec API 段為準,對齊 audit-prompts.md dim 72),分 quarter ship。對應 Dim 71 是攔當前 misuse,本 dim 是消除未來 misuse 可能 |
193
193
  | 73 | **Full-story visual+interaction sweep enforce**(2026-05-27 codex M31 P0 finding)| Audit report JSON `storyResults.length === manifest.totalStories`(916)。Sample < 916 = reject(per user「不准抽樣」)。Hook `check_full_story_visual_interaction_sweep.sh` PostToolUse audit-report.json BLOCKER。Escape `"_sampling_allowed": "<rationale>"`(極罕見)|
194
194
  | 74 | **Overlay open/focus/Escape probe**(2026-05-27 codex M31 P0 finding + user 7-bug 錨點「overlay 沒彈出」)| Consumer story 用 Tooltip / Popover / Dialog / Sheet / DropdownMenu / HoverCard Trigger 必含 `defaultOpen` OR `open={true}` OR `play()` interaction click。Trigger-only catalog = reject(visual snapshot 看不到 content)。Hook `check_overlay_open_focus_escape_probe.sh` BLOCKER。HoverCard exception via `@story-trait-allow: missing-opensnapshot` per codex |
@@ -34,6 +34,7 @@ import {
34
34
  SidebarMenuItem,
35
35
  SidebarMenuButton,
36
36
  SidebarTrigger,
37
+ ChromeHeader,
37
38
  TooltipProvider,
38
39
  Avatar,
39
40
  ItemAvatar,
@@ -92,15 +93,19 @@ function AppSidebar() {
92
93
  )
93
94
  }
94
95
 
95
- // ── PageHeader(對齊 DS canonical chrome-header pattern:fixed --chrome-header-height + SidebarTrigger + title)──
96
+ // ── PageHeader(消費 DS ChromeHeader primitive,2026-06-12 修:原手刻 <header className=...>
97
+ // 繞過 header-canonical 全部機械簽名 + 違反「消費 primitive 不 hand-craft」canonical;
98
+ // 對齊 _demo-helpers.tsx PageHeader 同款消費形)──
96
99
  // SidebarTrigger 必有(primary-sidebar mode 的 menu toggle 入口,⌘B keyboard shortcut)
97
- function PageHeader({ title, rightSlot }: { title: string; rightSlot?: React.ReactNode }) {
100
+ // rightSlot JSX.Element(非 React.ReactNode):workspace app 自帶 @types/react 副本與
101
+ // DS 套件 .d.ts 解析的 ReactNode 版本不相容(bigint 差異)— Element 兩邊皆可指派
102
+ function PageHeader({ title, rightSlot }: { title: string; rightSlot?: JSX.Element }) {
98
103
  return (
99
- <header className="flex items-center gap-2 h-[var(--chrome-header-height)] px-[var(--layout-space-loose)] bg-surface border-b border-divider">
104
+ <ChromeHeader className="bg-surface">
100
105
  <SidebarTrigger />
101
106
  <h1 className="text-body-lg font-medium flex-1 truncate">{title}</h1>
102
107
  {rightSlot}
103
- </header>
108
+ </ChromeHeader>
104
109
  )
105
110
  }
106
111
 
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.65
3
+ > 全 component / pattern 的 variants / sizes / 禁止事項。build-time 從 spec.md frontmatter 生成,禁手改。v0.1.0-beta.66
4
4
 
5
5
  # Components
6
6
 
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.65
4
+ > 54 components + 4 public patterns + design tokens。v0.1.0-beta.66
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)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qijenchen/design-system",
3
- "version": "0.1.0-beta.65",
3
+ "version": "0.1.0-beta.66",
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",
@@ -297,6 +297,7 @@ Main 內塞什麼(table / field / card / page header / list)的 layout + spacing
297
297
  - ❌ 禁:`sidebar={<div>...</div>}`(應傳 `<Sidebar>` primitive,確保視覺 SSOT。型別上收 ReactNode、不做機械強制 — React 型別強制易被 wrapper 包一層繞過,世界級 shell 元件皆收 ReactNode;本約定靠 story 示範 + audit 把關。2026-06-10 user 拍板 2a:措辭「必」→「應」對齊 code 真實)
298
298
  - ❌ 禁:`header={<header>...</header>}`(應傳 `<ChromeHeader>` 或消費 `header-canonical` 派生 header;同上,型別不機械強制)
299
299
  - ❌ 禁:AppShell.Main 自加 padding(違反 layoutSpace 規則 1B)
300
+ - ❌ 禁:AppShellAside header 加第二行 / actions(header = 單行 title + close X,API typed `title: string` 結構鎖;說明文字歸 aside body — 2026-06-12 fork-drift 防線)
300
301
  - ✅ 必:`layout` mode 在 product 啟動時固定,**不在 runtime 切換**(切換 = product 角色變動 = 應該重 mount app)
301
302
 
302
303
  ---
@@ -128,7 +128,9 @@ SidebarProvider ← 全域 context(open 狀態、cookie、快捷
128
128
  - **Density**(md/lg)是**全域 UI 設定**,影響所有 `--field-height-*` tokens 的實際值
129
129
  - 兩者**獨立**:size="md" 在 density="md" 下是 32px row、在 density="lg" 下自動變 36px row
130
130
 
131
- **Chrome header 不跟 size 變**:`SidebarHeader` / `SidebarFooter` 固定用 `var(--chrome-header-height)`,只跟 **density** 連動,不跟 `size` prop 變——因為 header 是結構槽位,不是 row
131
+ **Chrome header 不跟 size 變**:`SidebarHeader` 固定用 `var(--chrome-header-height)`,只跟 **density** 連動,不跟 `size` prop 變——因為 header 是結構槽位,不是 row。**SidebarFooter 修正(2026-06-12 doc-to-code)**:footer 實作自 2026-04-14 起為 content-based(`flex flex-col py-2`,sidebar.tsx SidebarFooter,行為同 SidebarGroup),**非** fixed-h——單行高度由下方內容紀律保證,非結構鎖。
132
+
133
+ **SidebarFooter user 區必單行(2026-06-12 明文,fork drift 防線)**:名字單行(`text-body-lg` truncate);職稱/email/工號等身份資訊歸 `ItemAvatar hoverCard` 的 ProfileCard(UserFooter canonical,sidebar.stories.tsx)。shadcn nav-user 的「name + email `text-xs` 兩行」是 demo 慣例非本 DS canonical(`text-xs` 同時違反下方 chrome typography 一律 `text-body-lg` 條);registry antiPattern `sidebar-footer-two-line` 機械攔。
132
134
 
133
135
  **SidebarHeader brand 必單行(2026-06-12 明文,fork drift anchor)**:Avatar 24 + 名稱單行,**無副標 slot**——Fixed-h chrome 結構上不可成長;plan/org 等第二行資訊歸 workspace switcher dropdown row,對齊 Slack/Notion/Linear 現行版單行。shadcn TeamSwitcher demo 的「Acme Inc + Enterprise」兩行是 demo 慣例非本 DS canonical(fork repo AI 曾以訓練先驗在 spec 沉默處回填此副標)。Registry antiPattern `sidebar-header-subtitle-second-line` 機械攔(flex-col 直接子層簽名)。
134
136
 
@@ -205,13 +207,13 @@ Gmail / Linear 的標準版型:上方 SidebarGroup 放扁平主導覽(Sideba
205
207
 
206
208
  ## Chrome header / footer 高度:density-responsive shared token
207
209
 
208
- `SidebarHeader` 和 `SidebarFooter` 的高度 = `var(--chrome-header-height)`,**不是寫死**,也**不是完全 content-based**。這個 token:
210
+ `SidebarHeader` 的高度 = `var(--chrome-header-height)`,**不是寫死**,也**不是完全 content-based**(SidebarFooter 為 content-based,見上方 2026-06-12 修正)。這個 token:
209
211
 
210
212
  > **跨家族 SSOT pointer**:本元件 SidebarHeader 屬 **Chrome header(Fixed-h)家族**;border / padding / dismiss size / withTabs(tabs 進 header 時 border auto-suppress + tabs size 對應 + flush stack)的跨家族視覺契約 SSOT 詳 `patterns/header-canonical/header-canonical.spec.md`。本節僅 codify Sidebar 特有的高度 + token rationale。
211
213
 
212
214
  - md density: `3rem`(48px)
213
215
  - lg density: `3.5rem`(56px)
214
- - **跨元件共享**:sidebar header、sidebar footer、主內容 page header 都用同一個 token
216
+ - **跨元件共享**:sidebar header、主內容 page header 都用同一個 token(sidebar footer 為 content-based,見上方 2026-06-12 修正)
215
217
  - **隨 density 同步變**
216
218
 
217
219
  ### 為什麼是這個設計(不是 h-12 寫死 或 py-3 純 padding)
@@ -288,6 +288,7 @@ ChromeHeader / SurfaceHeader 新增 `tabsSlot?: ReactNode` prop。提供時自
288
288
  - ❌ Hardcode `h-16` / `h-14` / `h-12` 自寫 chrome header 高度 — 必 `--chrome-header-height`
289
289
  - ❌ Header 內 close X 用 `size="md"` 或 `size="lg"`(real grep 100% sm)
290
290
  - ❌ Tabs default 覆寫回 `size="md"`(W5 + W6)— cva default 已是 sm,不該寫回 md
291
+ - ❌ ChromeHeader children 塞 `flex-col` 第二行(title + 副標/description)— Chrome 家族永遠 single-row(上方「為什麼兩家族」段);頁面說明文字歸 content 區 page heading,非 chrome(2026-06-12 fork-drift 防線,同 sidebar.spec.md SidebarHeader 必單行條;registry antiPattern `chromeheader-subtitle-second-line` 機械攔)
291
292
 
292
293
  ---
293
294