Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 192 additions & 0 deletions .agents/skills/frontend-craft/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
---
name: frontend-craft
description: 生成或修改前端界面时建立并维持统一的设计系统。当用户要求做前端页面/组件/样式、抱怨界面丑或乱或不一致、做前端技术选型与设计 Token、或需要整理迁移既有 CSS(样式膨胀、重复规则、颜色字号失控)时使用。提供两阶段工作流(先建 token/原语/lint/截图验收回路,再生成界面)、设计 Token 数值基准、原语组件模板、反模式清单、提示词模板与零依赖截图方法。不适用于纯后端工作;也不替代用户做审美与参照物的最终选择。
---

# 前端界面工程(frontend-craft)

## 目标

让 AI 产出的前端界面**一致、完整、可维护**,并且让"漂移"从一件无人察觉的事
变成一件会被机器发现的事。

**这个技能解决的不是"审美"问题,是"约束"问题。** 界面看起来乱,
绝大多数时候不是因为某个颜色选错了,而是因为**每一轮迭代都在重新发明一个值**。
一份真实的体检报告:某项目 1313 行 CSS 里只有 31 次 `var()`(约 97% 硬编码),
产出 7 个几乎一样的绿色、21 种圆角、19 种字号、24 种 rgba、3 个互相覆盖的 `:root` 块、
66 条重复声明。**单个按钮都不丑,叠在一起眼睛找不到节奏。**

所以做法不是"重新画得好看一点"(那会把同一类错误再犯一次),
而是**先把约束建起来,再在约束内生成**。

---

## 诚实的能力边界(先读,避免期待错位)

**这个技能能做到:**
- 消除颜色/字号/圆角/间距的失控(从"21 种圆角"到"3 种")
- 补齐 hover / focus / disabled / 空态 / 错态 / 加载态,不再遗漏
- 新会话不用重新解释规范(规范在仓库里,不在对话里)
- 从 mock 切真后端时改动极小(契约是真的)
- 让漂移可被机器发现,而不是靠人眼

**这个技能做不到:**
- **提升审美上限。** 参照物选得差,产出依然是"整洁的差品味"。
- **替代信息架构判断。** 把控件状态当正文写进页面,token 和 lint 都拦不住。
- **阻止用户自己用临时 prompt 破坏体系。** 技能管不住旁边那个人。

> 一句话:**这个技能把"很烂"提到"整洁统一",这是真实且巨大的提升。
> 从"整洁"到"优秀"那一步靠参照物和人的品味判断。**
> 不要向用户承诺超出这个范围的结果。

---

## 适用与不适用

**适用**:新项目前端从零搭建 / 既有界面整理迁移 / 用户抱怨"界面丑、乱、不一致" /
需要定义设计 Token / 需要建 lint 与验收回路 / 需要写前端相关的提示词。

**不适用**:纯后端或 CLI 工作 / 用户只想要一个一次性的静态页面(用不上这套)/
用户已明确有成熟设计系统且不希望改动。

---

## 工作流

### 0. 先判断当前处于哪个阶段

| 现象 | 阶段 | 去哪 |
|---|---|---|
| 项目没有 `tokens.css` / `DESIGN.md` | **阶段一(建约束)** | `references/bootstrap.md` |
| 有约束,要做一个新页面/组件 | **阶段二(生成)** | `references/build.md` |
| 已有界面但样式乱了,要整理 | **迁移** | `tokens-baseline.md` 第 4 节 |
| 需要对照截图检查 | **验收** | `references/screenshot-and-qa.md` |

**不要跳过阶段一直接生成界面。** 这是本技能最核心的一条。
理由见 `bootstrap.md` 开头 —— 简单说:直接生成时 AI 要做 200 个即兴决定,
其中 190 个没有依据,而这些决定会不可逆地叠加。

### 1. 阶段一:建设约束基础设施

产出 12 项(清单见 `bootstrap.md`),核心是这四类:

