CSS 变量:让设计系统真正落地的那把钥匙
很多团队都号称有「设计系统」,但改一次主色,还是得满仓库搜索替换。这通常不是设计系统的问题,而是变量分层没做对。
一、问题:改了 20 个文件还没改完
先看一段很常见的代码:
.button-primary {
background: #5b5bd6;
border: 1px solid #4a4ab8;
}
.link {
color: #5b5bd6;
}
.badge {
background: rgba(91, 91, 214, .12);
color: #5b5bd6;
}
这段代码里,同一个颜色以三种形式出现。一旦品牌色微调,你需要全部重找一遍,而且极容易漏掉 rgba 那一种。
二、三层变量结构
我的做法是把变量分成三层,各司其职:
/* 第一层:原始调色板 —— 只描述颜色本身,不带含义 */
:root {
--indigo-500: #5b5bd6;
--indigo-600: #4a4ab8;
--coral-500: #e0607e;
--gray-50: #f7f7fb;
--gray-900: #171a2b;
}
/* 第二层:语义变量 —— 描述用途,业务代码只用这一层 */
:root {
--color-accent: var(--indigo-500);
--color-accent-hover: var(--indigo-600);
--color-bg: var(--gray-50);
--color-text: var(--gray-900);
}
/* 第三层:组件变量 —— 只在需要局部定制时使用 */
.card {
--card-pad: 1.35rem;
padding: var(--card-pad);
}
分层的价值在于:换色只改第一层,改语义只改第二层,业务代码永远只碰第三层或第二层。层级清晰之后,一次主题调整从「半小时」变成「三十秒」。
三、语义化命名,而不是描述性命名
这是最常见的误区。对比一下:
- ❌
--blue-button:描述的是颜色,一旦按钮变成绿色,这个变量名就成了谎言。 - ✅
--color-primary-action:描述的是用途,颜色怎么换名字都成立。
变量名应该回答「它用来做什么」,而不是「它看起来像什么」。
四、暗色模式其实就是覆盖一层
因为业务代码只引用语义变量,暗色模式要做的事情就变得极其简单——只需要覆盖第二层:
html[data-theme="dark"] {
--color-bg: #0e1017;
--color-text: #e9ecf5;
--color-accent: #8b8bfa;
}
整个站点就完成换肤了,一行组件样式都不用改。这也是本站正在用的方案,你点右上角的图标就能看到效果。
五、几个容易踩的坑
- 别把变量当成万能胶。间距、字号这类需要参与计算的数值,变量化收益有限,反而降低可读性。
- 注意过渡闪烁。暗色模式切换时如果颜色是硬切的,观感会很廉价,给
body加个transition: background .3s会好很多。 - 避免在变量里做复杂计算。浏览器无法优化的表达式放在变量里,会拖慢重绘。
CSS 变量不是新语法糖,它是一种让「设计意图」可被代码表达的机制。用好了,设计系统才真的能落地。