语义规范体系:YAML里写的不是颜色值是语义令牌
设计规范正经历从文档到代码的范式转变,而语义翻译成为关键突破口。本文深度解析Schema-As-Code方法论,揭示如何通过YAML语义契约实现设计意图的机器可读化,构建包含四大命名空间、三种语义域和不可变边界的完整体系,彻底解决AI生成内容时的语义失控问题。

本文是阶段二《设计师作为”语义翻译者”》的语义规范体系专题。阶段二论证了设计规范从文档转向代码的必要性:传统规范给”人”看,机器无法读取,AI生成内容时不受约束。核心方案是Schema-As-Code——将设计意图翻译成YAML语义契约,让机器在生成前读取并执行约束。
需要首先澄清:YAML里写的不是颜色值,不是文案,是语义令牌。这不是”技术实现”,是”语义翻译”。
本文在此基础上回答”代码格式的规范体系长什么样”:YAML格式标准、语义令牌命名空间、语义域分类、契约库管理,以及到Prompt前缀/Checklist/CI规则的编译输出机制。
阶段一:组件语义快照与模式诊断:AI 生成界面的第一道检查
方法论总纲与开源仓库:把设计规范写成代码格式,是所有 AI 工具的上游约束方法论
一、引言:YAML 里到底写的是什么?
我第一次给别人看我的 YAML 契约时,对方看了一眼说:”这不就是 Design Token 吗?把颜色、字体写成代码格式。”
我说不是。他反问:”那你写的 color_token: status.critical 和 Design Token 的 color: #EF4444 有什么区别?”
这个问题让我意识到:如果我不先讲清楚“语义令牌”和“Design Token”的区别,所有人都会把 Schema-As-Code 理解成“另一种 Design Token 写法”,然后整个方法论就被矮化了。
所以这篇文章必须放在 YAML 格式之前——先理解语义规范体系,再看 YAML 怎么写,顺序不能反。
二、Design Token vs 语义令牌:两个完全不同的东西
Design Token 解决什么问题?
Design Token 管的是”视觉层的一致性”:
# Design Token(Figma / Tokens Studio / Style Dictionary)
color:
primary: “#1677FF”
danger: “#EF4444”
warning: “#F59E0B”
font:
family: “Inter”
size: 14px
Design Token 回答的问题:”这个按钮用什么颜色、什么字体?”
它的作用是:让设计稿和代码里的颜色值保持一致,设计系统更新时,改一处全局生效。
语义令牌解决什么问题?
语义令牌管的是”语义层的一致性”:
# 语义令牌(Schema-As-Code)
semantic_tokens:
error_severity:
fatal:
visual_mapping:
color_token: “status.critical” # ← 不是 #EF4444,是 “致命状态”
motion_token: “pulse.red.urgent” # ← 不是 pulse 2s,是 “紧急脉冲”
icon_token: “alert.octagon” # ← 不是 warning.svg,是 “八边形警告”
语义令牌回答的问题:”这个错误在这个场景下代表什么后果?用户应该怎么理解?”
核心区别:一层 vs 两层

关键洞察:Design Token 是“视觉字典”,语义令牌是“语义字典”。两者互补,不是替代。
用户看到的界面
↓
Design Token(管颜色、字体、间距)
↓
语义令牌(管颜色代表什么、文案传递什么)
↓
机器理解的规则
没有语义令牌,AI 知道“用红色”,但不知道“红色代表致命”。
三、语义令牌的四大命名空间
我设计语义令牌时,把常见的语义场景归纳为四大类,每类一个命名空间:
1. status.* —— 状态级别
回答的问题:“这件事有多严重?”

为什么不是直接写颜色?
因为同一个 status.critical 在不同设计系统里可以映射到不同的色值:
- 设计系统 A:#EF4444(Tailwind 红)
- 设计系统 B:#F5222D(Ant Design 红)
- 设计系统 C:#FF4D4F(DevUI 红)
语义令牌不关心具体色值,只关心语义映射关系。 设计系统更新时,改映射表,语义令牌不变。
2. phase.* —— 认知阶段
回答的问题:“AI 现在处于什么阶段?用户在等什么?”