1. **具体数值** → `src/styles/tokens.css`(复制 `templates/tokens.css`,
只调正文基准字号、主色、中性色偏色三处)
2. **视觉契约** → `docs/design/DESIGN.md`(复制 `templates/DESIGN.md`)
—— 尤其是**参照物表**和**页面×状态矩阵**
3. **可执行检查** → `stylelint.config.mjs` + `scripts/audit-css.mjs`
4. **验收回路** → `scripts/capture.mjs` + `docs/design/qa-checklist.md`

**为什么要"可执行检查"这一层**:一句写在文档里的"禁止字面量颜色",
被违反时什么都不会发生。一条会 `exit 1` 的规则,被违反时你一定会知道。
**规则的价值不在于被写下来,而在于被违反时会发生什么。**

### 2. 阶段二:按契约生成界面

**顺序是铁律**:

```
灰块结构 → 空态 + 错态 → 加载态 → 正常态 → 边界态 → 截图验收
```

两个最关键的顺序决定:
- **空态和错态在正常态之前** —— 反过来的话,空态需要的引导按钮和居中容器
在正常态布局里没有位置,只能硬塞或草草了事。
**顺序反了 = 空态永远补不上。** 而且新用户第一次打开看到的就是空态。
- **灰块结构在最前面** —— 先填内容再调布局,等于在移动的靶子上画画。

细节见 `references/build.md`。

### 3. 迁移既有界面

**不要推翻重做**(那是把同一类错误再犯一次,还丢掉已完成的交互)。
按 `tokens-baseline.md` 第 4 节:取色 → 按频次归一 → 主色取现状中最后生效的那个
(不新造,避免"又换风格了"的反弹)→ 用 `audit-css.mjs` 记录迁移前后分数。

按频次归一(19 种字号 → 7 种,21 种圆角 → 3 种)会**立刻**让界面看起来整齐 ——
因为噪音消失了,而不是因为"变漂亮了"。这是投入产出比最高的一次改动。

### 4. 验收(每次改完必做)

```
截图 → 看图挑刺 → 改 → 再截图
```

- 截图:`node scripts/capture.mjs <url> shots --widths 375,1280`(输出目录是位置参数)
- 挑刺:对照 `templates/qa-checklist.md`,按 8 组**顺序**过(对齐优先于颜色)
- **必须真的调用读图工具看图。** 声称"我看到界面很整洁"但没读图,是编造。
- 反馈必须能直接翻译成一行 CSS 改动。**"看起来不够精致"是无效反馈**,
"工具栏三个控件高度是 28/36/32px"才是有效反馈。
- 更有效:开一个**全新的 subagent**,只给它截图和清单、不告诉它代码是谁写的。
它的上下文里没有"我这么写是有道理的"这笔账。见 `prompts.md` 第 5 节。

---

## 硬规则(不可协商)

写进每个 prompt,并让 lint 强制:

1. 全项目**只有一个 `:root` 块**,只在 `tokens.css` 里
2. **禁止颜色字面量**(`#xxx` / `rgb()` / `hsl()`),颜色只用 `var(--c-*)`
3. **禁止设计维度写裸 px**(间距/字号/圆角/行高),只用 `var(--space-* / --text-* / --radius-* / --leading-*)`。例外:`1px` 边框线宽
4. **禁止 `!important`**(几乎总是重复规则打架的后遗症)
5. **禁止 `outline: none`**(焦点环是键盘用户唯一的导航手段)
6. **禁止为同一语义造第二个 token**(先 grep 找最接近的复用)
7. **改原规则,不要在文件末尾追加覆盖块** —— 这是 CSS 膨胀的头号来源。
真实案例:某文件 453 条规则只覆盖 387 个选择器,66 条重复声明,文件注释就是 6 段迭代史
8. **禁止"顺便优化"** —— "发现其他问题请单独列出,不要顺手改"
9. **一次只改一个维度**(对齐 → 间距 → 灰度 → 圆角 → 颜色),改完截图再动下一个
10. **禁止给形容词下需求** —— 给数值或给参照图。"高级感"没有可执行的落点

