css 中的 var() 是运行时计算的自定义属性占位符,真正起作用的是 :root 等选择器中声明的 --color-primary 等变量值;换肤本质是切换这些变量的声明值,而非修改 var() 本身。

var() 在 CSS 中不是“变量”,是运行时计算的 CSS 自定义属性
很多人以为 var(--color-primary) 和 JS 里的 let 一样能随时重赋值,其实它只是个“占位符”——真正起作用的是它背后绑定的 :root 或其他选择器里声明的 --color-primary: #007bff。换肤本质是切换这些自定义属性的值,不是改 var() 本身。
实操建议:
- 所有主题色必须统一在
:root声明,避免分散在多个选择器里导致覆盖混乱 - 不要用 JS 直接操作
document.documentElement.style.setProperty('--color-primary', '...')批量改几十个变量——性能差、难维护,应改 class 名再靠 CSS 层级生效 - 深色模式下,
prefers-color-scheme: dark只能响应系统设置,不能触发自定义主题(比如“蓝紫主题”),得靠手动加 class 控制
一键换肤 = 切换根元素 class + 预置多套 CSS 变量
核心逻辑很简单:给 加一个主题 class(如 theme-dark 或 theme-purple),然后用 CSS 选择器批量覆盖变量值。
常见错误现象:
立即学习“前端免费学习笔记(深入)”;
- 换肤后部分组件颜色没变 → 某些组件用了内联 style 或硬编码色值,绕过了
var() - 切换瞬间闪白/闪黑 → 新主题 class 写在旧 CSS 后面但未提前加载,浏览器先渲染旧样式再重绘
- 动态插入的弹窗/Tooltip 颜色错乱 → 它们挂载在
body下,脱离了html的主题 class 作用域
正确做法:
- 把所有主题变量写在独立 CSS 文件里(如
themes.css),和主样式并行加载,不依赖 JS 注入 - 用
html.theme-dark { --bg: #1a1a1a; --text: #e0e0e0; }这种方式定义,别用.theme-dark { --bg: ... }—— 后者只对当前元素生效,子元素要重复继承 - 对挂载到
body的浮层,手动同步主题 class:document.body.classList.add('theme-dark')
IE 不支持 var(),但没必要 polyfill
IE11 完全不识别 var(),连语法解析都失败,CSS 规则整条被丢弃。试图用 PostCSS 插件(如 postcss-custom-properties)静态替换,会丢失运行时换肤能力——因为编译时无法预知用户选哪个主题。
所以实际方案只有两个:
- 明确放弃 IE 支持(现在绝大多数大型项目已这么做)
- 如果必须兼容,就不用
var()做主题,改用预编译多套 CSS(如app-light.css/app-dark.css),JS 切换<link>标签的href,牺牲灵活性保兼容
注意:Sass/Less 的 $color-primary 是编译期变量,和 CSS var() 无关,不能用于运行时换肤。
主题切换不是“改颜色”,是改设计系统语义变量
直接写 --primary-color: #4285f4 是危险的——下次 UI 改版,“primary”可能指按钮、也可能指边框、甚至指文字强调色。一旦语义混用,换肤就会出错。
推荐命名方式:
-
--color-brand-primary(品牌主色,固定用途) -
--color-surface-bg(容器背景) -
--color-interactive-hover(交互态) -
--color-text-secondary(辅助文字)
这样换肤时只需调整这几组语义变量,所有组件自动适配;而不是在每个组件里搜 var(--primary) 然后猜它到底代表什么。
复杂点在于:设计系统越细,变量越多,团队协作时命名一致性越难保证。上线前最好用工具扫描 CSS,检查是否有未定义或拼错的 var(--xxx) 引用。









