设计令牌必须统一定义在 :root 下,通过语义变量(如 --cmc-primary-color)映射框架变量(如 --el-color-primary),并分层命名、支持暗黑模式与多主题切换。

硬编码颜色值(比如直接写 #409eff 或 rgb(64, 158, 255))不是“写得不对”,而是把设计决策锁死在代码里——改一个色,要翻十几处文件,暗黑模式、多主题、Figma同步全成空谈。
为什么 :root 是设计令牌的唯一合法起点
CSS变量只有挂载在 :root 下才能被全局读取,组件内零散声明 --color-primary 看似方便,实则让后续主题切换失效。Element Plus 的 --el-color-primary、Semi Design 的 --semi-color-primary 都依赖这个前提。
- 所有业务级语义变量(如
--cmc-primary-color、--semi-text-primary)必须统一收口到src/assets/styles/var.scss或design-tokens.css中的:root块里 - 禁止在
.vue单文件组件的<style></style>里重复定义同名变量,否则会覆盖全局值且无法被其他组件感知 - 若用 SCSS,
$primary-color: #004889只能用于编译期计算(如lighten($primary-color, 10%)),不能替代运行时可变的--cmc-primary-color
如何把 --el-* 这类框架变量“翻译”成业务语义
Element Plus 内部用 --el-color-primary 控制按钮、输入框、标签等所有主色元素,但你不能直接改它——这会让业务逻辑和 UI 框架强耦合。正确做法是建一层映射:
:root {
--cmc-primary-color: #004889;
--el-color-primary: var(--cmc-primary-color);
}
- 映射层(如
element-theme.scss)只做一件事:把框架变量绑定到你的业务变量上 - 当设计师要求把品牌色从蓝色改成紫色,只需改
--cmc-primary-color一行,所有--el-*衍生样式自动更新 - 避免直接覆盖
.el-button--primary类选择器加!important,那属于“覆盖链地狱”的典型症状
暗黑模式下颜色值怎么不崩
暗黑模式不是靠 JS 切换 class 再重写一堆 background 和 color,而是靠两套 :root 变量 + 媒体查询或 JS 动态注入:
@media (prefers-color-scheme: dark) {
:root {
--cmc-bg-color: #1f2937;
--cmc-text-primary: #f9fafb;
--el-color-primary: var(--cmc-primary-color);
}
}
- 媒体查询方案简单,但仅支持系统级暗色偏好;如需手动开关,得用 JS 动态切换
document.documentElement.setAttribute('data-theme', 'dark'),再配合 CSS 层叠 - 关键点:所有组件必须用
var(--cmc-*),而不是直接读--el-*—— 否则暗色模式下--el-color-primary还是亮色值,按钮背景就变成蓝底蓝字 - 别忘了中性色(
--cmc-neutral-border、--cmc-neutral-text)也要配齐暗色系,否则边框和文字会糊成一片
Token 命名最容易被忽略的陷阱
很多人以为命名只要“看得懂”就行,比如 --color-blue 或 --btn-primary-bg,但这两种都埋了雷:
-
--color-blue是原始值命名,违反语义原则——如果哪天品牌色换成紫色,你还留着blue? -
--btn-primary-bg是组件绑定命名,导致复用困难——同样一个主色,按钮、链接、标签、加载指示器都要各写一遍? - 正确路径是分层:原始色(
--color-primitive-blue-500)→ 语义色(--color-semantic-action-primary)→ 组件色(--button-primary-bg),每一层都可独立维护 - 哪怕项目初期只有一套主题,也建议按三层结构组织 JSON 或 SCSS 文件,否则半年后加暗色模式时你会想重写整个样式系统
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











