语义 Token 与基础 Token

语义 Token 是按用途命名而非按取值命名的设计 Token,例如用 --color-text-danger 而不是 --color-red-600。它通常引用一个基础 Token,因此可以更换底层取值,而无需改动任何引用它的地方。

两个层次

大多数成熟的 Token 体系至少有两层。

基础 Token(也叫原始、全局或核心 Token)保存实际的值,命名是描述性的:

--red-600: #DC2626;
--blue-500: #2563EB;
--space-4: 16px;

语义 Token 保存的是这些值用在哪里的决定,按角色命名:

--color-text-danger: var(--red-600);
--color-action-primary: var(--blue-500);
--space-card-padding: var(--space-4);

组件引用语义层。几乎没有什么应该直接引用基础 Token。

这层间接引用为什么值得

换品牌不再是重写。 改掉 --color-action-primary 指向的目标,所有按钮跟着变。如果组件引用的是 --blue-500,你就得逐个组件去改,而那些出于与"操作"无关的原因用了蓝色的地方还会被改坏。

主题成为可能。 深色模式就是同样的语义名指向不同的基础 Token。没有语义层,就没有可以重新指向的东西。

代码说出了它的意思。 --color-text-danger 告诉读者这个值为什么在这儿。--red-600 什么也没说,而这在有人需要判断改动是否安全时最要命。

常见的失误

团队跳过语义层,是因为第一天它看起来像官僚主义:一共十一个颜色,每个人都记得它们是干什么的。

代价出现在第一次换品牌、第一次做深色模式或第一次做无障碍检查的时候,而那时基础 Token 已经被引用了几百处。事后补一层语义 Token 是机械但磨人的活,没人会排进计划。

第三层

规模更大的体系会加上组件级 Token:--button-primary-background 指向 --color-action-primary,后者再指向 --blue-500。

当某个组件确实需要分叉时它有用,不需要时就只剩间接引用的成本。两层是正确的默认;第三层等真实的场景出现了再加,不要提前铺。

这与 Flash DSM 的关系

生成出来的体系两层都已就位:基础色阶,以及引用它们的文字、背景、边框和状态的语义角色,浅色和深色都有。