@qijenchen/design-system 0.1.0-beta.47 → 0.1.0-beta.49
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.
- package/ds-canonical/hooks/check_consumer_ds_primitive_misuse.sh +9 -0
- package/ds-canonical/hooks/check_story_invariants.sh +2 -3
- package/ds-canonical/hooks/tests/test_check_story_invariants.sh +18 -3
- package/ds-canonical/references/README.md +1 -1
- package/ds-canonical/references/composition-fidelity.md +27 -81
- package/ds-canonical/skills/design-system-audit/SKILL.md +2 -2
- package/ds-story-manifest.json +1 -1
- package/package.json +1 -1
- package/src/components/Sidebar/sidebar.stories.tsx +3 -2
|
@@ -72,6 +72,15 @@ if echo "$CONTENT" | grep -qE '<DS\.Empty[^>]+title=' && \
|
|
|
72
72
|
VIOLATIONS="${VIOLATIONS} - <Empty title=...> 無 icon 無 description = 違反 Empty.tsx:11「預設只需 description」minimal mock looks weird\n"
|
|
73
73
|
fi
|
|
74
74
|
|
|
75
|
+
# Pattern 8: 硬寫色值 / 字級 / shadow 繞過 DS token(2026-06-02 CF conformance-model 補主防線 —
|
|
76
|
+
# composition-fidelity 從 pixel-identity 收窄成 identity-opt-in 後,「consumer 用對 DS token」改由靜態
|
|
77
|
+
# conformance 防線保證,對齊 Polaris stylelint-polaris / Atlassian eslint-plugin / Carbon stylelint。
|
|
78
|
+
# 既有 check_layout_space_magic_numbers 守「間距」;此 pattern 補「色值/字級/shadow」缺口。
|
|
79
|
+
# 零誤判優先:只抓 hardcoded(`-[var(--...)]` token 用法不匹配)。
|
|
80
|
+
if echo "$CONTENT" | grep -qE '\b[a-z][a-z-]*-\[(#[0-9a-fA-F]{3,8}|rgb|rgba|hsl|hsla)[(]?|\btext-\[[0-9]|\bshadow-(sm|md|lg|xl|2xl)\b'; then
|
|
81
|
+
VIOLATIONS="${VIOLATIONS} - 硬寫色值/字級/shadow 繞過 DS token(bg-[#hex] / text-[14px] / shadow-md)→ 改 semantic color token / text-body 等 typography token / shadow-[var(--elevation-N)](per ui-development.md「Tailwind 5 條核心」rule 3)\n"
|
|
82
|
+
fi
|
|
83
|
+
|
|
75
84
|
# Pattern 6: Overlay trigger without defaultOpen state for visual demo
|
|
76
85
|
# (Skip in production .tsx; only enforce in .stories.tsx where visual snapshot matters)
|
|
77
86
|
if echo "$FILE" | grep -qE '\.stories\.tsx$'; then
|
|
@@ -534,7 +534,7 @@ rule_story_baseline_reference() {
|
|
|
534
534
|
# `.claude/references/story-baseline-registry.json` antiPatterns regex,逐條 scan 寫入內容。
|
|
535
535
|
# Severity=block → record_worst 2;severity=warn → stderr only。Allowlist
|
|
536
536
|
# `// @story-baseline-allow: <reason>` 整檔豁免。
|
|
537
|
-
#
|
|
537
|
+
# 2026-06-02 升 P0 BLOCKER(block-severity antiPattern):DS + consumer 全掃確認零現有違規後升級。
|
|
538
538
|
|
|
539
539
|
rule_story_archetype_registry() {
|
|
540
540
|
case "$TOOL" in
|
|
@@ -592,8 +592,7 @@ rule_story_archetype_registry() {
|
|
|
592
592
|
echo " 修法:Read baseline story + helpers,抄 production archetype。" >&2
|
|
593
593
|
echo " 或加 \`// @story-baseline-allow: <reason>\` 檔頭豁免(audit-logged)。" >&2
|
|
594
594
|
echo " 詳 .claude/rules/meta-patterns.md M35 + memory/feedback_nearest_same_purpose_canonical.md" >&2
|
|
595
|
-
#
|
|
596
|
-
# Future:zero existing violation → 改 record_worst 2 升 P0 BLOCKER
|
|
595
|
+
record_worst 2 # 2026-06-02 升 P0 BLOCKER:DS + consumer(ds-product-template)全掃確認零現有違規後升級(原 2026-05-20 ship-as-warn 的 TODO 條件達成);只 block-severity antiPattern(warn-severity 仍 stderr-only)
|
|
597
596
|
fi
|
|
598
597
|
done <<< "$PATTERNS"
|
|
599
598
|
done
|
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
# R5 name_jargon(PostToolUse;reads disk;L<n> layer / canonical / 中英夾雜 jargon)
|
|
11
11
|
# R6 description_jargon(PostToolUse;TS generic in description: → stderr warn)
|
|
12
12
|
# R7 story_baseline_reference(PreToolUse;wrap Sidebar/ChromeHeader/DataTable 無 baseline marker → stderr warn)
|
|
13
|
-
# R8 story_archetype_registry(PreToolUse;讀 .claude/references/story-baseline-registry.json
|
|
13
|
+
# R8 story_archetype_registry(PreToolUse;讀 .claude/references/story-baseline-registry.json;
|
|
14
|
+
# block-severity antiPattern → P0 record_worst 2,2026-06-02 升 P0)
|
|
14
15
|
#
|
|
15
16
|
# Test 重點:silent skip / 各 rule fire / allowlist marker escape。
|
|
16
|
-
# 不測 R3
|
|
17
|
+
# 不測 R3(需 spec.md frontmatter);R8 P0 block-severity 已測(#11,2026-06-02)。
|
|
17
18
|
|
|
18
19
|
set -u
|
|
19
20
|
|
|
@@ -194,12 +195,26 @@ STORIES_APP="/foo/my-project/packages/design-system/src/components/AppShell/app-
|
|
|
194
195
|
run_hook "PreToolUse" "Write" "$STORIES_APP" '
|
|
195
196
|
export const Default = () => (
|
|
196
197
|
<Sidebar>
|
|
197
|
-
<SidebarHeader><
|
|
198
|
+
<SidebarHeader><WorkspaceBrand /></SidebarHeader>
|
|
198
199
|
</Sidebar>
|
|
199
200
|
);
|
|
200
201
|
'
|
|
201
202
|
expect_warn "10. R7 wrap <Sidebar> no @story-baseline → stderr warn" "R7 story_baseline_reference"
|
|
202
203
|
|
|
204
|
+
# 11. R8 archetype registry P0(2026-06-02 升 P0;DS+consumer 零違規確認後升級):
|
|
205
|
+
# wrap <Sidebar> + simplified <SidebarHeader><span> mock(registry block-severity antiPattern)→ BLOCK
|
|
206
|
+
# 有 @story-baseline marker(R7 missing-marker 不 fire)→ exit 2 純由 R8 record_worst 2 造成
|
|
207
|
+
STORIES_R8="/foo/my-project/packages/design-system/src/components/AppShell/app-shell-r8.stories.tsx"
|
|
208
|
+
run_hook "PreToolUse" "Write" "$STORIES_R8" '
|
|
209
|
+
// @story-baseline: @qijenchen/design-system/components/Sidebar/sidebar.stories.tsx#IconCollapse
|
|
210
|
+
export const Default = () => (
|
|
211
|
+
<Sidebar>
|
|
212
|
+
<SidebarHeader><span>Acme</span></SidebarHeader>
|
|
213
|
+
</Sidebar>
|
|
214
|
+
);
|
|
215
|
+
'
|
|
216
|
+
expect_block "11. R8 simplified-mock <SidebarHeader><span> → P0 BLOCK" "R8 story_archetype_registry"
|
|
217
|
+
|
|
203
218
|
echo ""
|
|
204
219
|
echo "=== Summary ==="
|
|
205
220
|
echo "Passed: $PASS / $((PASS + FAIL))"
|
|
@@ -10,7 +10,7 @@ Agent 在執行時按需讀的深度 reference 檔 — audit protocol / FP 記
|
|
|
10
10
|
|-----|------|
|
|
11
11
|
| `audit-coverage-vs-24-checklist.md` | 業界 24-checklist 對照 + 為何不平行 audit 24 dim rationale |
|
|
12
12
|
| `build-ui-canonicals.md` | 建 UI 前 12 情境 + 8 layout primitive lookup |
|
|
13
|
-
| `composition-fidelity.md` | Composition fidelity
|
|
13
|
+
| `composition-fidelity.md` | Composition fidelity SSOT — consumer 用對 DS(conformance)為主,靜態 lint 驗(對齊 Polaris/Atlassian/Carbon);pixel/DOM identity diff 改 opt-in(2026-06-02 model 修正,非追求 product-vs-showcase 一致) |
|
|
14
14
|
| `cva-patterns.md` | cva 適用 / 不適用 + 例外清單(跟 CLAUDE.md shadcn 規範互補) |
|
|
15
15
|
| `drag-canonical.md` | 世界級 drag impl 對照 + dnd-kit collision strategy + Phase 1/2 fix plan |
|
|
16
16
|
| `item-anatomy-recipe.md` | 7 步建立新 row primitive workflow + audit grep guard |
|
|
@@ -1,101 +1,47 @@
|
|
|
1
|
-
# Composition Fidelity
|
|
1
|
+
# Composition Fidelity(SSOT,2026-05-27 初版 / 2026-06-02 conformance-model 修正)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**目標**:驗證「DS components 在 consumer(ds-product-template / fork)被正確使用、沒違反設計原則 / SSOT token」。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**2026-06-02 模型修正(per CF research + world-class benchmark + 專案 2026-05-27 自身結論)**:
|
|
6
|
+
驗的是「**consumer 有沒有用對 DS(conformance)**」,**不是**「consumer 畫面跟 DS showcase 長得一模一樣(pixel-identity)」。後者對「內容刻意不同的產品範本」是 **false-positive 來源、世界級公認反 pattern**。
|
|
6
7
|
|
|
7
|
-
## 對齊世界級
|
|
8
|
+
## 對齊世界級(2026-06-02 WebFetch verified,修正初版未驗 benchmark)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
- **Material UI X** PR snapshot gate:`mui-x` package consumer apps 用 `@mui/x-data-grid` 必過 visual snapshot
|
|
11
|
-
- **Carbon Design System**(IBM):Percy visual diff cross DS storybook + consumer-app sample
|
|
12
|
-
- **Atlassian Design System**:VR(visual regression)CI on consumer repos pulling DS
|
|
10
|
+
初版宣稱「Polaris/MUI/Carbon/Atlassian 都做 cross DS+consumer pixel diff」= **未驗證 + 不實**(M22/M26 違反)。實證(URL):
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
- **Consumer 用對 DS = 一律靜態 lint,非截圖**:Polaris [`stylelint-polaris`](https://polaris-react.shopify.com/tools/stylelint-polaris)(40+ rule promote DS adoption in consuming apps;「Please use a Polaris color token」)/ Atlassian [`@atlaskit/eslint-plugin-design-system`](https://atlassian.design/components/eslint-plugin-design-system/)(`ensure-design-token-usage` / `prefer-primitives` / `use-tokens-space`,帶 auto-fix)/ Carbon [`stylelint-plugin-carbon-tokens`](https://github.com/carbon-design-system/stylelint-plugin-carbon-tokens)(「enforce Carbon tokens ... rather than hard-coded values」)/ MUI(TS theme 型別 + eslint)。
|
|
13
|
+
- **Visual regression = 同一個 story 跨 commit 比自己的 baseline**(抓 DS 改壞自己),非跨 repo 比不同畫面:[Chromatic](https://www.chromatic.com/docs/branching-and-baselines/)(「baseline = last known good state of the story ... from a previous commit on that same branch」)/ [Storybook](https://storybook.js.org/docs/writing-tests/visual-testing)(「compare rendered pixels of every story against known baselines」+ 測 component-in-isolation)。
|
|
14
|
+
- **VR deterministic 鐵律**:seed 靜態 fixture + mask 動態區。產品 app 內容本質跟 DS showcase 不同 → 拿來 pixel 比必全紅 = 100% false positive。
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
## 機制三層(conformance 主、pixel identity 窄)
|
|
17
|
+
|
|
18
|
+
| 層 | 驗什麼 | 落地 |
|
|
17
19
|
|---|---|---|
|
|
18
|
-
| 1
|
|
19
|
-
| 2
|
|
20
|
-
| 3
|
|
20
|
+
| **1 marker(conformance 意圖)** | consumer wrap 高風險 primitive 必標 `// @story-baseline: <DS-path>#<export>` cite canonical | `check_consumer_story_baseline.sh`(P0)+ `check_story_invariants.sh` R7 |
|
|
21
|
+
| **2 靜態 conformance(主防線)** | consumer 用對 token/primitive、沒 simplified mock、沒硬寫色值/字級/間距/shadow、沒 API-misuse | `check_consumer_ds_primitive_misuse.sh`(P0,含 2026-06-02 Pattern 8 硬寫 token)+ `check_layout_space_magic_numbers.sh`(P0 間距)+ `check_story_invariants.sh` R8(P0,registry antiPattern)+ `check_chrome_header_avatar_canonical.sh` / `check_sidebar_menu_button_implicit_wrap.sh` |
|
|
22
|
+
| **3 pixel/DOM identity(opt-in,窄用)** | 僅「忠實複製 replica」或「同 story 跨版本回歸」才比 render identity | `scripts/composition-fidelity-visual-diff.mjs`,**只比標了 `@composition-fidelity-mode` 的 mapping**;單獨 `@story-baseline` = conformance 不做 identity diff |
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
**層 3 為何 opt-in(2026-06-02)**:`@story-baseline` 單獨 = conformance 意圖(交層 2 驗)。pixel/DOM identity 只在該 consumer story **額外標** `@composition-fidelity-mode: pixel|shell-only|structural` 才跑(用於忠實複製 / same-story 回歸)。0 個 opt-in → script exit 0(conformance 由層 2 保證)。**禁** 拿產品範本(內容刻意不同)去 pixel 比 DS showcase。
|
|
23
25
|
|
|
24
|
-
## 層 3 用法
|
|
26
|
+
## 層 3 用法(opt-in 時)
|
|
25
27
|
|
|
26
28
|
```bash
|
|
27
|
-
|
|
28
|
-
npm run composition-fidelity -- \
|
|
29
|
-
--ds-url=http://localhost:9001 \
|
|
30
|
-
--consumer-url=http://localhost:9002 \
|
|
31
|
-
--consumer-root=/path/to/ds-product-template \
|
|
32
|
-
--out=.claude/snapshots/composition-fidelity \
|
|
33
|
-
--threshold-pct=2
|
|
34
|
-
|
|
35
|
-
# CI — against built storybook-static dirs:
|
|
36
|
-
npm run composition-fidelity -- \
|
|
29
|
+
node scripts/composition-fidelity-visual-diff.mjs \
|
|
37
30
|
--ds-static=storybook-static \
|
|
38
31
|
--consumer-static=/path/to/ds-product-template/storybook-static \
|
|
39
|
-
--consumer-root=/path/to/ds-product-template
|
|
40
|
-
--threshold-pct=0.5
|
|
32
|
+
--consumer-root=/path/to/ds-product-template --threshold-pct=0.5
|
|
41
33
|
```
|
|
42
34
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
**Exit codes**:0 = all within threshold / 1 = at least one exceeds / 2 = setup error。
|
|
46
|
-
|
|
47
|
-
## Threshold guidance
|
|
48
|
-
|
|
49
|
-
- `0.5%` — strict baseline change(intentional fix必 update baseline)
|
|
50
|
-
- `2%` — typical(allows brand text difference like "Acme Inc" vs "Acme Product")
|
|
51
|
-
- `5%` — initial bootstrap(consumer 多元化內容差異)
|
|
52
|
-
|
|
53
|
-
**Initial ds-product-template baseline 1.41%**(measured 2026-05-27):brand text + NAV labels content-level diff。Structural composition byte-equal。
|
|
54
|
-
|
|
55
|
-
## CI workflow(shipped)
|
|
56
|
-
|
|
57
|
-
Actual gate is centralized in DS repo at `.github/workflows/composition-fidelity.yml`:checkout DS + `ajenchen/ds-product-template`,build both Storybooks,and run the local DS script against the two `storybook-static` directories.
|
|
58
|
-
|
|
59
|
-
```yaml
|
|
60
|
-
# .github/workflows/composition-fidelity.yml(design-system)
|
|
61
|
-
on: [push, pull_request]
|
|
62
|
-
jobs:
|
|
63
|
-
composition-fidelity:
|
|
64
|
-
runs-on: ubuntu-latest
|
|
65
|
-
steps:
|
|
66
|
-
- uses: actions/checkout@v4
|
|
67
|
-
with: { path: design-system }
|
|
68
|
-
- uses: actions/checkout@v4
|
|
69
|
-
with: { repository: ajenchen/ds-product-template, path: ds-product-template }
|
|
70
|
-
- run: npm ci --legacy-peer-deps
|
|
71
|
-
working-directory: design-system
|
|
72
|
-
- run: npm ci --legacy-peer-deps
|
|
73
|
-
working-directory: ds-product-template
|
|
74
|
-
- run: npm run build-storybook
|
|
75
|
-
working-directory: design-system
|
|
76
|
-
- run: npm run build-storybook
|
|
77
|
-
working-directory: ds-product-template
|
|
78
|
-
- run: |
|
|
79
|
-
node scripts/composition-fidelity-visual-diff.mjs \
|
|
80
|
-
--ds-static=storybook-static \
|
|
81
|
-
--consumer-static=../ds-product-template/storybook-static \
|
|
82
|
-
--consumer-root=../ds-product-template \
|
|
83
|
-
--threshold-pct=0.5
|
|
84
|
-
working-directory: design-system
|
|
85
|
-
```
|
|
35
|
+
掛在 `.github/workflows/composition-fidelity.yml`(checkout DS + ds-product-template,build 兩邊 storybook 後比對;**build-storybook 前必先 `build:lib`** 否則 storybook-config tsc 找不到 `@qijenchen/design-system` 型別 = TS2307)。
|
|
86
36
|
|
|
87
37
|
## 不該做的事
|
|
88
38
|
|
|
89
|
-
- ❌
|
|
90
|
-
- ❌
|
|
91
|
-
- ❌
|
|
92
|
-
- ❌ 抽樣
|
|
93
|
-
|
|
94
|
-
## 反 pattern 錨例
|
|
39
|
+
- ❌ **拿產品範本 demo 當 pixel-identity baseline 對 DS showcase 比**(內容刻意不同 = false positive,world-class 公認反 pattern)— 2026-06-02 改 identity opt-in 修正
|
|
40
|
+
- ❌ baseline story 用 `Math.random()` 等非確定性產值(render 不可重現 → 任何 diff 都是 noise)— 2026-06-02 修 `sidebar.stories.tsx` PageContent
|
|
41
|
+
- ❌ 把 baseline screenshots commit 進 consumer repo(stale)— fetch from DS Pages live
|
|
42
|
+
- ❌ 抽樣 N stories(M-rule 不抽樣)
|
|
95
43
|
|
|
96
|
-
|
|
97
|
-
1. Source byte-equivalent(DS sidebar.stories.tsx WorkspaceBrand 跟 product-workspace App.tsx 同 pattern)
|
|
98
|
-
2. Stale build artifact:DS storybook-static built BEFORE commit 4e3256c1 fix → DS render 用 ItemAvatar wrapper / consumer 用 raw Avatar
|
|
99
|
-
3. User screenshot 從 stale deploy 看到「DS-rendered」vs「consumer-rendered」structural diff
|
|
44
|
+
## 歷史錨例
|
|
100
45
|
|
|
101
|
-
**Lesson
|
|
46
|
+
- **2026-05-27**:user 抓 AppShell Avatar+Label drift。Root cause = stale build artifact(DS storybook-static built BEFORE fix commit)。Lesson:byte-identity ≠ visual ≠ deployed identity。
|
|
47
|
+
- **2026-06-02**:CF check 從建立起 30 次全紅 = 把產品範本(App.tsx,Acme Product / 自訂 nav / dashboard 內容)硬比 DS `sidebar#IconCollapse`(Acme Inc / 不同 nav / 隨機亂數內容),pixel/DOM identity 物理上不可能。修法:層 3 改 opt-in、補層 2 靜態 conformance(Pattern 8 + R8 升 P0)、修 baseline 隨機亂數。對齊專案 2026-05-27 自身結論(memory `feedback_ai_ground_truth_unreliable_mechanical_primary`:render fidelity 由架構保障、template-vs-canonical pixel diff = noise 非 drift)。
|
|
@@ -188,13 +188,13 @@ 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)。Action:DS canonical stories 加 `// @canonical-pattern: <pattern-name>` marker 標哪 helper 是 consumer 應抄的正確 reference;反 spec stories block 必加 `// @anti-pattern: <pattern-name>` 警示。配 Dim 53(spec-vs-code)+ 本 dim(spec-vs-stories)雙向 drift verify。對應 hook 預留 — 目前由 `check_chrome_header_avatar_canonical.sh` + `check_sidebar_menu_button_implicit_wrap.sh` 攔具體錨例;系統性 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_no_ds_catalog.sh` 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_story_baseline.sh` 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
|
|
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_ds_primitive_misuse.sh` 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`(per-component列 anti-pattern + tight API proposal),分 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 |
|
|
195
195
|
| 75 | **Plugin freshness session-start prompt**(2026-05-27 chain-C ship + user「主動引導」directive)| Fork user session_start hook `check_plugin_freshness.sh` reads local installed plugin.json version → fetch GitHub raw marketplace.json → diff version → if stale prompt run `npm run sync-all`. Sync-all 1-command 整合 npm update + claude plugin marketplace update + claude plugin update + restart prompt(per user 2026-05-27「不需要獨立命令兩次」)|
|
|
196
196
|
| 76 | **Escape marker abuse cap**(2026-05-27 per user「不亂加 escape markers」)| Consumer file 10 escape markers 累計 ≥3 distinct types OR ≥5 total → BLOCK。修法 3 選 1:重構走 DS canonical / 拆 file / env override `CLAUDE_BYPASS_ESCAPE_MARKER_AUDIT`。Hook `check_escape_marker_abuse.sh` enforces escape philosophy「rare per-line documented exception,非 daily tool」|
|
|
197
|
-
| 77 | **Composition-fidelity
|
|
197
|
+
| 77 | **Composition-fidelity:conformance-primary,pixel-identity opt-in**(2026-05-27 ship / **2026-06-02 model 修正**)| Consumer 對 DS 用法正確性**主要由靜態 conformance 驗**(dim 71 含 Pattern 8 + `check_layout_space_magic_numbers` + R7/R8),對齊世界級 static lint(Polaris stylelint-polaris / Atlassian eslint-plugin-design-system / Carbon stylelint-plugin-carbon-tokens;WebFetch verified)。`scripts/composition-fidelity-visual-diff.mjs` 的 pixel/DOM identity diff 改**明確 opt-in**:只比標 `@composition-fidelity-mode` 的 mapping(忠實複製 replica / same-story 跨版本回歸,對齊 Chromatic/Storybook same-story baseline);單獨 `@story-baseline` = conformance 不做 identity diff;0 opt-in → exit 0。**禁** 拿產品範本(內容刻意不同)pixel 比 DS showcase(world-class 公認反 pattern)。SSOT `composition-fidelity.md`。CI `.github/workflows/composition-fidelity.yml` |
|
|
198
198
|
| 78 | **Codex brief 禁列檔 invariant**(2026-05-27 codify per codex v1+v2 token-burn 2× anchor)| `check_codex_brief_invariants.sh` 4th invariant check:codex CLI brief 必含「禁列檔 / 禁 rg --files / 只讀 N file / 直接出 verdict」keyword,否則 codex 自動跑 `rg --files` 列 1300+ files 燒光 reasoning,無法產出 Step 5 Verdict(M31 Step 4.5 last-verdict gate fail)。Hook PreToolUse Bash codex exec/review 偵測 → BLOCKER。Escape `// @codex-brief-invariant-skip:` |
|
|
199
199
|
| 79 | **Tailwind v4 wildcard pattern in docs**(2026-05-28 codify per beta.27 6+ CI iteration anchor)| `check_tailwind_wildcard_in_docs.sh` P0 BLOCKER:.md/.spec.md/.sh 文件範例 `var(--X-*)` / `var(--X-A/B/C)` 寫法被 Tailwind v4 vite plugin scanner 當 literal class string 抓 → 產 invalid CSS(CSS 變數名禁 `*` `/`)→ Storybook build 死。改用 math notation `var(--X-N) N∈{a,b,c}`。Escape `// @tailwind-wildcard-allow:`。Anchor:beta.27 release CI iterations 1-6 死於此 |
|
|
200
200
|
| 80 | **Addon subdir ship completeness**(2026-05-28 codify per beta.27 7th iteration anchor)| `check_addon_subdir_ship.sh` P0 BLOCKER:addon 主檔(`.storybook/addons/*/` + `packages/storybook-config/addons/*/`)import `./utils/*` 等 relative subdir 但對應 dir 沒一起 ship → Rollup `Could not resolve` → CSF parse error cascade → build 死。修方向:(1) Copy 缺漏 dir(2) 跑 local build verify(3) commit。Escape `// @addon-subdir-skip:`。Anchor:beta.27 ds-devmode 搬 npm 包漏帶 utils/ 6 files |
|
package/ds-story-manifest.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"scripts/composition-fidelity-visual-diff.mjs",
|
|
8
8
|
"product-workspace apps/template/src/AllDsComponents.stories.tsx (DsCanonicalPortal)"
|
|
9
9
|
],
|
|
10
|
-
"generatedAt": "2026-06-
|
|
10
|
+
"generatedAt": "2026-06-02T16:12:11.748Z"
|
|
11
11
|
},
|
|
12
12
|
"components": {
|
|
13
13
|
"accordion": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qijenchen/design-system",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.49",
|
|
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",
|
|
@@ -132,10 +132,11 @@ const PageContent = ({ title, description }: { title: string; description: React
|
|
|
132
132
|
<div className="max-w-2xl">
|
|
133
133
|
<p className="text-body text-fg-secondary mb-6">{description}</p>
|
|
134
134
|
<div className="grid grid-cols-2 gap-4">
|
|
135
|
-
{
|
|
135
|
+
{/* 固定值(非 Math.random)— story 是 visual baseline,必確定性,否則每次 build 像素不同 = composition-fidelity / visual regression 無法比對(2026-06-02 fix)*/}
|
|
136
|
+
{([['專案數量', 24], ['團隊成員', 8], ['本週提交', 47], ['待處理', 3]] as const).map(([t, v]) => (
|
|
136
137
|
<div key={t} className="rounded-lg border border-divider bg-surface p-4">
|
|
137
138
|
<p className="text-caption text-fg-muted">{t}</p>
|
|
138
|
-
<p className="text-h5 font-semibold mt-1">{
|
|
139
|
+
<p className="text-h5 font-semibold mt-1">{v}</p>
|
|
139
140
|
</div>
|
|
140
141
|
))}
|
|
141
142
|
</div>
|