@q1travel/design-system 0.4.0 → 0.5.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@q1travel/design-system",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist-pkg/design-system.js",
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "files": [
23
23
  "dist-pkg",
24
- "contracts",
24
+ "contracts/*.md",
25
25
  ".agents",
26
26
  "tooling",
27
27
  "bin"
@@ -34,12 +34,15 @@
34
34
  "sync:contracts": "node scripts/sync-design-contracts.mjs",
35
35
  "build": "npm run sync:contracts && tsc -b && vite build",
36
36
  "preview": "vite preview --host 127.0.0.1",
37
- "check:shell": "npm run sync:contracts && node scripts/check-shell.mjs && node scripts/check-token-package.mjs",
38
- "check:browser": "node scripts/check-browser.mjs",
37
+ "check:shell": "npm run sync:contracts && node scripts/check-shell.mjs && node scripts/check-token-package.mjs && npm run check:release-provenance",
38
+ "check:browser:strictmode": "node scripts/check-browser.mjs --strictmode-ref",
39
+ "check:browser": "npm run check:browser:strictmode && node scripts/check-browser.mjs",
40
+ "check:package-api": "node scripts/check-package-api.mjs",
41
+ "check:release-provenance": "node --test scripts/check-release-provenance.test.mjs && node scripts/check-release-provenance.mjs",
39
42
  "check": "npm run build && npm run check:shell && npm run check:browser",
40
43
  "build:lib": "vite build --config vite.lib.config.ts",
41
44
  "build:style": "node scripts/build-style.mjs",
42
- "build:pkg": "npm run build:lib && npm run build:style",
45
+ "build:pkg": "npm run build:lib && npm run build:style && npm run check:package-api",
43
46
  "prepack": "npm run build:pkg"
44
47
  },