完整清单(22 条,含症状/为什么坏/正确做法)见 `templates/anti-patterns.md`。

---

## 物料索引

**物料比方法论值钱。** 大部分的实际效果来自下面这些具体文件和数值,
而不是上面这些流程描述。

### templates/ —— 复制到项目里去用的

| 文件 | 用途 |
|---|---|
| `templates/tokens.css` | 设计 Token 唯一真相源:11 节、含不可违反规则注释、深色模式结构预留 |
| `templates/DESIGN.md` | 视觉契约模板:定位/参照物表/组件白名单/页面×状态矩阵/变更流程 |
| `templates/anti-patterns.md` | 22 条视觉禁飞区,每条含症状·为什么坏·正确做法·机器检测方式 |
| `templates/qa-checklist.md` | 8 组视觉验收清单,全部可对着截图回答 |
| `templates/stylelint.config.mjs` | 把硬规则变成会 `exit 1` 的检查 |
| `templates/vue/Ui{Button,Input,Select,Card,Badge,EmptyState}.vue` | 6 个 Vue 3 原语,已覆盖 4 种交互状态、零颜色字面量 |

### references/ —— 按需加载的方法说明

| 文件 | 什么时候读 |
|---|---|
| `references/bootstrap.md` | 阶段一:为什么必须先建约束、12 项产出清单与验收判据 |
| `references/build.md` | 阶段二:单页面实现顺序、契约层与 mock、切真后端判据 |
| `references/tokens-baseline.md` | Token 数值为什么是这些数字;三种项目类型的调参;迁移做法 |
| `references/screenshot-and-qa.md` | 零依赖截图方案(实测可用)、截不到时的降级策略、验收回路、MCP 选型 |
| `references/reference-sources.md` | 去哪找参照图、**为什么 AI 不能替用户选参照物**、常见错误 |
| `references/prompts.md` | 6 个可直接复制的提示词模板 + 好坏对照表 |

### scripts/ —— 阶段一生成到项目里的

| 脚本 | 作用 |
|---|---|
| `scripts/capture.mjs` | 零依赖截图(自动探测 Chrome/Edge,多宽度,SPA 等待) |
| `scripts/audit-css.mjs` | 设计系统漂移记分卡(`:root` 块数、颜色近重复聚类、字号/圆角档位、token 覆盖率、冗余声明),任一超阈值 `exit 1`;支持 `--ignore <片段>` 排除 token 文件这类"字面量本来就合法"的文件;「同一选择器分散在多块」只提示、不参与判定 |

---

## 关键提醒

**参照物是价值最高的一项(约占 80%),而它的选择不能由 AI 代劳。**
"好看"是用户的品味判断,不在代码里也不在任何可检索的资料里。
AI 应该产出**候选集 + 特征分析 + 截图**,由用户做最终选择。
详见 `references/reference-sources.md` 第 4 节。

**如果项目已有界面,优先取现状中最后生效的那个主色**,
而不是新造一个 —— 迁移在视觉上接近中性,阻力最小。

**截图能力已实测**(本环境零依赖):Chrome headless 675ms 产出 PNG,
可正常读图。不要因为"没有截图工具"而跳过验收 —— `capture.mjs` 今天就能用。
153 changes: 153 additions & 0 deletions .agents/skills/frontend-craft/references/bootstrap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# bootstrap.md — 阶段一:先把"约束"建起来,再写界面

> **本文回答**:"先让 AI 按 skill 把仓库契约文件、可执行检查、验收回路都准备好,
> 然后再按 skill 生成前端代码,这样做出来会不会更好?"
>
> **答案是会,而且这是这套方法里最重要的一步。** 但要说清它好在哪里 ——
> 它提升的是**下限和稳定性**,不是审美上限。

---

## 为什么必须先做这一步

对比两种做法,同一个 AI、同一个需求:

**做法 A(绝大多数 vibe coding)**:直接说"给我做个工作台界面"。
AI 从零开始,每个决定都是即兴的:这个绿还是那个绿?12px 还是 13px?
圆角 8 还是 10?——每个决定单独看都合理,合起来就是 7 个绿 + 21 种圆角 +
19 种字号。**不是因为它审美差,而是因为它一共要做 200 个决定,
其中 190 个没有依据。**

