Files
hms/docs/superpowers/specs/2026-05-17-design-handoff-skill-design.md
iven 3aa71a94d2 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 等构建产物
2026-05-18 02:13:29 +08:00

993 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
## 页面索引
| 页面 | 截图 | 路由 |
|------|------|------|
| 访客首页 | ![home](./screenshots/home.png) | pages/index/index |
| 访客"我的" | ![profile](./screenshots/profile.png) | 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 访客首页
![首页全貌](./screenshots/home.png)
布局层级(从上到下):
1. **轮播区域** (height: 280px)
- 容器: `<Swiper>` (Taro)autoplay + circular
- 3 张品牌轮播:渐变背景 + 装饰圆 + 标题文案 + 指示点
- 指示点: 活跃 24×4 白色,非活跃 8×4 半透明白
2. **健康资讯** (padding: 20px → var(--tk-gap-lg))
- 标题行: SectionTitleserif 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 中的 TokenC2 提取方式)
方式: 静态解析 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` | 匹配到的项目 Tokennull 表示未匹配) |
| `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 提取流程
使用 Playwrighthuashu-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: "onPresstab 项)"
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 访客首页
![首页全貌](./screenshots/home.png) ← 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 个状态变体(加载 / 空数据 / 已登录 / 错误)