为什么需要阶段令牌?
Perplexity 的 “Searching…” “Reading…” “Wrapping up…” 让用户困惑——不知道 AI 是在查资料还是开始编答案。
有了 phase.*,系统可以显化认知阶段:”现在处于检索阶段,已找到 5 个来源,其中 4 个一致。”用户知道”这是查出来的,不是猜出来的”。
3. boundary.* —— 边界强度
回答的问题:“系统拒绝用户时,权利边界在哪里?”

为什么需要边界令牌?
Claude 的 “I can’t help with that” 和 “Claude has ended this chat” 在界面上都是”拒绝”——用户不知道对话还在不在、数据丢没丢。
有了 boundary.*,系统区分:软性拒绝(继续聊)vs 强制终止(开新会话)。用户明确知道自己的权利状态。
4. action.* —— 操作性质
回答的问题:“用户点击这个按钮后,后果是什么?”

为什么需要操作令牌?
AI 生成”删除账户”按钮时,默认给蓝色实心”确认”——因为 Prompt 里只说”生成一个删除按钮”,没有说”这是不可逆操作”。
有了 action.destructive,系统知道:这个按钮后果是破坏性的,必须红色空心、必须二次确认、必须输入账户名验证。
四、语义域:组件在什么场景下?
语义令牌定义了”是什么”,但同一个组件在不同场景下,语义可能完全不同。
我设计了三个语义域(Semantic Domain):
observational —— 观察型
特征:用户被动接收信息,不需要操作。
典型组件:错误提示、状态通知、告警卡片。
约束:
- 禁止要求用户输入
- 必须提供明确的退出路径
- 文案必须说明后果(不能只说“出错了”)
transactional —— 交易型
特征:用户主动触发操作,系统必须反馈结果。
典型组件:提交按钮、表单、确认弹窗。
约束:
- 必须提供操作结果反馈
- 成功/失败状态必须明确
- 不可逆操作必须有二次确认
conversational —— 对话型
特征:用户和系统双向对话,上下文持续累积。
典型组件:聊天界面、问答系统、AI 助手。
约束:
- 必须保留对话上下文
- 边界动作必须说明会话状态
- 拒绝时必须提供替代建议
为什么需要语义域?
同一个 Alert 组件:
- 在 observational 域(系统故障)→ 用户被动看,不能要求输入
- 在 transactional 域(表单验证)→ 用户主动填,必须给反馈
- 在 conversational 域(对话中断)→ 上下文要保留,不能说断就断
没有语义域,系统不知道“这个组件在用户流程中扮演什么角色”,约束就会错位。
五、不可变边界:红线
语义令牌定义”应该是什么”,不可变边界定义”绝对不能是什么”。
我把它理解为”语义红线”——一旦触碰,系统必须阻断。
示例:ERR-001 的不可变边界
immutable_boundaries:
– boundary_type: “safety”
rule: “禁止直接执行删除操作而不显示二次确认”
violation_action: “block”
– boundary_type: “clarity”
rule: “禁止在错误状态文案中使用模糊词汇(如’出错了’、’Something went wrong’)”
violation_action: “block”
– boundary_type: “consistency”
rule: “禁止将不同严重级别的错误使用相同视觉表达”
violation_action: “block”
为什么叫”不可变”?
这些边界不是”建议”,是”强制规则”:
- block:一旦触发,直接阻断生成,返回修改建议
- warn:触发后警告,但允许人工确认后放行
- escalate:触发后升级人工审核
与语义令牌的区别:
- 语义令牌 = “ fatal 应该用什么颜色”(推荐性映射)
- 不可变边界 = “fatal 绝对不能和 retryable 用同一种颜色”(强制性约束)
六、完整示例:ERR-001 的语义规范拆解
把上面的概念串起来,看 ERR-001 是怎么从”语义规范”翻译成”YAML 契约”的:
语义规范层(设计师的意图)
设计意图:
– 错误状态必须按后果严重程度分级
– 用户必须知道”多严重”和”该做什么”
– 系统绝对不能让不同级别的错误长得一样
语义规范:
– 状态级别:fatal / transient / retryable / degraded(status.*)
– 语义域:observational(用户被动接收)
– 不可变边界:不同级别不能用相同颜色
语义令牌层(机器可读的标识)
# 语义令牌映射
status.critical → 致命错误 → 红色脉冲
status.neutral → 可恢复错误 → 灰色加载
status.warning → 限流提示 → 黄色倒计时
status.info → 部分可用 → 蓝色提示
YAML 契约层(可执行的规则)
intent_id: “ERR-001”
semantic_domain: “observational”
semantic_tokens:
error_severity:
fatal:
description: “系统级故障,对话上下文可能丢失”
visual_mapping:
color_token: “status.critical” # ← 语义令牌
motion_token: “pulse.red.urgent” # ← 语义令牌
icon_token: “alert.octagon” # ← 语义令牌
user_action:
– label: “刷新页面”
action: “refresh”
– label: “导出历史”
action: “export_history”
llm_constraints:
– “必须明确告知用户对话上下文可能已丢失”
transient:
description: “网络抖动,系统可自动恢复”
visual_mapping:
color_token: “status.neutral” # ← 语义令牌
motion_token: “spinner” # ← 语义令牌
icon_token: “loader” # ← 语义令牌
user_action:
– label: “等待自动恢复”
action: “wait”
llm_constraints:
– “禁止红色背景(避免情绪过载)”
immutable_boundaries:
– boundary_type: “consistency”
rule: “禁止将不同严重级别的错误使用相同视觉表达”
violation_action: “block”
三层关系:
- 语义规范层 = 设计师的意图(人话)
- 语义令牌层 = 机器可读的标识(机器话)
- YAML 契约层 = 可执行的规则(代码话)
七、语义规范与 YAML 契约的关系
不是先有 YAML 格式,再往里填内容。
是先有语义规范体系,再选择 YAML 作为载体。
语义规范(理论层)
├── 语义令牌命名空间(status / phase / boundary / action)
├── 语义域分类(observational / transactional / conversational)
└── 不可变边界原则(安全 / 清晰 / 一致)
↓
YAML 契约(实现层)
├── semantic_tokens 字段(填语义令牌)
├── context 字段(填语义域)
└── immutable_boundaries 字段(填红线)
理解了这个关系,就不会把 Schema-As-Code 当成”另一种 Design Token”,而是”设计意图的语义翻译层”。
八、Before/After:加了语义规范后,YAML 契约长什么样?
Before(没有语义规范,YAML 只写视觉值)
# 错误状态规范(没有语义层)
error:
color: “#EF4444”
bg: “solid”
icon: “warning.svg”
text: “Something went wrong”
button: “重试”
问题:
- 前端看了知道“用红色”,但不知道“红色代表什么”
- AI 看了知道“生成红色按钮”,但不知道“什么场景下该用什么红色”
- 设计系统更新红色色值时,YAML 要全部改一遍
After(有了语义规范,YAML 写语义令牌)
# 错误状态规范(有语义层)
semantic_tokens:
error_severity:
fatal:
visual_mapping:
color_token: “status.critical” # ← 语义令牌,不是色值
motion_token: “pulse.red.urgent”
icon_token: “alert.octagon”
user_action: [“刷新页面”, “导出历史”]
transient:
visual_mapping:
color_token: “status.neutral” # ← 同一组件,不同语义,不同令牌
motion_token: “spinner”
icon_token: “loader”
user_action: [“等待自动恢复”]
改变:
- 前端看了知道“fatal 用 status.critical”,去映射表查具体色值
- AI 看了知道“fatal 必须红色脉冲 + 刷新/导出按钮”
- 设计系统更新色值时,改映射表,YAML 完全不变
九、总结
Schema-As-Code 的 YAML 契约里写的不是颜色值、不是文案、不是 Design Token。
写的是语义令牌——机器可读的语义标识,定义“这个界面元素在这个场景下代表什么”。
四大命名空间:
- status.* —— 状态级别(多严重)
- phase.* —— 认知阶段(在干什么)
- boundary.* —— 边界强度(权利在哪)
- action.* —— 操作性质(后果是什么)
三层语义域:
- observational —— 用户被动看
- transactional —— 用户主动点
- conversational —— 用户和系统聊
一条红线:
- immutable_boundaries —— 绝对不能做什么
理解了这个语义规范体系,再看 YAML 格式,就知道每个字段填的不是视觉值,是语义映射。
本文由 @阿基拉de_Akir 原创发布于人人都是产品经理。未经作者许可,禁止转载
题图来自Unsplash,基于CC0协议

起点课堂会员权益




语义令牌的框架很漂亮,但落地时有个现实问题:设计师和前端对“语义域”的理解可能不一致,同一个Alert在不同人手里定义不同。单纯的YAML约束能否消除这种分歧,还是得靠强制的Lint规则。