必须用 :root 声明 CSS 变量,因其语义明确、优先级高且在 Shadow DOM 和框架组件中更可靠;变量需加前缀如 --theme-primary 避免冲突;所有颜色引用必须用 var(),包括伪元素、表单控件和 SVG;动态换肤应使用 setProperty() 而非 cssText;深色模式需用 @media 直接重写 :root,且默认值必须预先声明。
:root 声明,不能写在 html 或 body 里浏览器把 :root 当作 CSS 作用域的顶层,它语义明确、优先级略高,且在 Shadow DOM 和现代框架组件中更可靠。写成 html { --primary: #007bff; } 虽然多数情况能用,但一旦遇到 Web Components 或 Vue 的 <style scoped>,变量可能无法透传到子树里。
变量名必须以两个连字符开头,比如 --theme-primary,写成 --primary 合法,但加前缀能避免和第三方库(如 Ant Design 的 --ad-primary-color)冲突。
:root 中定义的值支持所有合法 CSS 值:十六进制、hsl()、calc() 表达式都行,但不能是属性名或选择器。命名色(如 blue)要避开——JS 读不到它的数值,后续做深色模式计算或动画过渡会卡住。
var(--theme-primary) 替换,包括伪元素和状态样式只改 :root 里的值没用,关键在“引用”。哪怕只漏一个 .btn:hover 里还写着 background-color: #2563eb;,换主题时这个悬停色就永远卡在旧值上。
立即学习“前端免费学习笔记(深入)”;
::before 和 ::after 里的 background、color、border-color 都得显式加 var()
input:focus、select:disabled、textarea::placeholder 容易被跳过fill 或 stroke 时,必须手动写 fill: var(--theme-primary);
color: var(--theme-primary);
cssText,用 setProperty() 逐个设值JavaScript 动态换肤最常见错误是用 document.documentElement.style.cssText = '...' ——这会清空所有已有内联样式,包括其他 JS 注入的动态样式或框架管理的 class。
正确做法是:
document.documentElement.style.setProperty('--theme-primary', '#1e40af');
setProperty(),不拼接字符串background-color)在 CSS 中提前加 transition: background-color 0.2s;,否则重绘会生硬html.dark),确保 .dark :root 规则权重足够,且放在默认 :root 定义之后@media (prefers-color-scheme: dark) 直接重写 :root
不能写成 :root { @media (prefers-color-scheme: dark) { --theme-bg: #111827; } } ——语法非法,浏览器直接忽略。
正确结构是:
:root { --theme-primary: #2563eb; --theme-bg: #ffffff; --theme-text: #1e293b;}@media (prefers-color-scheme: dark) { :root { --theme-bg: #111827; --theme-text: #f9fafb; }}
注意:所有基础变量必须在默认 :root 中声明初始值,否则页面首次加载时,未覆盖的变量会退回到继承值或 initial,造成大面积空白或错色。媒体查询只覆盖差异项,不是重写整套。