CSS自定义变量是声明在:root中的--开头样式占位符,通过var()读取实现全局动态更新;与class切换相比,它无需遍历DOM、不触发重排,但需配合级联覆盖机制,而class切换更利于SSR、调试和局部主题控制。

什么是CSS自定义变量,和普通class切换有啥区别
CSS自定义变量(--primary-color)本质是作用域可控的样式“占位符”,声明在:root里就全局可用,用var(--primary-color)调用。它和写死颜色值或靠JS批量改class不同:改一个变量,所有引用它的地方自动重算,不用遍历DOM、不触发重排、也不依赖框架响应式机制。
关键点在于——变量本身不生效,必须被var()读取才参与渲染;而且它支持级联和覆盖,比如深色模式下在.dark容器里重新声明--primary-color,内部所有var(--primary-color)就自动换色。
怎么声明和切换主题变量(含暗色/亮色两套)
最简可行方案是把两套主题色都预设在:root,再用一个开关类(如.theme-dark)覆盖部分变量:
:root {
--primary-color: #007bff;
--bg-color: #ffffff;
--text-color: #333333;
}
.theme-dark {
--primary-color: #0056b3;
--bg-color: #1a1a1a;
--text-color: #e0e0e0;
}
切换时只需给或加/删theme-dark类:
- JS里用
document.documentElement.classList.toggle('theme-dark') - 别用
setAttribute('class', ...),会清空原有class - 首次加载时,可从
localStorage读取上次主题,再设class
为什么直接改:root变量值不如用class切换
有人试过用document.documentElement.style.setProperty('--primary-color', '#ff6b6b')逐个改变量,但问题明显:
- 每次调用只改一个变量,主题通常含10+颜色,代码冗长易错
- 无法利用CSS级联做“局部覆盖”(比如只让弹窗用暗色,其余保持亮色)
- SSR或静态站点生成时,服务端无法执行JS,
setProperty完全失效
- DevTools里调试时,
:root样式面板看不到被JS动态改过的变量值,排查困难
setProperty完全失效:root样式面板看不到被JS动态改过的变量值,排查困难而class切换是纯CSS行为,服务端能预渲染,DevTools里一目了然,也方便用@media (prefers-color-scheme: dark)自动联动。
实际用的时候容易漏掉哪些细节
主题切换不是改几个颜色就完事,这些地方常出问题:
- 按钮
border、box-shadow、outline这些边框/阴影色,容易只改background和color,结果暗色下看不见焦点框
-
input、textarea等表单控件的默认样式受系统影响大,需显式重置background、color、caret-color
- 图片滤镜(如
filter: brightness(0.8))在暗色下可能过暗,建议用变量控制滤镜强度:filter: brightness(var(--img-brightness, 1));
- 动画过渡要加
transition: background-color 0.2s, color 0.2s,否则变色突兀;但别对所有属性用all,性能差
border、box-shadow、outline这些边框/阴影色,容易只改background和color,结果暗色下看不见焦点框input、textarea等表单控件的默认样式受系统影响大,需显式重置background、color、caret-color
filter: brightness(0.8))在暗色下可能过暗,建议用变量控制滤镜强度:filter: brightness(var(--img-brightness, 1));
transition: background-color 0.2s, color 0.2s,否则变色突兀;但别对所有属性用all,性能差变量名别起太笼统(比如--color-1),优先语义化(--accent-brand、--surface-card),否则半年后你自己都看不懂哪根变量管哪个灰度层级。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