**做法 B(先建约束)**:先花一轮把 token 表定下来(7 档字号、4 档圆角、
4 个有彩色),把原语组件写出来,把 lint 打开,把截图脚本跑通。
然后再生成界面 —— 此时 AI 要做的是**选择**而不是**发明**:正文用
`--text-base`,卡片用 `--radius-md`,主色用 `--c-accent`。

差别在于:做法 A 里"12px"和"13px"是两个同等合法的即兴创作;
做法 B 里只有一个存在,另一个在 lint 里会报错。

> **一句话**:这一步不产生任何业务价值,它的全部价值是让后面每一步都有依据。
> 跳过它,后面 200 个决定就全部失去约束,而这些决定是不可逆地叠加的。

---

## 阶段一的产出清单

按顺序做,每一项都必须**落盘成文件**。没落盘 = 没做(下一轮会话就忘了)。

| # | 产出 | 路径 | 验收方式 |
|---|---|---|---|
| A1 | 目录骨架 | `src/{components/{ui,layout},styles,api,mocks,views}` | 目录存在 |
| A2 | Design Token | `src/styles/tokens.css` | `audit-css.mjs` 的 `:root` 块数 == 1 |
| A3 | 视觉契约 | `docs/design/DESIGN.md` | 参照物表非空;状态矩阵填满 |
| A4 | 负面清单 | `docs/design/anti-patterns.md` | 至少 10 条 |
| A5 | 原语组件 | `src/components/ui/Ui{Button,Input,Select,Card,Badge,EmptyState}.vue` | 每个覆盖 4 种状态;无字面量颜色 |
| A6 | 布局骨架 | `src/components/layout/AppShell.vue` | **只有灰块**,无业务内容 |
| A7 | 契约层 | `src/api/types.ts` + `client.ts` | 类型完整;唯一网络出口 |
| A8 | Mock | `src/mocks/*` | 能返回 typed 假数据 |
| A9 | lint | `stylelint.config.mjs` + `package.json` 脚本 | 在干净项目上通过 |
| A10 | 漂移审计 | `scripts/audit-css.mjs` | 能跑出报告 |
| A11 | 截图 | `scripts/capture.mjs` | 能产出非空 PNG |
| A12 | 验收清单 | `docs/design/qa-checklist.md` | 清单已按项目调整 |

**A6 的"只有灰块"是硬要求。** 先用灰色矩形把布局占满,确认结构、间距、
断点都对了,再往里填内容。先填内容再调布局,等于在移动的靶子上画画 ——
你会发现每次改内容都要重调布局。

---

## A2 / A3 的具体做法

### 定 token(这一步决定了 80% 的后续自由度)

**不要从零发明。** 直接抄 `tokens-baseline.md` 里的数值表,只调三件事:

1. **正文基准字号**:密集工具界面 13px;阅读/营销型 15~16px。
2. **主色**:从参照物或品牌色取一个。
> 如果项目已有界面,**优先取现状里最后生效的那个主色**,
> 而不是新造一个。这样迁移看起来是"整理"而不是"换风格",
> 阻力最小,也不会引起"又改配色"的反弹。
3. **中性色偏色**:冷灰(偏蓝)显得技术、理性;暖灰(偏黄/绿)显得柔和。

其余档位(间距 13 档、圆角 3 档、语义色 3 个)**不要动**,它们不是审美选择,
是防膨胀的结构。

### 定参照物(这一步决定上限)

见 `reference-sources.md`。核心:**AI 不能替用户选参照物** ——
因为"好看"是用户的品味判断,不是技术判断。AI 可以找到候选、
可以描述差异、可以指出某个参照物和你的产品调性不匹配,
但最终选定必须由人做。

如果没有参照物,产出会稳定地落在"整洁但平庸"这个位置。

---

## A9 / A10 / A11 为什么算"阶段一"的一部分

