docs(skills): design-handoff 设计稿 + spec + .gitignore 更新
- mp-11-doctor-core 设计交付包(截图 + tokens) - mp-13/mp-14 新原型 HTML - design-handoff skill 设计规格文档 - .gitignore 排除 dist-h5/test-results/uploads 等构建产物
This commit is contained in:
992
docs/superpowers/specs/2026-05-17-design-handoff-skill-design.md
Normal file
992
docs/superpowers/specs/2026-05-17-design-handoff-skill-design.md
Normal file
@@ -0,0 +1,992 @@
|
||||
# design-handoff Skill 设计规格
|
||||
|
||||
> 日期: 2026-05-17 | 状态: 草案
|
||||
> 问题: HTML 原型到实际实现的翻译层缺失,新会话 LLM 无法高保真还原原型设计
|
||||
> 方案: 结构化交付包(截图 + SPEC.md + Token 映射)
|
||||
|
||||
## 目录
|
||||
|
||||
1. **背景与问题** — 现状分析、核心矛盾、解决目标
|
||||
2. **Skill 架构** — 触发方式、输入输出、核心流程
|
||||
3. **SPEC.md 核心格式** — 页面结构、组件映射、交互规格的文档模板
|
||||
4. **Token 映射系统** — 混合匹配算法、配置文件、产出格式
|
||||
5. **截图提取与交互推断** — Playwright 截图、交互规则引擎
|
||||
6. **多平台支持与集成** — 多平台映射、huashu-design 协作、Prompt 模板
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与问题
|
||||
|
||||
### 1.1 现状
|
||||
|
||||
项目使用 `huashu-design` skill 产出 HTML 原型稿(React inline styles + 设计 Token 对象 `T`)。目前已有 **20 个原型文件**(`docs/design/mp-*.html`),覆盖小程序访客端、患者端、医生端全部页面。
|
||||
|
||||
实施时的工作流是:
|
||||
|
||||
1. **会话 A**:调用 huashu-design → 选定设计风格 → 扩展到所有页面原型
|
||||
2. **会话 B**(新会话):拿 HTML 原型实现 Taro/React 组件代码
|
||||
|
||||
### 1.2 核心矛盾
|
||||
|
||||
会话 B 的 LLM 与会话 A 完全隔离,丢失了所有设计上下文。三层翻译丢失:
|
||||
|
||||
| 丢失层 | 原因 | 表现 |
|
||||
|--------|------|------|
|
||||
| **视觉丢失** | LLM 读 HTML 源码,看不到渲染结果 | 颜色、字号、间距与原型不一致 |
|
||||
| **Token 断裂** | 原型用 `#C4623A`,代码用 `var(--tk-pri)` | 映射关系丢失,硬编码泛滥 |
|
||||
| **组件错配** | 原型全是 `<div style={...}>`,代码要用组件库 | 应复用 ContentCard/PageShell 但没复用 |
|
||||
| **交互缺失** | 原型暗示的按压反馈、滑动、状态切换无文档化 | 交互行为被忽略 |
|
||||
|
||||
### 1.3 解决目标
|
||||
|
||||
创建 `design-handoff` skill,在 HTML 原型完成后自动产出**结构化交付包**,让新会话的 LLM 能:
|
||||
|
||||
- **看到**原型渲染效果(截图)
|
||||
- **知道**每个值对应哪个项目 Token(映射表)
|
||||
- **选用**正确的现有组件(组件映射)
|
||||
- **实现**原型暗示的交互(交互规格)
|
||||
|
||||
---
|
||||
|
||||
## 2. Skill 架构
|
||||
|
||||
### 2.1 触发方式
|
||||
|
||||
```bash
|
||||
# 单个原型
|
||||
/design-handoff docs/design/mp-00-visitor.html
|
||||
|
||||
# 批量处理
|
||||
/design-handoff docs/design/mp-*.html
|
||||
|
||||
# 指定平台
|
||||
/design-handoff docs/design/mp-00-visitor.html --platform web
|
||||
```
|
||||
|
||||
**触发词**:`design-handoff`、`设计交付`、`handoff`
|
||||
|
||||
### 2.2 输入
|
||||
|
||||
| 参数 | 必需 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| HTML 文件路径 | 是 | — | 原型文件路径,支持 glob |
|
||||
| `--platform` | 否 | `miniprogram` | 目标平台:miniprogram / web / h5 |
|
||||
| `--tokens` | 否 | `.design/tokens.yml` | Token 映射配置文件路径 |
|
||||
|
||||
### 2.3 输出目录结构
|
||||
|
||||
```
|
||||
docs/design/
|
||||
├── mp-00-visitor.html # 原始原型(不修改)
|
||||
├── mp-00-visitor/ # 交付包目录
|
||||
│ ├── SPEC.md # 主规格文件(LLM 消费入口)
|
||||
│ ├── screenshots/ # 截图目录
|
||||
│ │ ├── home.png
|
||||
│ │ ├── home-slide-1.png
|
||||
│ │ ├── home-slide-2.png
|
||||
│ │ ├── home-slide-3.png
|
||||
│ │ └── profile.png
|
||||
│ ├── tokens.json # Token 映射表(机器可校验)
|
||||
│ └── META.yml # 元数据
|
||||
├── mp-01-login/
|
||||
│ └── ...
|
||||
└── .design/ # 全局配置(项目级)
|
||||
└── tokens.yml # Token 映射配置
|
||||
```
|
||||
|
||||
### 2.4 核心流程
|
||||
|
||||
```
|
||||
输入: HTML 原型文件
|
||||
│
|
||||
├─ Step 1: 解析 HTML ──────────────────────┐
|
||||
│ 提取 T 对象(Token 引用) │
|
||||
│ 提取组件树(页面结构) │
|
||||
│ 识别 IosFrame 实例(截图目标) │
|
||||
│ │
|
||||
├─ Step 2: 截图提取 ───────────────────────┤
|
||||
│ Playwright 打开 HTML │
|
||||
│ 逐 IosFrame 截图 │
|
||||
│ 裁剪设备框,保留屏幕内容 │
|
||||
│ │
|
||||
├─ Step 3: Token 匹配 ─────────────────────┤
|
||||
│ 优先级 1: 别名直查(aliases) │
|
||||
│ 优先级 2: 值精确匹配 │
|
||||
│ 优先级 3: 色彩模糊匹配(ΔE < 3) │
|
||||
│ 产出 tokens.json │
|
||||
│ │
|
||||
├─ Step 4: 组件映射 ───────────────────────┤
|
||||
│ 原型元素 → 项目组件库匹配 │
|
||||
│ 标记需新建的组件 │
|
||||
│ │
|
||||
├─ Step 5: 交互推断 ───────────────────────┤
|
||||
│ DOM 模式 → 交互规则匹配 │
|
||||
│ 高/中/低置信度分级 │
|
||||
│ │
|
||||
└─ Step 6: 组装 SPEC.md ───────────────────┘
|
||||
截图 + 结构 + Token + 组件 + 交互
|
||||
→ 单一规格文档
|
||||
```
|
||||
|
||||
### 2.5 Skill 文件清单
|
||||
|
||||
```
|
||||
.claude/skills/design-handoff/
|
||||
├── SKILL.md # Skill 入口
|
||||
├── scripts/
|
||||
│ ├── extract-screenshots.mjs # Playwright 截图
|
||||
│ ├── parse-prototype.mjs # HTML 解析(T 对象 + 组件树)
|
||||
│ ├── match-tokens.mjs # Token 三层匹配
|
||||
│ └── infer-interactions.mjs # 交互推断规则引擎
|
||||
├── templates/
|
||||
│ └── spec-template.md # SPEC.md 模板
|
||||
├── defaults/
|
||||
│ └── tokens.yml # 初始 Token 配置
|
||||
└── rules/
|
||||
└── interaction-rules.yml # 交互推断规则表
|
||||
```
|
||||
|
||||
**前置依赖**:
|
||||
- Node.js ≥ 18(运行 .mjs 脚本)
|
||||
- Playwright(截图提取;huashu-design 已依赖,无需额外安装)
|
||||
- 项目需包含 `styles/tokens.scss` 或等效的 Token 定义文件
|
||||
|
||||
---
|
||||
|
||||
## 3. SPEC.md 核心格式
|
||||
|
||||
SPEC.md 是 LLM 在新会话里消费的**唯一入口文件**。设计原则:
|
||||
|
||||
- **自包含**:截图通过 markdown 图片引用内嵌,一个文件看全貌
|
||||
- **结构化**:六个固定章节,LLM 可按需定位
|
||||
- **可操作**:每个值都映射到项目 Token 或组件,不含模糊描述
|
||||
|
||||
### 3.1 文档结构(六个固定章节)
|
||||
|
||||
```markdown
|
||||
# {页面名称} 设计规格
|
||||
|
||||
> 来源: {原型文件名} | 平台: {platform} | 页面数: {n}
|
||||
|
||||
## 页面索引
|
||||
(截图缩略图 + 路由映射表)
|
||||
|
||||
## 一、Token 映射
|
||||
(原型值 → 项目 Token,含匹配状态标记)
|
||||
|
||||
## 二、页面结构
|
||||
(逐页:截图 + 布局层级描述 + 尺寸标注)
|
||||
|
||||
## 三、组件映射
|
||||
(原型元素 → 项目组件库组件,含来源路径)
|
||||
|
||||
## 四、交互规格
|
||||
(元素 × 交互类型 × 触发 × 反馈 × 备注)
|
||||
|
||||
## 五、状态变体
|
||||
(加载中 / 空数据 / 错误 / 已登录等边界状态)
|
||||
|
||||
## 六、样式清单
|
||||
(间距 / 字号 / 圆角 / 阴影的 Token 汇总)
|
||||
```
|
||||
|
||||
### 3.2 页面索引
|
||||
|
||||
用表格关联截图与路由,LLM 一眼定位:
|
||||
|
||||
```markdown
|
||||
## 页面索引
|
||||
|
||||
| 页面 | 截图 | 路由 |
|
||||
|------|------|------|
|
||||
| 访客首页 |  | pages/index/index |
|
||||
| 访客"我的" |  | pages/profile/index |
|
||||
```
|
||||
|
||||
### 3.3 Token 映射章节
|
||||
|
||||
三级状态标记,LLM 知道哪些可以直接用:
|
||||
|
||||
| 标记 | 含义 | LLM 行为 |
|
||||
|------|------|---------|
|
||||
| ✅ confirmed | 已确认映射 | 直接使用对应 Token |
|
||||
| ⚠️ pending | 模糊匹配,待确认 | 使用但需人工复核 |
|
||||
| ❌ unmatched | 无匹配 | 硬编码或新建 Token |
|
||||
|
||||
```markdown
|
||||
## 一、Token 映射
|
||||
|
||||
| 原型值 | 项目 Token | 状态 |
|
||||
|--------|-----------|------|
|
||||
| #C4623A (T.pri) | var(--tk-pri) | ✅ |
|
||||
| #F5F0EB (T.bg) | var(--tk-bg-base) | ✅ |
|
||||
| 32px (轮播标题) | var(--tk-font-h1) | ⚠️ 待确认(28→32 不匹配) |
|
||||
| serif 字体 | — | ❌ 无 Token,硬编码 font-family |
|
||||
```
|
||||
|
||||
### 3.4 页面结构章节
|
||||
|
||||
每个页面一个子节,**以截图开头**,紧跟布局层级:
|
||||
|
||||
```markdown
|
||||
### 2.1 访客首页
|
||||
|
||||

|
||||
|
||||
布局层级(从上到下):
|
||||
|
||||
1. **轮播区域** (height: 280px)
|
||||
- 容器: `<Swiper>` (Taro),autoplay + circular
|
||||
- 3 张品牌轮播:渐变背景 + 装饰圆 + 标题文案 + 指示点
|
||||
- 指示点: 活跃 24×4 白色,非活跃 8×4 半透明白
|
||||
|
||||
2. **健康资讯** (padding: 20px → var(--tk-gap-lg))
|
||||
- 标题行: SectionTitle,serif 18px bold + "更多›"
|
||||
- 文章卡片 ×2: ContentCard variant="default"
|
||||
- 左侧配图 110px 宽,右侧标题 15px + 摘要 13px
|
||||
|
||||
3. **登录引导** (padding: 28px 20px 40px)
|
||||
- CTA: PrimaryButton size="large",阴影 var(--tk-shadow-btn)
|
||||
```
|
||||
|
||||
每个元素标注:**用什么组件** + **尺寸/间距的 Token 引用** + **视觉特征**。
|
||||
|
||||
### 3.5 组件映射章节
|
||||
|
||||
表格形式,明确"用哪个组件":
|
||||
|
||||
```markdown
|
||||
## 三、组件映射
|
||||
|
||||
| 原型元素 | 推荐组件 | 来源 | 备注 |
|
||||
|----------|---------|------|------|
|
||||
| 文章卡片 | ContentCard | @components/ui/ContentCard | variant="default" |
|
||||
| 区块标题 | SectionTitle | @components/ui/SectionTitle | action="更多›" |
|
||||
| CTA 按钮 | PrimaryButton | @components/ui/PrimaryButton | size="large" |
|
||||
| 页面容器 | PageShell | @components/ui/PageShell | padding="md" safeBottom |
|
||||
| 功能卡片 | — | 需新建 | 3 列等宽圆形图标卡片 |
|
||||
| 轮播 | Swiper | @tarojs/components | autoplay circular |
|
||||
| TabBar | Taro TabBar | 框架配置 | 原型不实现 |
|
||||
```
|
||||
|
||||
来源列使用 `@components/` 前缀,LLM 可直接 `import`。"需新建"标记让 LLM 知道这个需要从头写。
|
||||
|
||||
#### 组件映射推断规则(H4 修复)
|
||||
|
||||
组件映射通过 DOM 结构特征推断,规则定义在 skill 中:
|
||||
|
||||
| 原型 DOM 特征 | 推断组件 | 匹配依据 |
|
||||
|--------------|---------|---------|
|
||||
| 容器 + borderRadius + boxShadow + 内含标题+正文 | ContentCard | 卡片结构 |
|
||||
| 容器 + 左侧竖线装饰 + 标题文本 + 可选右侧链接 | SectionTitle | 区块标题结构 |
|
||||
| 按钮 + 主色背景 + 白色文字 + 高度 44-56px | PrimaryButton | CTA 按钮特征 |
|
||||
| 按钮 + 边框 + 透明背景 | SecondaryButton | 次按钮特征 |
|
||||
| 页面最外层容器 + padding + 滚动 | PageShell | 页面壳结构 |
|
||||
| 圆形小容器 + 内含数字/文字 + 配色背景 | StatusTag | 标签/徽章 |
|
||||
| 列表行 + 左图标 + 中间双行文字 + 右箭头 | ListItem | 列表项结构 |
|
||||
| 未匹配上述任何规则 | — (需新建) | 标记为"需新建" |
|
||||
|
||||
推断失败时默认标记为"需新建",不强制匹配错误组件。
|
||||
|
||||
### 3.6 交互规格章节
|
||||
|
||||
五列表格,覆盖触发 → 反馈 → 生命周期:
|
||||
|
||||
```markdown
|
||||
## 四、交互规格
|
||||
|
||||
| 元素 | 交互 | 触发 | 反馈 | 备注 |
|
||||
|------|------|------|------|------|
|
||||
| 轮播 | 自动播放+滑动 | 页面显示 | 3s 切换 | useDidShow 启动,useDidHide 暂停 |
|
||||
| 文章卡片 | 点击跳转 | onPress | activeFeedback="bg" | 跳转文章详情 |
|
||||
| CTA 按钮 | 点击登录 | onPress | loading 态 | 跳转登录页 |
|
||||
| 功能卡片 | 点击跳转 | onPress | opacity 反馈 | 各卡跳不同服务页 |
|
||||
| 资讯区域 | 下拉刷新 | onPullDownRefresh | — | 无数据时显示功能引导 |
|
||||
```
|
||||
|
||||
### 3.7 状态变体章节
|
||||
|
||||
覆盖边界场景,避免实现时遗漏:
|
||||
|
||||
```markdown
|
||||
## 五、状态变体
|
||||
|
||||
- **无数据**: 资讯为空 → 显示功能引导卡片(3 个固定入口)
|
||||
- **加载中**: LoadingCard layout="card" count=2
|
||||
- **已登录**: 自动跳转 HomeDashboard(不由本页处理)
|
||||
- **网络错误**: ErrorState + onRetry 重新加载
|
||||
```
|
||||
|
||||
### 3.8 样式清单章节
|
||||
|
||||
Token 维度的汇总,便于 LLM 快速查表:
|
||||
|
||||
```markdown
|
||||
## 六、样式清单
|
||||
|
||||
间距: 页面 padding 20px (--tk-gap-lg), 卡片 padding 16px (--tk-gap-md)
|
||||
字号: 标题 18px (--tk-font-nav), 正文 15px (硬编码), 摘要 13px (--tk-font-cap)
|
||||
圆角: 卡片 16px (--tk-card-radius), 按钮 14px (--tk-radius-lg)
|
||||
阴影: 卡片 (--tk-shadow-sm), 按钮 (--tk-shadow-btn)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Token 映射系统
|
||||
|
||||
### 4.1 全局配置文件
|
||||
|
||||
项目级配置 `.design/tokens.yml`,所有原型共享。**首次运行自动生成**,从项目代码库扫描:
|
||||
|
||||
- `styles/tokens.scss` → CSS 运行时 Token(`--tk-*`)
|
||||
- `styles/variables.scss` → SCSS 编译时变量(`$pri`, `$r` 等)
|
||||
|
||||
生成后可手动微调,后续运行直接读取。
|
||||
|
||||
配置文件结构分为五个 Token 类别 + 一个别名映射区:
|
||||
|
||||
```yaml
|
||||
# .design/tokens.yml
|
||||
|
||||
version: 1
|
||||
updated: 2026-05-17
|
||||
|
||||
# 色彩 Token — 从 tokens.scss 中 --tk-pri 等提取
|
||||
colors:
|
||||
- token: --tk-pri
|
||||
value: "#C4623A"
|
||||
- token: --tk-pri-l
|
||||
value: "#F0DDD4"
|
||||
# ...
|
||||
|
||||
# 字号 Token — 从 tokens.scss 中 --tk-font-* 提取
|
||||
typography:
|
||||
- token: --tk-font-h1
|
||||
value: "28px"
|
||||
note: 页面标题 serif bold
|
||||
# ...
|
||||
|
||||
# 间距 Token — 从 tokens.scss 中 --tk-gap-* 提取
|
||||
spacing:
|
||||
- token: --tk-gap-md
|
||||
value: "16px"
|
||||
# ...
|
||||
|
||||
# 圆角 Token — 从 tokens.scss 中 --tk-card-radius 等提取
|
||||
radius:
|
||||
- token: --tk-card-radius
|
||||
value: "16px"
|
||||
# ...
|
||||
|
||||
# 阴影 Token — 从 tokens.scss 中 --tk-shadow-* 提取
|
||||
shadow:
|
||||
- token: --tk-shadow-sm
|
||||
value: "0 1px 4px rgba(45,42,38,0.04)"
|
||||
# ...
|
||||
|
||||
# 原型 Token 别名 — 手动或自动积累的映射
|
||||
aliases:
|
||||
prototype_keys:
|
||||
T.pri: --tk-pri
|
||||
T.priL: --tk-pri-l
|
||||
T.bg: --tk-bg-base
|
||||
T.card: --tk-bg-card
|
||||
T.r: --tk-card-radius
|
||||
T.rSm: --tk-radius-sm
|
||||
# ...
|
||||
```
|
||||
|
||||
### 4.2 三层匹配算法
|
||||
|
||||
从原型 HTML 中提取的每个 Token 引用,按以下优先级匹配:
|
||||
|
||||
```
|
||||
优先级 1: 别名直查
|
||||
原型中 T.pri → 查 aliases.prototype_keys → --tk-pri ✅
|
||||
预估覆盖率: 最高(已有 aliases 的常用 Token)
|
||||
特点: 零歧义,直接确认
|
||||
|
||||
优先级 2: 值精确匹配(按 CSS 属性上下文消歧)
|
||||
原型中 #C4623A → 仅在 colors 类别中查找 → --tk-pri ✅
|
||||
原型中 borderRadius: 16px → 仅在 radius 类别中查找 → --tk-card-radius ✅
|
||||
原型中 padding: 16px → 仅在 spacing 类别中查找 → --tk-gap-md ✅
|
||||
预估覆盖率: 中等(值恰好一致的 Token)
|
||||
特点: 按 CSS 属性类别过滤,避免同值多 Token 歧义
|
||||
|
||||
优先级 3: 色彩模糊匹配(仅颜色类)
|
||||
原型中 #C4623B → 与 colors 中各值算色差 ΔE
|
||||
ΔE < 3 → 视为近似色 → ⚠️ pending 待确认
|
||||
预估覆盖率: 较低(设计迭代中微调的色值)
|
||||
特点: 仅限色彩,必须人工确认
|
||||
|
||||
未匹配: ❌ unmatched
|
||||
无任何匹配结果
|
||||
需人工决定: 新建 Token / 硬编码 / 忽略
|
||||
```
|
||||
|
||||
**值归一化规则(M4)**:
|
||||
- `"16px"` 和 `"16"` 视为相同(去除 px 后缀比较数值)
|
||||
- `"#C4623A"` 和 `"c4623a"` 视为相同(大小写不敏感)
|
||||
- 不支持 `rem`/`em` 转换,遇此单位标记为 pending
|
||||
|
||||
### 4.3 首次运行流程
|
||||
|
||||
```
|
||||
/design-handoff docs/design/mp-00-visitor.html
|
||||
|
||||
Step 0: 格式校验(C3 弹性检查)
|
||||
- 检查文件是否为合法 HTML
|
||||
- 检查是否包含 React/Babel 脚本标签(huashu-design 特征)
|
||||
- 检查是否包含 T 对象定义(const T = { ... })
|
||||
- 任何检查失败 → 输出清晰错误信息并终止
|
||||
- 缺少 T 对象: "未检测到设计 Token 对象 (T),请确认是否为 huashu-design 产物"
|
||||
- 缺少 React: "未检测到 React 渲染环境,无法解析原型"
|
||||
|
||||
Step 1: 检查 .design/tokens.yml
|
||||
→ 不存在: 自动扫描项目 SCSS 文件生成
|
||||
- 解析 tokens.scss → 提取所有 --tk-* 变量及值
|
||||
- 解析 variables.scss → 提取所有 $ 变量及值
|
||||
- 生成 .design/tokens.yml
|
||||
→ 存在: 直接使用
|
||||
|
||||
Step 2: 解析 HTML 中的 Token(C2 提取方式)
|
||||
方式: 静态解析 T 对象定义 + 内联 style 值
|
||||
- 用正则匹配 "const T = { ... }" 提取完整对象定义
|
||||
- 解析 T 对象的每个 key-value 对(如 T.pri: '#C4623A')
|
||||
- 扫描内联 style 中的硬编码值: fontSize, padding, borderRadius 等
|
||||
- 不依赖 React 运行时,纯文本解析
|
||||
- 值归一化规则: "16px" 和 "16" 视为相同; 不支持 rem/em 转换
|
||||
|
||||
Step 3: 三层匹配
|
||||
- 匹配时按 CSS 属性上下文消歧(H1 修复)
|
||||
- borderRadius / radius 相关属性 → 只匹配 radius 类别 Token
|
||||
- padding / margin / gap → 只匹配 spacing 类别 Token
|
||||
- fontSize / fontWeight → 只匹配 typography 类别 Token
|
||||
- color / background → 匹配 colors 类别 Token
|
||||
- 如果同值多 Token 且无属性上下文 → 标记为 ⚠️ pending
|
||||
- 产出 tokens.json
|
||||
|
||||
Step 4: 处理未确认项
|
||||
- pending + unmatched 项在终端列表展示
|
||||
- 用户确认后更新 aliases
|
||||
- 后续原型自动复用已确认的映射
|
||||
```
|
||||
|
||||
### 4.3.1 .design/ 目录管理
|
||||
|
||||
- `.design/` 目录**提交到 git**(tokens.yml 是团队共享的项目级配置)
|
||||
- `.design/config.yml`(个人偏好配置,如 auto_handoff)可加入 `.gitignore`
|
||||
- 首次生成 tokens.yml 后建议立即提交,作为团队基线
|
||||
|
||||
### 4.4 产出文件 `tokens.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"source": "mp-00-visitor.html",
|
||||
"platform": "miniprogram",
|
||||
"generated": "2026-05-17T10:30:00Z",
|
||||
"tokens": {
|
||||
"T.pri": {
|
||||
"value": "#C4623A",
|
||||
"mapped": "var(--tk-pri)",
|
||||
"match": "alias",
|
||||
"status": "confirmed"
|
||||
},
|
||||
"fontSize:32": {
|
||||
"value": "32px",
|
||||
"closest": "var(--tk-font-h1)",
|
||||
"closestValue": "28px",
|
||||
"match": "fuzzy",
|
||||
"status": "pending",
|
||||
"note": "原型 32px vs 最近 Token 28px (--tk-font-h1),差 4px"
|
||||
},
|
||||
"T.serif": {
|
||||
"value": "Georgia, 'Times New Roman', serif",
|
||||
"mapped": null,
|
||||
"match": "none",
|
||||
"status": "unmatched",
|
||||
"note": "项目无字体族 Token,需硬编码或新建"
|
||||
}
|
||||
},
|
||||
"summary": {
|
||||
"total": 18,
|
||||
"confirmed": 13,
|
||||
"pending": 2,
|
||||
"unmatched": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `value` | 原型中的原始值 |
|
||||
| `mapped` | 匹配到的项目 Token(null 表示未匹配) |
|
||||
| `match` | 匹配方式:alias / exact / fuzzy / none |
|
||||
| `status` | 状态:confirmed / pending / unmatched |
|
||||
| `note` | 补充说明(模糊匹配的差异原因等) |
|
||||
|
||||
### 4.5 映射积累效应
|
||||
|
||||
每次运行 `/design-handoff` 并确认 pending 项后,映射自动写入 `aliases`:
|
||||
|
||||
```
|
||||
第 1 次运行: 13 confirmed + 2 pending + 3 unmatched
|
||||
→ 确认 2 个 pending → aliases 新增 2 条
|
||||
第 2 次运行: 15 confirmed + 1 pending + 2 unmatched
|
||||
→ 确认 1 个 pending → aliases 新增 1 条
|
||||
第 N 次运行: 18 confirmed + 0 pending + 0 unmatched
|
||||
→ 全部自动匹配,无需人工干预
|
||||
```
|
||||
|
||||
随着项目推进,手动确认越来越少,最终趋近全自动。
|
||||
|
||||
---
|
||||
|
||||
## 5. 截图提取与交互推断
|
||||
|
||||
### 5.1 截图提取
|
||||
|
||||
#### 5.1.1 提取流程
|
||||
|
||||
使用 Playwright(huashu-design 已内置 `verify.py` 依赖 Playwright):
|
||||
|
||||
```
|
||||
1. Playwright 启动浏览器,打开 HTML 原型文件
|
||||
2. 等待 React + Babel 渲染完成(检测 #root 内有子元素)
|
||||
3. 遍历所有 IosFrame 实例,逐个截图
|
||||
4. 裁剪设备框(12px padding + 54px 状态栏 + 34px 底部指示器)
|
||||
5. 输出到 screenshots/ 目录
|
||||
```
|
||||
|
||||
#### 5.1.2 IosFrame 定位规则
|
||||
|
||||
原型 HTML 中 `screen-label` 和 `IosFrame` 是**兄弟节点**,不是父子关系:
|
||||
|
||||
```html
|
||||
<div class="screen-wrap">
|
||||
<span class="screen-label">访客首页 — 完整页</span> <!-- 兄弟节点 -->
|
||||
<IosFrame time="9:41" battery={85}> <!-- 兄弟节点 -->
|
||||
<GuestHome />
|
||||
</IosFrame>
|
||||
</div>
|
||||
```
|
||||
|
||||
定位方式:
|
||||
|
||||
```
|
||||
1. 查找所有 class="screen-wrap" 的容器
|
||||
2. 每个容器内:
|
||||
- <span class="screen-label"> 的文本 → 截图文件名
|
||||
- <IosFrame> 实例 → 截图目标
|
||||
3. 如果容器无 screen-label → 使用序号命名 (screen-1.png, screen-2.png)
|
||||
```
|
||||
|
||||
如果原型未使用 screen-wrap + IosFrame 结构,进入降级方案(见 5.1.4)。
|
||||
|
||||
#### 5.1.3 文件命名规则
|
||||
|
||||
| 规则 | 示例 |
|
||||
|------|------|
|
||||
| 使用 screen-label 文本 | `访客首页 — 完整页` → `home.png` |
|
||||
| 轮播编号保留 | `访客首页 — 轮播 1` → `home-slide-1.png` |
|
||||
| 中文翻译为英文简写 | `我的` → `profile`, `登录` → `login` |
|
||||
| 同名页面加序号 | `home-2.png`, `home-3.png` |
|
||||
|
||||
翻译映射表内置在 skill 中(覆盖项目已有的 20 个原型中出现的所有中文标签)。
|
||||
|
||||
#### 5.1.4 降级方案
|
||||
|
||||
当原型未使用 IosFrame 时(如早期部分原型或 Web 端原型):
|
||||
|
||||
1. **整页截图**:截取完整渲染页面
|
||||
2. **手动标注**:用户通过 `--crop "x,y,w,h"` 参数指定裁剪区域
|
||||
3. **CSS 选择器**:通过 `--selector ".screen-content"` 指定截图目标
|
||||
|
||||
### 5.2 交互推断
|
||||
|
||||
#### 5.2.1 推断策略
|
||||
|
||||
从原型 HTML 的 DOM 结构推断交互行为。不做运行时分析,纯静态代码模式匹配。
|
||||
|
||||
推断规则存储在 `rules/interaction-rules.yml` 中,按 DOM 模式匹配。
|
||||
|
||||
#### 5.2.2 内置规则表
|
||||
|
||||
```yaml
|
||||
# rules/interaction-rules.yml
|
||||
|
||||
rules:
|
||||
- id: swiper-autoplay
|
||||
name: 自动轮播 + 手动滑动
|
||||
patterns:
|
||||
- "连续多个子元素含渐变背景 + 底部指示点(宽窄交替)"
|
||||
- "Swiper / swiper / carousel 关键词"
|
||||
infer:
|
||||
interaction: "自动播放 + 手动滑动"
|
||||
trigger: "页面显示"
|
||||
feedback: "3s 间隔自动切换"
|
||||
lifecycle: "useDidShow 启动,useDidHide 暂停"
|
||||
confidence: high
|
||||
|
||||
- id: card-tap
|
||||
name: 卡片点击跳转
|
||||
patterns:
|
||||
- "卡片容器含标题+摘要+配图"
|
||||
- "onClick / onPress 事件"
|
||||
infer:
|
||||
interaction: "点击跳转"
|
||||
trigger: "onPress"
|
||||
feedback: "activeFeedback 按组件类型自动选择"
|
||||
confidence: high
|
||||
|
||||
- id: form-submit
|
||||
name: 表单提交
|
||||
patterns:
|
||||
- "输入框 + 按钮在同一容器内"
|
||||
- "input / textarea + button 组合"
|
||||
infer:
|
||||
interaction: "表单提交"
|
||||
trigger: "onPress(按钮)"
|
||||
feedback: "loading 态 + 禁用重复提交 + 错误提示"
|
||||
confidence: high
|
||||
|
||||
- id: list-scroll
|
||||
name: 列表滚动加载
|
||||
patterns:
|
||||
- "列表容器含多个重复结构子元素"
|
||||
- "overflow: auto / scroll"
|
||||
- "3 个以上同类卡片连续排列"
|
||||
infer:
|
||||
interaction: "下拉刷新 + 上拉加载"
|
||||
trigger: "onPullDownRefresh / onReachBottom"
|
||||
feedback: "分页加载"
|
||||
confidence: medium
|
||||
|
||||
- id: tab-switch
|
||||
name: 标签页切换
|
||||
patterns:
|
||||
- "多个平行区域 + 选中态样式差异"
|
||||
- "tab / segment / filter 关键词"
|
||||
- "一组按钮样元素,其中一个高亮"
|
||||
infer:
|
||||
interaction: "标签页切换"
|
||||
trigger: "onPress(tab 项)"
|
||||
feedback: "选中态样式切换 + 内容区切换"
|
||||
confidence: high
|
||||
|
||||
- id: static-decoration
|
||||
name: 静态装饰
|
||||
patterns:
|
||||
- "渐变背景 + 装饰圆/波浪/几何图形"
|
||||
- "无事件绑定"
|
||||
- "position: absolute + opacity < 1"
|
||||
infer:
|
||||
interaction: "无(纯装饰)"
|
||||
trigger: "—"
|
||||
feedback: "—"
|
||||
confidence: high
|
||||
|
||||
- id: login-cta
|
||||
name: 登录/注册 CTA
|
||||
patterns:
|
||||
- "按钮含 '登录' / '注册' / '立即' 文案"
|
||||
- "主色按钮 + 阴影"
|
||||
infer:
|
||||
interaction: "点击触发登录流程"
|
||||
trigger: "onPress"
|
||||
feedback: "PrimaryButton loading 态"
|
||||
guard: "GuestGuard 拦截未登录态"
|
||||
confidence: high
|
||||
|
||||
- id: empty-fallback
|
||||
name: 空数据降级
|
||||
patterns:
|
||||
- "条件渲染: data.length > 0 ? ... : ..."
|
||||
- "占位符文字 '暂无' / '空'"
|
||||
- "硬编码的固定内容(非 API 数据)"
|
||||
infer:
|
||||
interaction: "无数据时显示替代内容"
|
||||
trigger: "数据加载完成且为空"
|
||||
feedback: "EmptyState 组件或固定引导卡片"
|
||||
confidence: medium
|
||||
```
|
||||
|
||||
#### 5.2.3 置信度分级
|
||||
|
||||
| 级别 | 阈值 | 处理方式 |
|
||||
|------|------|---------|
|
||||
| **high** (≥80%) | 结构明确,模式唯一 | 直接写入 SPEC.md 交互规格表 |
|
||||
| **medium** (50-80%) | 结构合理但有歧义 | 写入交互规格表,标注 `⚠️ 待确认` |
|
||||
| **low** (<50%) | 模式模糊,可能是静态 | 不写入交互规格,末尾单独列出 `## 待人工补充的交互` |
|
||||
|
||||
#### 5.2.4 推断结果示例
|
||||
|
||||
对 `mp-00-visitor.html` 的推断输出:
|
||||
|
||||
| 元素 | 推断交互 | 置信度 | 来源规则 |
|
||||
|------|---------|--------|---------|
|
||||
| 轮播区域 | 自动播放 3s + 手动滑动 | high | swiper-autoplay |
|
||||
| 文章卡片 ×2 | 点击跳转详情 | high | card-tap |
|
||||
| "更多›" 链接 | 点击跳转列表页 | high | card-tap |
|
||||
| 功能卡片 ×3 | 点击跳转各服务页 | high | card-tap |
|
||||
| CTA"立即登录" | 点击触发登录 | high | login-cta |
|
||||
| TabBar 占位 | 无(小程序原生处理) | high | static-decoration |
|
||||
| 资讯为空降级 | 显示功能引导卡片 | medium | empty-fallback |
|
||||
|
||||
### 5.3 截图与规格的关联机制
|
||||
|
||||
SPEC.md 中每个页面结构块都以对应截图开头,形成视觉-规格一一对应:
|
||||
|
||||
```markdown
|
||||
### 2.1 访客首页
|
||||
|
||||
 ← LLM 读到此处时看到截图
|
||||
|
||||
布局层级(从上到下): ← 对照截图理解每层
|
||||
1. 轮播区域 (height: 280px) ... ← 截图中顶部渐变区域
|
||||
2. 健康资讯 (padding: 20px) ... ← 截图中卡片列表区域
|
||||
3. 登录引导 ... ← 截图中底部 CTA 按钮
|
||||
```
|
||||
|
||||
LLM 无需在多个文件间跳转。**一个 SPEC.md = 视觉 + 结构 + 映射 + 交互的全景图**。
|
||||
|
||||
---
|
||||
|
||||
## 6. 多平台支持与集成
|
||||
|
||||
### 6.1 多平台 Token 映射
|
||||
|
||||
`tokens.yml` 中的每个 Token 支持按平台指定不同变量名和引用方式:
|
||||
|
||||
```yaml
|
||||
# 单平台通用(大多数 Token,值和引用方式相同)
|
||||
- token: --tk-pri
|
||||
value: "#C4623A"
|
||||
# 无 platforms 字段 → 所有平台统一用 var(--tk-pri)
|
||||
|
||||
# 多平台差异映射(值相同,引用方式不同)
|
||||
- token: --tk-pri
|
||||
value: "#C4623A"
|
||||
platforms:
|
||||
miniprogram: "var(--tk-pri)"
|
||||
web: "token('color.primary')" # Ant Design Token 系统
|
||||
h5: "var(--color-primary)" # H5 独立 CSS 变量体系
|
||||
```
|
||||
|
||||
运行时按 `--platform` 参数展开对应平台的映射:
|
||||
|
||||
```bash
|
||||
# 默认展开 miniprogram 映射
|
||||
/design-handoff mp-00-visitor.html
|
||||
|
||||
# 展开 web 映射
|
||||
/design-handoff mp-00-visitor.html --platform web
|
||||
```
|
||||
|
||||
SPEC.md 和 tokens.json 中仅包含目标平台的映射,不混淆。
|
||||
|
||||
### 6.2 多平台组件映射
|
||||
|
||||
可选配置文件 `.design/components.yml`,定义各平台的组件对应关系:
|
||||
|
||||
```yaml
|
||||
# .design/components.yml — 自动扫描生成,可手动调整
|
||||
|
||||
components:
|
||||
ContentCard:
|
||||
miniprogram:
|
||||
import: "@components/ui/ContentCard"
|
||||
props: "variant, padding, margin, activeFeedback, onPress, className"
|
||||
web:
|
||||
import: "@app/components/ContentCard"
|
||||
props: "variant, padding, margin, onClick, className"
|
||||
|
||||
PrimaryButton:
|
||||
miniprogram:
|
||||
import: "@components/ui/PrimaryButton"
|
||||
props: "children, onClick, disabled, loading, size"
|
||||
web:
|
||||
import: "antd/es/button"
|
||||
props: "children, onClick, disabled, loading, type='primary'"
|
||||
|
||||
PageShell:
|
||||
miniprogram:
|
||||
import: "@components/ui/PageShell"
|
||||
props: "padding, safeBottom, scroll, className"
|
||||
web:
|
||||
import: "@app/layouts/PageLayout"
|
||||
props: "padding, children, className"
|
||||
|
||||
Swiper:
|
||||
miniprogram:
|
||||
import: "@tarojs/components"
|
||||
tag: "Swiper"
|
||||
web:
|
||||
import: "antd/es/carousel"
|
||||
tag: "Carousel"
|
||||
|
||||
TabBar:
|
||||
miniprogram:
|
||||
type: "framework-config"
|
||||
note: "小程序原生 TabBar,在 app.config.ts 配置"
|
||||
web:
|
||||
import: "@app/components/AppTabBar"
|
||||
note: "Web 端自定义 TabBar 组件"
|
||||
```
|
||||
|
||||
**扫描生成规则**:
|
||||
- `miniprogram` → 扫描 `apps/miniprogram/src/components/ui/` 目录
|
||||
- `web` → 扫描 `apps/desktop/src/components/` 或对应 Web 目录
|
||||
- 只在对应平台目录存在时才生成映射
|
||||
|
||||
### 6.3 huashu-design 协作方式
|
||||
|
||||
两种集成方式,**不修改 huashu-design 本身**:
|
||||
|
||||
#### 方式 A:串行调用(默认)
|
||||
|
||||
```
|
||||
会话 1: /huashu-design → 原型 HTML(设计阶段)
|
||||
会话 2: /design-handoff mp-00-visitor.html → 交付包(规格化)
|
||||
会话 3: "按 SPEC.md 实现" → Taro/React 代码(实施阶段)
|
||||
```
|
||||
|
||||
三个阶段各自独立,松耦合。用户按需调用。
|
||||
|
||||
#### 方式 B:自动追加(可选配置)
|
||||
|
||||
在 `.design/config.yml` 中开启:
|
||||
|
||||
```yaml
|
||||
# .design/config.yml
|
||||
auto_handoff: true
|
||||
```
|
||||
|
||||
开启后,huashu-design 完成原型写入时,skill 检测到 `auto_handoff: true`,自动提示:
|
||||
|
||||
```
|
||||
原型已生成: docs/design/mp-00-visitor.html
|
||||
检测到 auto_handoff 开启。是否自动生成设计交付包?
|
||||
→ 是: 立即运行 /design-handoff
|
||||
→ 否: 跳过,稍后手动调用
|
||||
```
|
||||
|
||||
### 6.4 实施 Prompt 模板
|
||||
|
||||
交付包生成完毕后,skill 在终端输出推荐 prompt,用户直接复制到新会话:
|
||||
|
||||
```
|
||||
📋 实施时复制以下 prompt 到新会话:
|
||||
|
||||
请按设计规格实现「访客首页」页面。
|
||||
|
||||
设计规格: docs/design/mp-00-visitor/SPEC.md
|
||||
Token 映射: docs/design/mp-00-visitor/tokens.json
|
||||
目标平台: miniprogram (Taro + React)
|
||||
组件库: apps/miniprogram/src/components/ui/
|
||||
样式 Token: apps/miniprogram/src/styles/tokens.scss
|
||||
|
||||
要求:
|
||||
1. 先阅读 SPEC.md 中的截图和规格
|
||||
2. 按组件映射表使用现有组件,不要用 div 堆砌
|
||||
3. 按 Token 映射表使用 CSS 变量,不要硬编码颜色值
|
||||
4. 按交互规格表实现所有交互行为
|
||||
5. 覆盖所有状态变体(加载/空数据/错误)
|
||||
```
|
||||
|
||||
注意:SPEC.md 中的截图路径为相对路径(`./screenshots/home.png`),LLM 应使用项目根目录的绝对路径读取(如 `docs/design/mp-00-visitor/screenshots/home.png`)。
|
||||
|
||||
#### 批处理索引(M3 修复)
|
||||
|
||||
批量运行时(`/design-handoff docs/design/mp-*.html`),完成后自动在 `docs/design/` 下生成 `INDEX.md`:
|
||||
|
||||
```markdown
|
||||
# 设计交付包索引
|
||||
|
||||
> 生成时间: 2026-05-17 | 原型数: 20
|
||||
|
||||
| 原型 | 交付包 | 页面数 | 未匹配 Token |
|
||||
|------|--------|--------|-------------|
|
||||
| mp-00-visitor | [SPEC](mp-00-visitor/SPEC.md) | 2 | 2 |
|
||||
| mp-01-login | [SPEC](mp-01-login/SPEC.md) | 2 | 1 |
|
||||
| ... | ... | ... | ... |
|
||||
```
|
||||
|
||||
LLM 可通过 `docs/design/INDEX.md` 定位所需的交付包。
|
||||
|
||||
### 6.5 META.yml 元数据
|
||||
|
||||
每个交付包的元数据文件:
|
||||
|
||||
```yaml
|
||||
# docs/design/mp-00-visitor/META.yml
|
||||
|
||||
source: mp-00-visitor.html
|
||||
platform: miniprogram
|
||||
generated: 2026-05-17T10:30:00Z
|
||||
generator: design-handoff v1
|
||||
|
||||
pages:
|
||||
- name: 访客首页
|
||||
screenshot: screenshots/home.png
|
||||
route: pages/index/index
|
||||
- name: 访客"我的"
|
||||
screenshot: screenshots/profile.png
|
||||
route: pages/profile/index
|
||||
|
||||
tokens:
|
||||
total: 18
|
||||
confirmed: 13
|
||||
pending: 2
|
||||
unmatched: 3
|
||||
|
||||
components:
|
||||
reused: 5 # 已有组件
|
||||
new: 1 # 需新建组件
|
||||
|
||||
interactions:
|
||||
total: 7
|
||||
high_confidence: 6
|
||||
medium_confidence: 1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 附录 A. 交互推断规则表
|
||||
|
||||
完整内置规则清单(见 §5.2.2),共 8 条:
|
||||
|
||||
| 规则 ID | 名称 | 置信度 | 匹配模式 |
|
||||
|---------|------|--------|---------|
|
||||
| swiper-autoplay | 自动轮播 + 手动滑动 | high | 渐变背景 + 指示点宽窄交替 |
|
||||
| card-tap | 卡片点击跳转 | high | 卡片容器 + 标题摘要配图 |
|
||||
| form-submit | 表单提交 | high | 输入框 + 按钮同容器 |
|
||||
| list-scroll | 列表滚动加载 | medium | 3+ 同类卡片连续排列 |
|
||||
| tab-switch | 标签页切换 | high | 平行区域 + 选中态差异 |
|
||||
| static-decoration | 静态装饰 | high | 渐变 + 装饰圆 + 无事件 |
|
||||
| login-cta | 登录/注册 CTA | high | 按钮含登录文案 + 主色 |
|
||||
| empty-fallback | 空数据降级 | medium | 条件渲染 / 占位符文字 |
|
||||
|
||||
扩展方式:在 `rules/interaction-rules.yml` 中追加新规则即可。
|
||||
|
||||
## 附录 B. Token 配置文件完整模板
|
||||
|
||||
见 §4.1 的 `.design/tokens.yml` 结构。首次运行时自动从以下文件扫描生成:
|
||||
|
||||
| 源文件 | 提取内容 |
|
||||
|--------|---------|
|
||||
| `styles/tokens.scss` | 所有 `--tk-*` CSS 变量 |
|
||||
| `styles/variables.scss` | 所有 `$` SCSS 变量 |
|
||||
| `styles/mixins.scss` | mixin 名称和用途(用于注释) |
|
||||
|
||||
## 附录 C. SPEC.md 完整示例(mp-00-visitor)
|
||||
|
||||
见 §3.2 - §3.8 的完整格式定义。以 mp-00-visitor 为实际案例,涵盖:
|
||||
|
||||
- 5 张截图(首页全貌 + 3 张轮播 + 我的页)
|
||||
- 15+ Token 映射(11 confirmed + 2 pending + 2 unmatched)
|
||||
- 7 个组件映射(5 复用 + 1 新建 + 1 框架配置)
|
||||
- 7 条交互规格(6 high + 1 medium)
|
||||
- 4 个状态变体(加载 / 空数据 / 已登录 / 错误)
|
||||
Reference in New Issue
Block a user