45
48
  "dependencies": {
@@ -1,94 +0,0 @@
1
- # Review 模式 — 评审本仓组件对齐 Geist
2
-
3
- 这是 product-design **Review 模式**在「评审本仓组件对齐 Geist」场景下的完整流程。Review 评审消费者产品界面(如 q1travel-web)时用 `SKILL.md` 的通用路由;评审**本仓组件**(`q1travel-design-system` 自身的组件页,对齐 Geist)时用本文件。权威边界:本文件是收编的评审**流程**;设计判断的 canonical 恒为 `contracts/`,二者冲突时以 `contracts/` 为准。Geist 是外部参照(实测对齐的工作方法),不是实现源(见 review/decision-log/0002)。
4
-
5
- # Geist 对齐评审(逐组件)
6
-
7
- ## Overview
8
-
9
- 参照物是 Geist 的**实测渲染值**,不是记忆、截图目测或猜测。每件组件独立走完本清单,产出差异单,交用户逐件拍板。队列与进度唯一源:`tasks/v1-queue.md`。
10
-
11
- ## 何时使用
12
-
13
- - 打磨/复核任何组件页;验收自己或子代理的组件改动
14
- - 不适用:纯文档措辞调整、与 Geist 无对应物的自有页面(但"量化门槛"节仍然执行)
15
-
16
- ## 评审步骤(REQUIRED,按序)
17
-
18
- 1. **/web-design-guidelines 定向扫描**:用 Skill 工具调用 `web-design-guidelines`,扫描目标**只有一个 URL——该组件的 Geist 同名页**(如 Avatar → `https://vercel.com/geist/avatar`),不扫整站、不带其他页;发现项全部并入差异单。无 Skill 工具的子代理不得跳过此步——由主编排者代跑并把发现项注入子任务。
19
- 2. **章节大纲对照**:同一 URL 上列出该页**全部章节**,逐章判定:已覆盖 / 需补齐 / 记 non-goal(书面理由)。demo 与文档两头核;冲突时以 demo 的整体观感为准(用户看的是 demo)。
20
- 3. **实测度量**:浏览器读 Geist 的 computed style 与真实几何,逐项对照:尺寸档、padding、radius、字号字重、颜色(oklch)、动效时长与缓动。
21
- 4. **感知复查(两主题)**:亮/暗两主题下测**渲染后的实际对比度**;结构/aria/契约全对 ≠ 可读(暗色禁用态曾三次漏检)。
22
- 5. **逐元素交互探测 + 边界**(最易漏维度,见下方专章):
23
- - **探测**:对每一个"长得像能点"的元素(图标 / 按钮样 / pill / 链接样)用扩展真的 hover+click——查它是不是 `button/a/[role]`、有没有 `cursor:pointer`/`tabindex`/`aria-label`、点击后有无状态变化(图标切换 / DOM / 剪贴板)。**"判定某物是纯装饰"是最危险的预设,必须逐元素实测反证**(Browser 把可点的复制按钮当装饰漏了)。图标不只查"有没有"、还要查"是哪个"(渲染真实 SVG path 放大确认,别照抄旧实现——Browser 锁/复制照抄反了)。
24
- - **边界**:已知交互元素测键盘全路径(Tab/方向键/Esc/Enter/Space)、焦点环完整、disabled 不可聚焦;长文案/换行/极窄容器/空列表;浮层组件测视口边缘碰撞翻转。
25
- - 每个确认为真交互的元素,再逐条过下方「交互完整性清单」。
26
- 6. **家族配方优先**:改配方前先查下表既有规则,复用不新开。品牌层永远不跟 Geist:indigo #533afd 守「动作」、绿守「成功」、蓝守「info」。任何偏离 Geist 必须留书面理由(页面注释或 memory)。
27
- 7. **组件化**:该件若仍是页面内联 specimen,按先例(Tabs/Switch/Skeleton/StatusDot)提升为 `components/ui/` 真组件 + barrel 导出 + 契约。
28
-
29
- ## 交互完整性清单(Browser 连栽三层的教训 —— 复核每件都过一遍)
30
-
31
- **根因**:此前复核只测"看起来对不对"(视觉/几何/章节/对比度),几乎没测"每个元素能不能点、该不该能点"。用户三次复核 Browser 才逼出:图标反了 → 复制按钮被当纯装饰(漏了它是真 button)→ 补了交互又漏配套。**凡是判定"纯装饰/静态组件"的组件,都要用第 5 步探测反证一遍;凡确认为真交互的元素,下表缺一即漏:**
32
-
33
- | 配套项 | 要求 | Browser 栽点 |
34
- |---|---|---|
35
- | 点击目标 | ≥24×24px(WCAG 2.2);过小用 `min-width/height` + 负 margin 扩大命中区、不撑高容器 | 复制按钮只有 16px |
36
- | 状态可感知 | 视觉状态切换必须配 `role="status"`/`aria-live` 播报;`aria-label` 恒为动作名(如"复制网址"),状态另走 live region,**不把 label 当状态切** | 只切图标,读屏听不到成功 |
37
- | 卸载清理 | `setTimeout`/事件监听/订阅在 `useEffect` return 里清理,防已卸载组件 setState | timer 无 cleanup |
38
- | 键盘可达 | `tabindex` 正确 + `focus-visible` 焦点环 + Enter/Space 触发 | — |
39
- | a11y 联动 | 可交互元素**绝不**能落在 `aria-hidden` 容器内(WCAG);一旦发现漏了交互 → 整个 aria-hidden 策略要**连带重构**(装饰各自 hidden、交互元素可达),不能局部补丁 | 整个 chrome aria-hidden 吞掉复制按钮 |
40
- | 契约层 | 断言交互**行为**不只结构存在:点击后状态切换、失败路径、Tab 可达、装饰全量 aria-hidden、交互元素无 aria-hidden 祖先 | 断言只覆盖"元素存在" |
41
- | Provider 依赖 | 可独立组合的 building block 若需复制等交互,直接用 `lib/clipboard` 边界 + 就地反馈,**不接 useCopy/ToastProvider**(否则单独取用会崩) | — |
42
-
43
- 测试局限记录:自动化点击(preview/CDP)无 user-activation,`execCommand('copy')` 返回 false → 就地复制反馈触发不了;成功路径靠代码与 `useCopy` 同源 + Codex 逻辑复审背书,契约层测结构+尺寸+aria、不强测剪贴板写入。
44
-
45
- ## 家族配方速查
46
-
47
- | 领域 | 既有配方 |
48
- |---|---|
49
- | 语义色面 | `--q1-tone-{bg,border,fg}-{info,success,warning,danger}` |
50
- | 禁用态 | bg-subtle + text-muted(opacity 恒 1) |
51
- | 字段尺寸档 | xs 24 / sm 32 / md 36 / lg 48 |
52
- | Button 刻度 | 高 32/40/48,radius 6/6/8,字重 500 |
53
- | 暗色阴影 | 恒用 `--q1-color-shadow`(黑) |
54
- | 浮层边框 | 恒用 `--q1-color-border-overlay` |
55
- | 可作 Tooltip 触发器的组件 | 必须 `{...rest}` 透传原生/ARIA/事件属性(cloneElement 注入;Button/Badge 两案教训) |
56
- | 纯图标可访问名 | title 属性读屏不可靠,升格 role="img"+aria-label(StatusDot/Badge 配方) |
57
-
58
- ## 测量工具坑(先疑方法,再下结论)
59
-
60
- | 症状 | 正确做法 |
61
- |---|---|
62
- | "子元素溢出了"(rect 比较得出) | getBoundingClientRect 不能证明可见溢出;用 elementFromPoint 正反对照做命中测试 |
63
- | 视口边缘测试出现负坐标 | 用 position:fixed 模拟贴边,margin 会触发 Chrome scroll anchoring 补偿 |
64
- | "动画不可见 / 冻在 opacity:0" | 自动化标签页常为 visibilityState:hidden,动画冻结;查 Animation.currentTime 或 finish() 后读定格态 |
65
- | "hover 无动效"(hidden 页实测) | rAF 与 JS 弹簧在 hidden 页完全不跑,悬停后读恒等矩阵≠无动效,该结论一律无效;hover 后用 getAnimations() 读冻结 CSSTransition 的关键帧取证(Book 案:据此挖出 rotateY(-20deg) scale(1.066) translateX(-16px)) |
66
- | 截图看出"布纹/颗粒/材质感"等细腻纹理 | 大片纯色区域截图会被 JPEG 压缩出块状伪影,酷似编织纹理;先注入同色纯色 div 做对照截图,伪影会同样出现就证伪;真凭据是 computed style 的 background-image/filter/mask-image/backdrop-filter/mix-blend-mode(Book 案:Geist"Textured"节色带经查全部 none,视觉纹理是截图伪影,非真实设计) |
67
- | 子部件脱离原生父级上下文单独渲染(如"单独组合"演示) | 结构完整+geometry 数字对 ≠ 颜色对——若样式靠 `--x-var` 只在父级选择器定义,脱离上下文后 `var()` 静默退化(border-color→currentColor、background→transparent),肉眼截图可能看不出;必须给每个 `var()` 补 token fallback(`var(--x, var(--q1-color-y))`)并单独断言 computed 颜色值,不能只信"渲染出来了、geometry 对了"(Browser 案:Codex 揪出,自查两轮都没测颜色) |
68
- | 黑盒行为测不出/自相矛盾 | 先查工具签名与事件可信度(真信任输入 vs 合成 dispatchEvent),再谈"不可知" |
69
-
70
- ## 量化门槛(全过才算完成)
71
-
72
- - `npm run check` 全绿(= build + check:shell + check:browser)。**裸 `tsc --noEmit` 恒过 = 没测**(根 tsconfig 是 files:[] 的 solution 文件);类型检查用 `tsc -b tsconfig.app.json --noEmit` 或走 build。
73
- - 契约与守卫**同步**更新;新断言必须断言规则体/真实几何不变量,不只断言 class 名存在。
74
- - 浏览器 console 零新错误/警告。
75
- - 守卫既有坑:fixed-width 黑名单(用 `min(420px,…)`/`20rem` 等值绕)、禁 `rgba(` 字面量(用 color-mix 表达)、hex 守卫下品牌字面量用 oklch。
76
-
77
- ## 完成后双盲复审(REQUIRED,定稿前)
78
-
79
- check 全绿、提交后、标记已定稿前,必须过两道**互相独立**的评审,再按严重程度合并呈报:
80
-
81
- 1. **自查 diff**:不只看语法和明显 bug,重点检查——隐藏副作用、破坏兼容性、边界情况、性能风险、安全风险、命名误导、测试不足、未来维护成本;并逐条回答四问:这个改动会不会破坏旧逻辑?有没有漏掉边界情况?测试是不是只覆盖了最顺利的路径?以后维护的人会不会被这个命名误导?
82
- 2. **Codex 独立复审**:把同一评审要求原文交给 Codex,**不喂自查结论**,双方独立得出发现后再合并对照。
83
-
84
- 真问题修复后重跑量化门槛再定稿;判定不修的发现必须记书面理由。发现清单按严重程度排序呈报用户。
85
-
86
- ## 产出:差异单(交用户拍板的唯一格式)
87
-
88
- 差异单合并两个来源:/web-design-guidelines 扫描发现项 + Geist 实测差异。每条四栏:**Geist 实测值(或 guideline 条目) | 我们现值 | 层级判定(跟/不跟 + 理由) | 建议**。
89
- 拍板通过后,每组件独立 commit(混合文件按 hunk 切分),中文提交说明含:问题/思路/验证结果。
90
-
91
- ## 红线
92
-
93
- - 未呈差异单就把改动落库 = 违规;批量处理、跳过用户逐件拍板 = 违规——即使该件此前磨过。
94
- - "check 太慢先跳过"、"截图看着一样" = 未验证;跑完门槛再说完成。