你看看
开篇小故事: 一天下午,产品经理跑过来找你说:「品牌色要换了,需要把这种蓝改成那种蓝,下周要上线。」
你打开作图软件,开始全局搜索 #4A90D9 这个色值。然后你替换了按钮、替换了导航栏、替换了图标、替换了输入框的选中态、替换了进度条、替换了标签页的激活线、就这样,你以为改完了,于是交付给研发。
上线一周后,你突然发现:首页的卡片标题还是旧颜色,用户个人页的链接也没改,空状态插图里有个图形还是旧色。
你重新打开文件,搜索颜色,又陆续找到七处。再改,再交。就这样又过了几天,你又陆续发现两处。
这件事让你非常恼火,因为它花了你将近两天的时间去完成这件事。但从逻辑上讲,修改一个颜色本来应该只需要改一个地方即可。
一、直接用色值的问题
设计文件里,颜色通常以两种方式存在。
一种是直接写色值,比如某个按钮背景色是 #4A90D9,某个文字是 #333333,直接输入进去。这是大多数设计师一开始的工作方式,因为这样做很快、直接、没有额外步骤。
另一种是用颜色样式(或变量):先建立一个命名的颜色,比如叫「主色/500」,然后把这个样式应用到按钮上。改的时候,修改「主色/500」这个定义,所有引用它的地方会自动更新。
第二种显然更好维护,但很多团队做的其实是一个变体:颜色有名字,但名字描述的是外观,不是职责。
比如:
•蓝色/中 → 按钮背景
•蓝色/中 → 选中态边框
•蓝色/中 → 进度条填充
•蓝色/中 → 链接文字颜色
这样颜色统一了,名字也有了,但这个名字只能告诉你「它是蓝色」,不能告诉你「它在哪里做什么」。
四个元素都叫「蓝色/中」,看起来颜色能够做到统一。但有一天你想让链接颜色比按钮深一点,这就很难做到,因为它们指向了同一个名字,改一个就的全改了。到了深色模式,问题就会更明显:「蓝色/中」这个名字里没有任何信息告诉你,它在深色背景上应该变成什么。
不管是直接写色值还是用外观命名,问题的根源是一样的:它们描述的都是「这个颜色是什么」,但界面里真正需要的是「这个颜色是用来干什么的」。
二、语义色是什么
「语义」这个词听起来抽象,但说白了就是这样一句话:名字要说清楚这个东西是做什么用的。
在颜色命名上,「语义」指的是颜色的名字应该表达它在界面里承担的职责,而不是它的外观特征。举个例子:
•blue-500——这是个外观名,告诉你这是蓝色系第五级。它描述颜色本身
•button/primary/background——这是个语义名,告诉你这是主要按钮的背景色。它描述颜色的职责。
语义色系统通常建立在两层结构上。
第一层:调色板层(Palette) 这一层存储颜色的原始值,用外观描述命名:blue-100、blue-200……blue-900;gray-50、gray-100……gray-900;red-500、green-600,诸如此类。
这一层是颜色的「原材料库」。它只管颜色是什么,不管颜色在哪里用。
第二层:语义层(Semantic) 这一层不存储颜色值,只存储映射关系:把调色板层里的颜色,分配给具体的界面职责。
比如:
•button/primary/background → 引用 blue-500
•button/primary/text → 引用 gray-50
•text/primary → 引用 gray-900
•text/secondary → 引用 gray-600
•surface/default → 引用 gray-50
•surface/elevated → 引用 white
界面里的所有元素,只使用语义层的名字,不直接引用调色板层的值。
这个分层带来的好处是,当你需要改动某类颜色时,你只需要修改语义层里的映射,不需要逐个找界面元素。
三、品牌色换色
回到开头的故事。品牌色要从 #4A90D9 换成 #2563EB。
如果没有语义层,你需要找出所有使用了旧品牌色的界面元素,逐一替换。这里的问题是,「主按钮背景」「导航激活态」「链接颜色」「进度条填充」「选中边框」虽然视觉上都是用的品牌蓝,但它们可能是四个不同的设计师在不同时间做的,有些用了颜色样式,有些直接写了色值,有些引用的是不同的颜色名。没有一个统一抓手,直观告诉你「这就是品牌色」,你只能靠全局搜索色值来找,而且很难保证找全。
如果有语义层,你只需打开调色板,把 blue-500 的值从 #4A90D9 改成 #2563EB,就完成了。
所有引用了 button/primary/background、link/default、border/focus 等语义 token 的界面元素,因为它们背后映射的都是 blue-500,全部自动更新。一次改动,全局生效。不需要找,也不会漏,不需要协调四个设计师确认有没有进行过特殊处理。
这就是语义层的第一个价值:让「改色」这件事的操作范围和它的逻辑范围一致。改品牌色,逻辑上只是「换一个颜色」,操作上也应该只需要改一个地方。
四、深色模式
深色模式从来就不是「把界面变深」,也不是「把颜色反过来,这件事我们在之前的文章里讲过。但从颜色系统的角度看,还有一件事需要讲清楚,那就是深色模式的本质,是同一套语义名对应不同的颜色值。
浅色模式下:
•surface/default = #FFFFFF(纯白背景)
•text/primary = #111111(深色文字)
•border/default = #E5E5E5(浅灰边框)
深色模式下:
•surface/default = #1A1A1A(深色背景)
•text/primary = #F0F0F0(浅色文字)
•border/default = #333333(深灰边框)
你会发现名字完全一样,但背后映射的颜色值会跟随模式切换。
界面里的每一个元素,只引用语义名(surface/default、text/primary),不直接写色值。切换到深色模式时,语义名不变,只有名字背后的值发生了变化,界面整体跟着切换。
如果没有这套结构,你要支持深色模式,就得给每个元素单独指定深色模式下的颜色——每个背景、每条文字、每条边框都要手动维护两套颜色。这不是做一次深色模式,而是把整个界面再重新做一遍。
语义层让设计师可以描述一个元素在不同环境下的颜色意图,而不是它在某一时刻的颜色值。
五、语义名的命名逻辑
理解了语义色的概念之后,很多设计师遇到的下一个困惑是,那就是怎么给颜色起名字?
颜色命名有一个核心原则,那就是名字要描述「这个颜色是用来做什么的」,不描述「这个颜色看起来是什么」。以下是几组命名对比:
外观命名(不好)语义命名(好)bluebutton/primary/backgroundlight-bluelink/defaultdark-graytext/primarymedium-graytext/secondarylight-graysurface/subtleredstatus/error/default
外观命名的问题是:当你决定把品牌色从蓝色换成绿色,所有叫「blue」的 token 名字就不准确了,你要么一起改名(牵一发动全身),要么忍受一个叫「blue」却是绿色的 token(这比没有名字更有误导性)。
语义命名不依赖外观,名字永远准确——不管背后的颜色值怎么变,「button/primary/background」永远是「主要按钮的背景色」。
常见的命名结构
语义 token 的命名通常由两到三段构成:
[类别] / [子类别或层级] / [状态或属性]
常见的类别:
•button — 按钮
•text — 文字
•surface — 背景面(容器、面板)
•border — 边框和分割线
•icon — 图标
•status — 状态色(错误 / 警告 / 成功 / 信息)
常见的层级或子类:
•primary — 主要 / secondary — 次要 / tertiary — 再次一级
•default — 默认状态
•subtle — 弱化版本(更浅、更轻)
•inverse — 反色版本(通常用于深色背景上)
常见的状态:
•default — 默认 / hover — 悬停 / pressed — 按下 / disabled — 禁用
•foreground — 前景(在该背景上的内容颜色)/ background — 背景
组合起来的例子:
•button/primary/background — 主要按钮的背景色
•button/primary/background/hover — 主要按钮 hover 状态的背景色
•button/primary/foreground — 主要按钮上的文字颜色
•text/secondary — 次要文字颜色
•surface/overlay — 遮罩层的背景色
•status/error/background — 错误状态的背景色
•status/error/foreground — 错误状态的文字颜色
命名不需要追求一套「标准格式」,每个团队可以根据自身产品的复杂度,调整层级深度。简单的产品可能三四十个语义 token 就够了,复杂的设计系统可能有几百个。
命名核心关键是:名字对团队里任何人来说都是自解释的——看到名字,就能知道它用在哪里,不需要查文档。
六、调色板层和语义层的关系
调色板层存颜色的原始值,语义层存映射关系——前面讲过了。但两层之间还有一条重要的使用规则。
1. 调色板层是颜色的原材料库
调色板存储所有颜色的原始值,按色相和明度编号命名:blue-50、blue-100、blue-200……一直到 blue-900。这就是色阶系统——10 个明度梯度,名字就是编号,不含任何使用意图。
调色板层有一条重要的使用规则:它不能直接出现在界面元素上。调色板层的颜色,只通过语义层「转发」到界面上。界面元素引用的是语义 token,语义 token 背后才是调色板值。
2. 为什么要有这个限制
因为一旦你的界面元素直接引用了 blue-500,当你想修改「主要按钮」的颜色时,你无法区分「这个 blue-500 是主要按钮的颜色」还是「这个 blue-500 是链接的颜色」——它们引用的是同一个值,无法分开调整。
通过语义层中转,这个问题消失了:button/primary/background 和 link/default 都引用 blue-500,但它们是两个独立的语义 token。如果有一天你想让链接颜色比按钮颜色稍深,你只需要把 link/default 指向 blue-600,按钮则不受影响。
3. 一个调色板值可以被多个语义 token 引用
gray-900 可能同时是 text/primary(主要文字颜色)和 icon/default(默认图标颜色),因为在大多数浅色界面里,文字和图标共用深灰色。这一点也没有问题。两个语义 token,各自独立命名,但当前都指向同一个调色板值。如果将来你想把图标改成稍浅一些的颜色,修改 icon/default 的映射就好,文字颜色不受影响。
七、在 Figma 里如何使用
理解了概念,现在看怎么在 Figma 里落地。Figma 的 Variables(变量)功能是实现语义色系统的主要工具。
第一步:建立调色板 Collection
打开 Variables 面板,新建一个 Collection,命名为「Palette」(或「色板」)。在这里建立所有颜色的原始值:
•把你的品牌主色阶放进来:blue/100、blue/200……blue/900
•把中性色阶放进来:gray/50、gray/100……gray/900
•把功能色放进来:red/500、green/500、yellow/500……
每个变量存储一个颜色值(HEX 或 RGB)。这个 Collection 只有一个 Mode(因为调色板值本身不随模式变化——blue-500 永远是那个蓝色,它的值不会因为深浅模式而改变)。
第二步:建立语义 Collection
新建第二个 Collection,命名为「Semantic」(或「语义色」)。这个 Collection 需要建立两个 Mode:「Light」和「Dark」。
在这里建立所有语义 token,命名遵循上文讲的结构:
•surface/default
•surface/subtle
•text/primary
•text/secondary
•button/primary/background
•button/primary/foreground
•border/default
•……
每个语义 token 的值,不直接填写颜色,而是「引用」调色板 Collection 里的变量。在 Light Mode 下,surface/default 引用 gray/50(或 white);在 Dark Mode 下,surface/default 引用 gray/900。
第三步:在组件里使用语义 token
建立或修改组件时,把所有填色、描边颜色、文字颜色,都改为引用语义 Collection 的变量。不要引用调色板 Collection 的变量,更不要直接写色值。
这一步做完之后,切换 Mode(Light ↔ Dark),整个界面的颜色会随之切换。修改调色板里的 blue-500 值,所有引用了以它为基础的语义 token 的界面元素都会更新。
关于 Figma Variables 的一点补充
Variables 功能在 Figma 的付费版本里有完整支持,免费版有一定限制但基本功能可用。对于中小型项目,用 Color Styles(颜色样式)也可以实现类似的效果,只是没有多 Mode 切换,深色模式需要手动切换颜色库。规模较大、有深色模式需求的项目,建议迁移到 Variables。
迁移的工作量通常集中在把已有的颜色样式整理成两层结构这一步——这部分需要设计团队对齐命名规范,也是设计系统建设中最耗时但最值得投入的基础工作之一。
八、语义色不是规范文档
颜色的「名字」和「值」承载的是不同层面的信息,把它们分开管理,界面才能响应变化。
色值是数据,是颜色在屏幕上的精确呈现。色值本身没有意图,它不知道自己在按钮上还是在背景上,不知道自己代表主要行为还是次要行为,不知道自己在浅色模式里还是深色模式里。
语义名是意图,是设计师对「这个颜色在这里做什么」的描述。它不随外观变化——即使明天品牌色从蓝变成绿,button/primary/background 仍然是「主要按钮的背景色」,只是背后指向了一个绿色的调色板值。
当一个设计团队共用同一套语义 token,他们共享的不只是颜色,而是对颜色职责的理解和约定。设计师 A 和设计师 B 在做不同页面,但他们都知道「主要按钮用 button/primary/background」,所以两个人做出来的页面,按钮颜色天然一致,不需要对色。
当设计师和开发工程师对接时,语义 token 的名字也是一种沟通语言——「这个按钮用了 button/primary/background」,工程师知道这对应什么,不需要对着设计稿拿取色器取色值。
这才是语义色系统长期价值所在:它把颜色从「一个外观值」变成「一个可以被团队共同引用和维护的约定」。 一旦建立起来,改色、适配深色模式、跨页面保持一致——这些本来耗时的事,都变成了机械性的简单操作。