DESIGN.md:让 Coding Agents 理解视觉设计的格式规范
posts posts 2026-04-30T11:30:00+08:00对着 google-labs-code/design.md 的 README、docs/spec.md 与 packages/cli 源码逐条核查后的项目解读:双层结构如何把设计系统变成 Agent 可消费的纯文本、linter 的 11 条规则、四个 CLI 命令与退出码、Windows 下的 designmd 别名坑,以及哪些流行写法在官方规范里并不存在。技术笔记设计系统, 前端, AI Agent判断:DESIGN.md 解决的问题不是「让 AI 画得更好看」,而是让 AI 生成的前端代码有据可依。设计系统住在 Figma 的二进制文件里,Coding Agent 吃的是纯文本,两边没有桥梁,于是同一个 Agent 两次生成的按钮颜色、间距、圆角各不相同。这个项目给出的桥是一份 Markdown 文件:YAML front matter 放机器可读的设计 token,Markdown 正文放人类可读的设计理由,再配一套 CLI 做 lint、diff、导出和规范注入。它更像设计系统界的
.editorconfig——文件本身不渲染任何像素,价值在于让「设计约束」成为 Agent 上下文里可校验的部分。项目仍在 alpha,格式会变,但五个月里 stars 从一万涨到两万八,方向已经有人用脚投票。
读完后能做什么:写一份能过 lint 的 DESIGN.md;跑通 lint / diff / export / spec 四个命令并看懂退出码;知道 linter 的 11 条规则各自在查什么;把规范注入自己的 Agent 工作流;以及判断自己的项目现阶段值不值得引入它。
依据:google-labs-code/design.md
main分支README.md、docs/spec.md与packages/cli/src/源码(parser、model、linter rules、emitters),GitHub API(应用程序接口)在 2026-09-30 读到 28,167 stars / 2,280 forks / Apache-2.0 / created 2026-04-10 / pushed 2026-09-14;npm registry 显示@google/design.md最新版 0.4.0(2026-07-27 发布)。格式版本为alpha。文中所有命令、选项、字段名逐一对照过 README 与源码,与网上旧教程冲突处以仓库为最终依据。
目录
- §1 问题:Agent 生成的 UI 为什么长不一样
- §2 双层结构:Token 是规范,Prose 是上下文
- §3 Token 类型系统
- §4 八段式 Section 与 omitted 声明
- §5 组件 Token
- §6 架构:一份 Markdown 如何变成校验报告与导出产物
- §7 CLI 四命令
- §8 编程 API
- §9 实战:从零到 Agent 工作流
- §10 未知内容的处理规则
- §11 版本演化、技术栈与局限
- §12 总结与采用建议
- §13 常见问题
- §14 事实核验与引用
§1 问题:Agent 生成的 UI 为什么长不一样
AI Coding Agent(AI 编程智能体,下文简称 Agent)写前端越来越快,产出的视觉一致性却跟不上。同一个项目、同一个 Agent,两次会话生成的按钮颜色不同、间距各异、圆角随手定——这不是模型幻觉,是上下文缺失。
根本原因一句话就能说清:设计系统活在 Figma 里,Agent 活在文本里。Figma 的变量库、组件集、设计稿是二进制格式,Agent 无法直接消费。传统工作流靠人工中转:设计 token(Design Token,设计令牌,即用键值对记录颜色、字号、间距等视觉决策的机制)从 Figma 导出,开发者手写成 CSS 变量或 Tailwind 配置,Agent 再间接引用。多一层人工,就多一层延迟和失真。
DESIGN.md 的思路是把这层中转拆掉:将设计系统以纯文本形式直接交给 Agent,作为其上下文的一部分。它由两部分组成——
- 一份格式规范(docs/spec.md):规定怎么写一份 DESIGN.md 文件;
- 一个 CLI 工具(npm 包
@google/design.md):负责校验(lint)、版本对比(diff)、格式导出(export)和规范输出(spec)。
两者都由 Google Labs Code 团队以 Apache-2.0 协议开源,官方文档托管在 stitch.withgoogle.com(Stitch 是 Google 面向 UI 生成的产品,DESIGN.md 是其配套的开放格式)。
§2 双层结构:Token 是规范,Prose 是上下文
一份 DESIGN.md 文件分两层:
┌──────────────────────────────────────────┐
│ YAML Front Matter(机器可读层) │ ← 设计 Token:精确数值
├──────────────────────────────────────────┤
│ Markdown Body(人类可读层) │ ← 设计理由:为什么选这些值
└──────────────────────────────────────────┘规范原文的措辞是:tokens are the normative values; the prose provides context for how to apply them——token 是规范值,prose(正文散文部分)提供应用场景。两者分工明确,Agent 解析时不会把描述性文字误读成样式指令。
下面这个 Heritage 示例来自官方 README,两层齐看:
---
name: Heritage
colors:
primary: "#1A1C1E"
secondary: "#6C7278"
tertiary: "#B8422E"
neutral: "#F7F5F2"
typography:
h1:
fontFamily: Public Sans
fontSize: 3rem
body-md:
fontFamily: Public Sans
fontSize: 1rem
label-caps:
fontFamily: Space Grotesk
fontSize: 0.75rem
rounded:
sm: 4px
md: 8px
spacing:
sm: 8px
md: 16px
---## Overview
Architectural Minimalism meets Journalistic Gravitas. The UI evokes a
premium matte finish — a high-end broadsheet or contemporary gallery.
## Colors
The palette is rooted in high-contrast neutrals and a single accent color.
- **Primary (#1A1C1E):** Deep ink for headlines and core text.
- **Secondary (#6C7278):** Sophisticated slate for borders, captions, metadata.
- **Tertiary (#B8422E):** "Boston Clay" — the sole driver for interaction.
- **Neutral (#F7F5F2):** Warm limestone foundation, softer than pure white.读到这份文件,Agent 就知道:标题用 Public Sans 深墨色,背景用暖石灰白,行动按钮交给「Boston Clay」陶土红——不必猜,也不必再问设计稿。
token 的组织方式刻意向 W3C Design Token Format 靠拢:规范自述「inspired by the Design Token JSON spec」,采纳了它的两个设计——按类型分组(colors / typography / spacing),以及 {path.to.token} 引用语法。这带来了实际的互操作红利:token 可以低成本地在 DESIGN.md、Figma 变量、Tailwind 配置之间转换(§6.4 的 export 命令就是干这个的)。
§3 Token 类型系统
3.1 四种类型
| 类型 | 格式 | 示例 |
|---|---|---|
| Color | 任意合法 CSS 颜色 | "#1A1C1E"、oklch(62% 0.18 250)、rgba(255, 255, 255, 0.1) |
| Dimension | 数字 + 单位(仅 px、em、rem) | 48px、-0.02em |
| Token Reference | {path.to.token} | {colors.primary} |
| Typography | 含 fontFamily、fontSize 等字段的对象 | 见 3.2 |
Color 的合法范围比很多人想象的宽:规范支持 hex(#RGB、#RGBA、#RRGGBB、#RRGGBBAA)、命名色(cornflowerblue)、函数式写法(rgb()、hsl()、hwb())、广色域(oklch()、oklab()、lch()、lab()),甚至 color-mix()。linter 做 WCAG 对比度检查时会把所有颜色内部转成 sRGB,但导出时保留原始写法。规范同时明确:hex 仍是推荐的默认选择,简单、工具支持广。
3.2 Typography 全字段
typography:
h1:
fontFamily: Public Sans
fontSize: 3rem
fontWeight: 600 # 裸数字或带引号的字符串等价
lineHeight: 1.1 # 也接受 24px 这类 Dimension
letterSpacing: -0.02em
fontFeature: "tnum" # 映射 CSS font-feature-settings
fontVariation: "wght 600" # 映射 CSS font-variation-settingslineHeight 双格式是刻意设计:无单位数字表示字号倍数(推荐写法),Dimension 表示绝对行高。规范还提到,多数设计系统会定义 9–15 个 typography 层级,命名习惯是 headline / body / label / caption 加尺寸后缀(body-md、label-caps)。
3.3 Token Reference 的约束
跨 token 引用用 {path.to.token}:
components:
button-primary:
backgroundColor: "{colors.tertiary}" # 引用 colors token
textColor: "{colors.on-tertiary}"
rounded: "{rounded.sm}"
padding: 12px # 直接字面值有一条边界值得记住:多数 token 组里的引用必须指向基元值(如 colors.primary-60),不能指向整个组(如 {colors});只有 components 内允许引用组合值,比如 {typography.label-md}。引用构成依赖图,linter 的 broken-ref 规则负责抓「悬空引用」——指向从未定义的 token(§6.3)。
3.4 Front Matter 全景
顶层 schema 一共九个键:
version: <string> # 可选,当前值 "alpha"
name: <string>
description: <string> # 可选
omitted: <string[] | OmittedSection[]> # 可选,见 §4
colors: { <token 名>: <Color> }
typography: { <token 名>: <Typography> }
rounded: { <档位>: <Dimension> }
spacing: { <档位>: <Dimension 或数字> }
components: { <组件名>: { <token 名>: <字符串或引用> } }rounded 和 spacing 的档位名(<scale-level>)是自由命名,规范建议 xs 到 full 这类描述性字符串。spacing 的值除了尺寸还可以是无单位数字,用来表达列数、比例这类非尺寸量。规范附录列了一份非规范性推荐命名:colors 用 primary / secondary / tertiary / neutral / surface / on-surface / error,typography 用 headline-lg / body-md / label-sm 这类,rounded 用 none / sm / md / lg / xl / full——遵循它,linter 的 missing-primary 规则才能正确识别你的主色。
§4 八段式 Section 与 omitted 声明
Markdown Body 用 ## 标题划分 section,出现时必须遵循固定顺序:
| # | Section | 别名 |
|---|---|---|
| 1 | Overview | Brand & Style |
| 2 | Colors | |
| 3 | Typography | |
| 4 | Layout | Layout & Spacing |
| 5 | Elevation & Depth | Elevation |
| 6 | Shapes | |
| 7 | Components | |
| 8 | Do’s and Don’ts |
规则有四条:用不上的 section 可以整个省略;出现了就必须按上表顺序,乱序触发 section-order 警告;规范外的未知标题(比如 ## Iconography)原样保留、不报错;重复标题属于硬错误,文件被拒绝。另外,文件开头允许放一个可选的 # 一级标题做文档题目,它不会被当成 section 解析。
省略一个 section 时如果不想看到 linter 的缺失提醒,可以在 front matter 里显式声明 omitted——这是 schema 的一等公民,不是注释技巧:
omitted:
- spacing
- section: rounded
reason: "No rounded corners defined in brand book"每一项可以是纯 section 名字符串,也可以是带 reason 的对象——后者把「为什么没有」写进文件本身,比在评审里口头解释更可维护。
§5 组件 Token
components 把一组子 token 打包成可复用的样式模板:
components:
button-primary:
backgroundColor: "{colors.tertiary}"
textColor: "{colors.on-tertiary}"
rounded: "{rounded.sm}"
padding: 12px
button-primary-hover:
backgroundColor: "{colors.tertiary-container}"
glass-card-standard:
backgroundColor: rgba(255, 255, 255, 0.1)
textColor: "{colors.primary}"
rounded: "{rounded.lg}"
padding: "{spacing.glass-padding}"合法的组件属性只有八个:backgroundColor、textColor、typography、rounded、padding、size、height、width。写了别的属性(比如 borderColor)不会报错,但会收到 warning(§10)。
hover、active、pressed 这些状态变体,规范选择用独立键名表达而不是嵌套结构:button-primary 和 button-primary-hover 是两个平级条目。规范原文写明:「The agent will consider all variants and make the appropriate styling decisions.」——Agent 读到全部变体后自行决定如何应用。这套命名是约定而非语法,工具不会校验 -hover 后缀。
规范同时列了七个常见组件类型的建模建议(buttons、chips、lists、tooltips、checkboxes、radio buttons、input fields),但配了一句官方注记:components 规范仍在演进中,当前结构刻意保持弹性,给各领域的自定义组件留空间。这也是整个项目 alpha 状态的一个缩影。
§6 架构:一份 Markdown 如何变成校验报告与导出产物
先看全景,再逐层拆。以下结构对应仓库 packages/cli/src/linter/ 目录:
6.1 Parser:两种嵌入方式,绝不抛异常
Parser 基于 unified + remark-frontmatter + yaml,支持两种 token 嵌入方式:标准 front matter(文件头 --- 围栏),或者散落在正文里的 ```yaml 代码块(yml 也认)。源码 parser/handler.ts 的类注释写着原话:「Never throws — all errors returned as ParserResult failures」——所有错误都包装成结构化结果返回,绝不以异常形式逃逸。三类可恢复错误各有错误码:
NO_YAML_FOUND:文件里找不到任何 YAML;YAML_PARSE_ERROR:YAML 语法解析失败;DUPLICATE_SECTION:同一个顶层键(比如colors)同时出现在 front matter 和代码块里,或出现在两个代码块里。
多个代码块中的 YAML 会被合并成一份设计系统,但合并不是无脑 Object.assign——同名顶层键触发 DUPLICATE_SECTION,这个设计让「两处都定义了 colors」这种配置漂移在解析阶段就暴露。解析同时产出一份 sourceMap,记录每个顶层键的来源行号和所属代码块——注意粒度是顶层键(colors、typography),不是每个叶子 token。
调用方对可恢复错误的处理分两档:lint() 顶层拿到 recoverable: true 的失败时,返回一份「空设计系统 + 一条 warning」的报告继续工作;不可恢复的错误才向上抛异常。
6.2 Model:从 YAML 树到类型化设计系统
Model 层把裸 YAML 树解析成类型化的 DesignSystemState:颜色解析、token 引用解引用、依赖图构建都在这一层完成。它也会产出自己的 findings(比如引用解析失败),与 linter 的 findings 合并进最终报告。
6.3 Linter:11 条规则
README 的说法是「The linter runs eleven rules against a parsed DESIGN.md」。逐条对照源码 linter/linter/rules/ 目录,11 条都在:
| 规则 | 级别 | 检查内容 |
|---|---|---|
broken-ref | error | {colors.primary} 这类引用解析不到任何已定义 token |
missing-primary | warning | 定义了颜色但没有 primary——Agent 会自行生成一个 |
contrast-ratio | warning | 组件 backgroundColor/textColor 配对的对比度低于 WCAG AA 下限(4.5:1) |
orphaned-tokens | warning | 定义了但没被任何组件引用的颜色 token |
token-summary | info | 各 section 的 token 数量统计 |
missing-sections | info | 有其他 token 但缺少 spacing、rounded 这类可选 section |
missing-typography | warning | 定义了颜色但没有 typography——Agent 会退回默认字体 |
section-order | warning | section 顺序偏离规范 |
unknown-key | warning | 顶层键疑似已知键的拼写错误(colours: → colors:);真正的自定义扩展键保持沉默 |
token-like-ignored | warning | 未知顶层键的值长得像 token(hex 色、字体名、尺寸),暗示它被拼错或被丢弃了 |
omitted-rules | info | 校验 omitted 配置里未知的或冗余的 section |
值得注意的两条:contrast-ratio 是唯一检查「视觉后果」的规则——它真的在算两个颜色的对比度;unknown-key 和 token-like-ignored 是一对拼写纠错机制,前者抓键名 typo,后者抓「值看起来完全合法、键名却写错了所以整块被忽略」的更隐蔽情况。唯一 error 级别的只有 broken-ref,其余都是 warning 或 info——这套分级意味着 CLI 的退出码策略(§7)基本由引用完整性决定。
6.4 Emitter:导出到 Tailwind 与 DTCG
解析后的设计系统可以导出到三种目标产物,CLI 提供四个 --format 取值——tailwind 是 json-tailwind 的向后兼容别名:
--format | 产物 | 说明 |
|---|---|---|
json-tailwind | JSON | Tailwind v3 的 theme.extend 配置对象 |
css-tailwind | CSS | Tailwind v4 的 @theme { ... } 块,走 --color-*、--text-*、--radius-* 等 CSS 变量命名空间 |
dtcg | JSON | W3C Design Tokens Format Module 的 tokens.json |
导出产物长什么样,仓库 examples/atmospheric-glass/ 里有现成答案。Tailwind 一侧是标准的 theme.extend:
module.exports = {
theme: {
extend: {
colors: {
"surface": "#0b1326",
"surface-container": "#171f33",
"on-surface": "#dae2fd",
"primary": "#ffffff",
// …共 40 余个颜色 token
},
},
},
};DTCG 一侧则是带类型标注的 W3C 格式,颜色值连色彩空间一起记录:
{
"colors": {
"surface": {
"$type": "color",
"$value": {
"colorSpace": "srgb",
"components": [0.043, 0.075, 0.149],
"hex": "#0b1326"
}
}
}
}6.5 Fixer:机器可读的修复建议
linter/ 下还有一个 Fixer 模块,以 fixSectionOrder 函数对外暴露:输入文档与期望顺序,输出调整后的结果(FixerResult)。section 乱序这类机械问题不必手工改,交给它即可。
§7 CLI 四命令
安装与直接运行:
npm install @google/design.md
# 或免安装直接跑
npx @google/design.md lint DESIGN.md所有命令都接受文件路径或 -(stdin),默认输出 JSON。
7.1 lint:结构校验
npx @google/design.md lint DESIGN.md
cat DESIGN.md | npx @google/design.md lint -输出示例(README 原例,注意 summary 的键是 infos):
{
"findings": [
{
"severity": "warning",
"path": "components.button-primary",
"message": "textColor (#ffffff) on backgroundColor (#1A1C1E) has contrast ratio 15.42:1 — passes WCAG AA."
}
],
"summary": { "errors": 0, "warnings": 1, "infos": 1 }
}退出码:有 error 时为 1,否则 0。--format 目前只有 json 一种。这条对比度消息的措辞值得留意——15.42:1 是「远超标准」的意思,warning 也会报告通过项,方便 Agent 在日志里确认检查确实跑过。
7.2 diff:版本间回归检测
npx @google/design.md diff DESIGN.md DESIGN-v2.md输出报告 token 级别的增删改(五个 token 组各一组 added/removed/modified),外加 findings 前后对比:
{
"tokens": {
"colors": { "added": ["accent"], "removed": [], "modified": ["tertiary"] },
"typography": { "added": [], "removed": [], "modified": [] },
"rounded": { "added": [], "removed": [], "modified": [] },
"spacing": { "added": [], "removed": [], "modified": [] },
"components": { "added": [], "removed": [], "modified": [] }
},
"findings": {
"before": { "errors": 0, "warnings": 1, "infos": 1 },
"after": { "errors": 0, "warnings": 1, "infos": 1 },
"delta": { "errors": 0, "warnings": 0 }
},
"regression": false
}「after 比 before 多了 error 或 warning」即判定回归,regression 置 true,退出码 1——CI 里可以直接用它当门禁。
7.3 export:格式导出
npx @google/design.md export --format json-tailwind DESIGN.md > tailwind.theme.json
npx @google/design.md export --format css-tailwind DESIGN.md > theme.css
npx @google/design.md export --format dtcg DESIGN.md > tokens.json退出码分三档:0 导出成功(源文件有 lint findings 不影响导出,要用 lint 把关就分开跑);1 格式名非法或 emitter 出错;2 输入文件读不到。这个「导出不卡 lint」的设计是有意的——lint 是质量门禁,export 是构建步骤,两者解耦。
7.4 spec:把规范注入 Agent prompt
npx @google/design.md spec # 完整规范(Markdown)
npx @google/design.md spec --rules # 附加 lint 规则表
npx @google/design.md spec --rules-only --format json # 仅规则表,JSON 格式这是官方给 Agent 集成的推荐姿势——README 对 spec 命令的定位原话是「useful for injecting spec context into agent prompts」:
You are a frontend coding agent. Before generating UI, read the design system spec:
---
{npx @google/design.md spec 的输出}
---Agent 不需要预先训练过 DESIGN.md,拿到这段规范文本就知道格式怎么读、token 怎么用。
7.5 Windows 用户的坑:designmd 别名
README 花了三段篇幅讲一个 Windows 特有的坑:bin 名 design.md 带 .md 后缀,在 PowerShell 下做命令解析时会和系统的 Markdown 文件关联撞车——npx @google/design.md lint DESIGN.md 可能没有输出,甚至直接用 Markdown 编辑器把文件打开了。官方解法是用包内提供的无点别名 designmd:
npx -p @google/design.md designmd lint DESIGN.md在 package.json 的 script 里直接调用时同样建议用 designmd;designmd 与 design.md 解析到同一个入口,全平台行为一致。
顺带一个安装期故障排查:报 npm error ENOVERSIONS(No versions available)基本都意味着 npm 没有在查公共 registry——.npmrc 里的自定义 registry=、还没同步这个包的公司镜像、或者 @google scope 的 registry 配错了。用 npm config get registry 看实际生效值,正常应为 https://registry.npmjs.org/。
§8 编程 API
CLI 之外,@google/design.md 导出完整的 TypeScript API,入口是 @google/design.md/linter。以下字段与函数逐一对照过源码 packages/cli/src/linter/index.ts 与 lint.ts:
import { lint } from '@google/design.md/linter';
const report = lint(markdownString);
report.findings; // Finding[]:全部发现
report.summary; // { errors, warnings, infos }
report.designSystem; // 解析后的 DesignSystemState
report.tailwindConfig; // 顺带生成的 Tailwind v3 主题配置
report.sections; // 文档中出现的 H2 标题名
report.documentSections; // 按 H2 切分后的正文块
lint() 一次跑完全流程(解析 → 建模 → 规则检查 → Tailwind 生成),返回的 LintReport 六个字段都在上面。需要更细的控制时用高级接口:
import { runLinter, preEvaluate, DEFAULT_RULES } from '@google/design.md/linter';
import { contrastRatio } from '@google/design.md/linter';
// 逐层手动执行
const lintResult = runLinter(designSystem, customRules);
// 颜色对比度计算是公开工具函数
const ratio = contrastRatio('#ffffff', '#000000'); // → 21
11 条规则各自有具名导出(brokenRef、missingPrimary、contrastCheck、orphanedTokens、tokenSummary、missingSections、missingTypography、unknownKey、tokenLikeIgnored、omitted),可以单独调用或组合。自定义规则只需实现 LintRule 接口:
import { lint, DEFAULT_RULES, type LintRule } from '@google/design.md/linter';
const noRedButtons: LintRule = {
name: 'no-red-buttons',
severity: 'warning',
check: (state) => {
const findings = [];
const btn = state.components['button-primary'];
if (btn?.backgroundColor === '{colors.error}') {
findings.push({
severity: 'warning',
path: 'components.button-primary.backgroundColor',
message: 'Avoid using error color for primary buttons.',
});
}
return findings;
},
};
const report = lint(content, { rules: [...DEFAULT_RULES, customRule] });四个 emitter 也以类形式导出(TailwindEmitterHandler、TailwindV4EmitterHandler、DtcgEmitterHandler、CssVarsEmitterHandler),加上 fixSectionOrder——想在 CLI 之外把 DESIGN.md 嵌进自己的构建链或内部平台,这些就是扩展点。
§9 实战:从零到 Agent 工作流
用一个完整案例把前面的机制串起来。场景:给一个天气 App 建 Glassmorphism(玻璃拟态)设计系统。官方仓库 examples/atmospheric-glass/ 就是这个例子,以下片段取自真实文件。
第一步:写 front matter。 玻璃拟态的关键是半透明表面色,token 里直接用 rgba() 记录:
---
name: Atmospheric Glass
colors:
surface: "#0b1326"
surface-container: "#171f33"
on-surface: "#dae2fd"
primary: "#ffffff"
on-primary: "#2f3131"
secondary: "#adc9eb"
typography:
display-lg:
fontFamily: Inter
fontSize: 84px
fontWeight: "700"
lineHeight: 90px
letterSpacing: -0.04em
rounded:
lg: 1rem
xl: 1.5rem
components:
glass-card-standard:
backgroundColor: rgba(255, 255, 255, 0.1)
textColor: "{colors.primary}"
rounded: "{rounded.lg}"
button-primary:
backgroundColor: "{colors.primary}"
textColor: "{colors.on-primary}"
height: 48px
---第二步:写 prose。 半透明的设计意图只有人写得出来,这段话 Agent 读得懂:
## Elevation & Depth
- **Level 2 (Standard Card):** `backdrop-filter: blur(20px)`,
`background: rgba(255, 255, 255, 0.1)`.
- **Level 3 (Elevated/Modals):** `backdrop-filter: blur(40px)`,
`background: rgba(255, 255, 255, 0.2)`.第三步:lint。 改完设计 token 就跑一次:
npx @google/design.md lint DESIGN.md第四步:export。 把 token 落成 Tailwind 配置进仓库:
npx @google/design.md export --format json-tailwind DESIGN.md > tailwind.theme.json第五步:接入 Agent。 两种互补的方式。其一是把规范注入系统提示(§7.4 的 spec 命令),让 Agent 认识格式本身;其二是在项目的 Agent 说明文件(如 AGENTS.md 或 CLAUDE.md)里写明约定:
Before generating any UI code, read DESIGN.md in the project root.
Apply all tokens from the frontmatter exactly as defined.
Use the prose sections to resolve any ambiguity in component design.
Run `npx @google/design.md lint DESIGN.md` after any design token changes.日常运转就是一条闭环:设计改了主题色 → 更新 DESIGN.md → lint 确认没写坏 → diff 检查这次改动是否引入回归 → export 同步 Tailwind 配置 → CI 里跑同样的命令做门禁:
# .github/workflows/design-lint.yml
name: Design System Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npx @google/design.md lint DESIGN.md
- run: npx @google/design.md diff DESIGN.md DESIGN-baseline.md这套流程的价值在于:设计约束的变更从此走代码评审——谁改了主色、影响几个组件、对比度有没有掉到 AA 以下,diff 报告一清二楚,而不是埋在某次 Figma 的静默更新里。
§10 未知内容的处理规则
格式要在真实世界存活,必须回答一个问题:遇到规范没定义的东西怎么办?DESIGN.md 的答案是一张明确的行为表(spec 原文):
| 场景 | 行为 | 官方示例 |
|---|---|---|
| 未知 section 标题 | 保留,不报错 | ## Iconography |
| 未知颜色 token 名 | 值合法即接受 | surface-container-high: '#ede7dd' |
| 未知 typography token 名 | 接受为合法 typography | telemetry-data |
| 未知 spacing 值 | 接受;不是合法 Dimension 就按字符串存 | grid-columns: '5' |
| 未知组件属性 | 接受,带 warning | borderColor |
| 重复 section 标题 | Error:拒绝文件 | 两个 ## Colors |
这张表是整个格式的前向兼容承诺:未来的 token 类型、新增的 section、团队自定义的组件属性,都不会让旧版 linter 报错。唯一的硬约束是重复标题——那属于文件内部自相矛盾,没有保留价值。唯一的 warning 是未知组件属性——松到能扩展,紧到能发现手滑。
§11 版本演化、技术栈与局限
11.1 时间线
2026-04-10 GitHub 仓库创建(google-labs-code/design.md)
2026-04-21 npm 首发 @google/design.md 0.1.0,同日 0.1.1
2026-04-30 本文初版发布时点(当时 stars 10,199)
2026-05-26 0.2.0
2026-06-15 0.3.0
2026-07-27 0.4.0(截至 2026-09-30 的最新版)
2026-09-14 仓库最后一次推送
2026-09-30 本文更新时点(GitHub API 读到 stars 28,167)五个月从 0.1.0 走到 0.4.0,README 从早期版本的少量规则扩到现在的 11 条,export 从单一 tailwind 别名扩出 json-tailwind/css-tailwind/dtcg 全家桶。格式本身停在 alpha,规范明说「Expect changes to the format as it matures」。
11.2 技术栈
CLI 的依赖刻意保持轻量:Markdown 解析用 unified + remark-frontmatter + remark-parse,YAML 用 yaml,schema 校验用 zod,命令行框架用 citty。仓库是 bun + turbo 管理的 monorepo,CLI 代码集中在 packages/cli/,examples/ 下带三个完整示例(atmospheric-glass、paws-and-paths、totality-festival),每个都附 DESIGN.md、Tailwind 配置和 DTCG tokens.json 三件套。
11.3 局限
- alpha 状态:spec、token schema 和 CLI 都在活跃开发,升级可能要改文件;
- 组件规范未定型:官方注记原话是「actively evolving」,不同领域的组件建模差异靠弹性容纳,暂时没有强结构;
- 语义色 token 无正式定义:
on-primary、on-secondary这类 Material 风格语义名不在 schema 里,只是非规范性推荐名单上有surface/on-surface/error几个;用了也不报错(§10),但没有校验保障; - 无主题切换机制:暗色/亮色模式的双 token 集尚未支持,需要主题的项目得自己想办法;
- 不参与 Google 漏洞奖励:README 的 Disclaimer 明确此项目不在 Google OSS VRP 范围内,安全研究视角下这是个独立项目,不是有 Google 全套保障的产品。
§12 总结与采用建议
DESIGN.md 把三件事压进了一份纯文本文件:精确性(token 给无歧义的数值,linter 抓悬空引用和对比度)、可评审性(设计变更走 git diff 和 code review,diff 命令直接判回归)、互操作性(export 到 Tailwind 和 W3C DTCG,不锁死在任何工具里)。它的 CLI 设计明显是给 Agent 和 CI 用的——JSON 输出、退出码语义、spec 注入,每个接口都能直接进自动化链路。
采用顺序建议:
- 个人项目、新项目:直接开写。一份十几行的 front matter 加几段 prose,配上
spec注入,Agent 生成 UI 的稳定性立刻不同; - 已有设计系统的团队:从 export 切入。先用 DTCG 或 Tailwind 导出把现有 token 接过来,让 Agent 消费导出物,再逐步把事实来源(source of truth)迁到 DESIGN.md;
- 多 Agent 协作、组件超过 20 个、设计需要版本管理的团队:这是回报最大的场景。lint 进 CI 当门禁,diff 当回归检测,设计系统的变更流程从此和代码同构;
- 页面只有一两个、视觉复杂度低的项目:先别用。维护一份规范文件的成本高于它消除的随机性,这个工具的价值随项目规模非线性增长。
一句话收尾:DESIGN.md 不解决「AI 会不会做设计」,它解决「AI 做设计时听谁的」。在 Agent 大规模写前端的当下,这个问题的答案值得被写成一个文件、提交进仓库、跑在 CI 里。
§13 常见问题
1. 有了 Tailwind 配置,还需要 DESIGN.md 吗?
参考答案
两者是源与产物的关系。DESIGN.md 是带 lint、diff 和设计理由的事实来源,Tailwind 配置是 export 的产物之一。只维护 Tailwind 配置,丢掉的是:引用完整性校验、WCAG 对比度检查、版本间回归检测,以及给 Agent 的「为什么这么设计」。反过来说,如果团队只想让 Agent 拿到颜色值,导出的 theme.json 也确实够用——价值取决于你要不要「设计系统作为被评审的代码」。
2. Agent 会自动读取项目里的 DESIGN.md 吗?
参考答案
不会。DESIGN.md 只是文件,没有任何框架强制 Agent 读它。官方给的路径是主动注入:用 npx @google/design.md spec 把规范文本塞进系统提示,或在 AGENTS.md / CLAUDE.md 里写明「生成 UI 前先读 DESIGN.md」。文件里写了什么和 Agent 知不知道,是两件事。
3. 我想用 on-primary 这类语义色 token,安全吗?
参考答案
能用但无保障。schema 的 colors 接受任意 token 名(未知名合法值即接受),非规范性推荐名单里也出现了 on-surface、error。但 on-primary 这类名字没有进入正式 schema,linter 对它们不做任何语义检查,missing-primary 之类规则也只认 primary。自己用没问题,跨团队对齐时要在 prose 里写清语义。
4. 现在把生产设计系统迁上来,会不会明年就大改?
参考答案
有这个风险,规范自己承认——格式版本是 alpha,「Expect changes to the format as it matures」。缓解手段:schema 有 version 字段预留版本追踪;核心结构(front matter + 八段式)自 0.1.0 以来保持稳定,变动集中在规则数量和导出格式这类加法;spec 与 lint 都是官方命令,格式变了跟着升级 CLI 即可,文件本体大概率只是小修。稳妥做法是先在非关键项目试跑一个迭代周期。
§14 事实核验与引用
- 仓库数据(stars 28,167 / forks 2,280 / Apache-2.0 / created_at 2026-04-10 / pushed_at 2026-09-14)与项目描述:GitHub API,2026-09-30 读取。
- 格式规范全文(token 类型、schema、八段式、组件属性、omitted、Consumer Behavior 表、推荐命名):docs/spec.md @ main,2026-09-30 读取;官方渲染版 stitch.withgoogle.com/docs/design-md/specification(同为仓库 homepage)。
- CLI 四命令的选项、输出示例、退出码、Windows
designmd别名、ENOVERSIONS排查、11 条 lint 规则表、互操作格式说明:仓库README.md@ main,2026-09-30 读取。 - 「Never throws」解析契约、两种 YAML 嵌入、
NO_YAML_FOUND/YAML_PARSE_ERROR/DUPLICATE_SECTION错误码、sourceMap 顶层粒度、LintReport六字段(含tailwindConfig/sections/documentSections)、summary.infos键名:packages/cli/src/linter/下parser/handler.ts、lint.ts、index.ts源码 @ main,2026-09-30 读取;11 条规则的规则名与严重级别另与linter/linter/rules/目录文件名逐一对照。 - npm 包版本史(0.1.0 → 0.4.0 及各发布日期)、依赖清单(citty、remark-frontmatter、yaml、zod 等):npm registry
@google/design.md元数据,2026-09-30 读取。 - Atmospheric Glass 示例(front matter 片段、Tailwind
theme.extend结构、DTCG$type/$value/colorSpace结构):examples/atmospheric-glass/下DESIGN.md、tailwind.config.js、design_tokens.json@ main。 - 版本时间线:GitHub Releases(0.1.0 至 0.4.0 共 5 个 release);stars 历史对比取本文初版(2026-04-30,10,199)与本次更新(2026-09-30,28,167)两个时点。
- 时效声明:stars/forks、版本号、规则数量均为时点数据,以仓库与 npm 实时值为准;格式处于
alpha,schema 与 CLI 细节随版本演化,与本文冲突处以官方 spec 为最终依据。
参与讨论
使用 GitHub 登录。欢迎补充事实、异议与实践。
讨论暂时无法加载。