因为它们不是"代码质量工具",它们是**约束的执行机构**。

- 一句写在文档里的"禁止字面量颜色",AI 有 5% 概率违反且你不会知道。
- 一条会 `exit 1` 的 stylelint 规则,违反时你**一定会知道**。

**规则的价值不在于被写下来,而在于被违反时会发生什么。**
文字规则违反后什么都不会发生 —— 这就是为什么单靠 SKILL.md 不够。

同理,`capture.mjs` 让"看一眼"从一件麻烦事变成一条命令。
如果截图很麻烦,它就不会发生;不发生的验收等于没有验收。

---

## 阶段一结束的判据

全部满足才算阶段一完成:

- [ ] `npx stylelint "src/**/*.{css,vue}"` 在**新代码**上 0 error。
迁移期 legacy 文件用下面的 audit 跟踪收敛,不进这个硬门 ——
一开就是 600+ 条报错时,人会习惯性忽略输出,那等于没开。
- [ ] **token 文件本身干净**:`node scripts/audit-css.mjs src/styles/tokens.css`
的报告里 `:root` 块 = 1、变量无重复声明、**冗余声明(同属性重复声明)= 0**。
> 「同一选择器分散在多块」已降级为提示项,不计入这条判据 ——
> 各块属性互不重叠时它并不冗余,把它归零反而要冒合并块带来的渲染风险。
> ⚠️ **不要拿"全绿"当这条的判据。** token 文件按定义必然让
> `token 覆盖率` 和 `彩色 distinct` 两项 FAIL —— 它**就是**那些
> 字面量的唯一合法来源。这两项要在「排除 token 文件之后」的主样式上量:
> `node scripts/audit-css.mjs src --ignore tokens.css`。
- [ ] `node scripts/audit-css.mjs src --ignore tokens.css` 能跑出报告,
并把当前分数**存档**(这就是迁移的"前"分数,阶段二每一步都要跟它比)
- [ ] `node scripts/capture.mjs <本地 URL> shots` 产出非空 PNG,
且**你亲眼看过**那张图(没看过的截图等于没截)
- [ ] **迁移项目专用**:引入 token 文件后重截,与引入前**逐像素一致**。
阶段一的硬要求是"零视觉变化" —— 地基不该改变外观,只改变依据。
- [ ] 原语组件在 `AppShell` 灰块骨架里渲染正常(**新建项目**要求;
迁移项目跳过,不要为了这一条去重建已有的壳)
- [ ] `DESIGN.md` 的参照物表和状态矩阵已填
- [ ] `api/types.js`(JS 项目用 JSDoc `@typedef`)或 `api/types.ts`(TS 项目)
覆盖了所有接口的形状,且**形状来自真实响应**而不是凭印象写的。
把取证记录(哪个端点、HTTP 码、响应大小、观测到的枚举值)写进文件末尾。

**此时应该能截到一张"结构正确但内容空洞"的界面**(新建项目),
或者一张"和改造前逐像素相同、但依据已经换掉"的界面(迁移项目)。
这张图就是后续所有页面的地基。

---

## 诚实的边界

做这一步**会**带来:

- 一致性的数量级提升(不再有 7 个绿、21 种圆角)
- 空态/错态/hover/focus 不再遗漏(因为原语层就要求覆盖)
- 新会话不用重新解释规范(规范在仓库里)
- 从 mock 切真后端时改动极小(契约是真的)
- 漂移可被机器发现,而不是靠人眼

做这一步**不会**带来:

- 审美上限。参照物选得差,产出依然是"整洁的差品味"。
- 信息架构能力。把不该出现在正文里的控件状态写进正文(AP-18),
token 和 lint 都拦不住 —— 那是产品判断。
- 阻止用户自己用临时 prompt 破坏体系。skill 管不住旁边那个人。

> **所以:这一步把"很烂"提到"整洁统一",这是真实且巨大的提升。
> 但"整洁"到"优秀"之间那一步,靠的是参照物和人的品味判断。**
Loading
Loading