用 :root 定义两套颜色变量需统一加 --dark- 和 --light- 前缀,语义化命名如 --color-surface,覆盖 color/background/border/box-shadow 等属性,并用 prefers-color-scheme 媒体查询兜底,配合 .theme-dark/.theme-light class 切换,操作 document.documentelement.classlist,存 localstorage 防闪烁,声明 color-scheme: light dark 保证原生控件适配。

怎么用 :root 定义两套颜色变量
核心是把所有主题色抽成 CSS 变量,放在 :root 里,再通过 class 切换作用域。别直接写死 background-color: #1e1e1e 这种值,否则换主题时得全局搜替换。
常见错误:在 :root 里只写暗色值,亮色靠 JS 动态改 —— 这样无法利用浏览器原生媒体查询回退,也不利于 SSR 渲染首屏。
- 暗色主题变量统一加
--dark-前缀(如--dark-bg),亮色用--light-,避免命名冲突 - 主色调变量名要语义化,比如
--color-surface比--bg-1更易维护 - 把
color、background-color、border-color、box-shadow等常用属性都覆盖到,漏掉一个就可能在某处“闪白”或“闪黑”
如何用 prefers-color-scheme 自动匹配系统偏好
用户没手动切主题时,得靠这个媒体查询兜底。它不是“可有可无”的增强功能,而是现代 Web 的基础体验要求。
容易踩的坑:只监听 dark,忽略 no-preference;或者把 @media (prefers-color-scheme: dark) 写在变量定义之后,导致变量被覆盖失败。
-
@media (prefers-color-scheme: dark)必须包裹完整的:root变量重定义块,不能只改其中几个 - 加一层
.theme-autoclass 控制是否启用自动匹配,方便用户后续手动覆盖(比如系统设暗色但用户偏爱亮色) - 注意 Safari 对嵌套媒体查询的支持较弱,别把
@media套在@layer或其它规则里
JS 切换主题时为什么必须操作 document.documentElement.classList
因为只有修改 元素的 class,才能让 .theme-dark 或 .theme-light 这类选择器生效,且不影响子元素继承链。
错误做法:用 document.body.classList —— body 不是根作用域,CSS 变量作用域在 :root,body 上的 class 对 :root 无影响;或用 style.setProperty() 逐个改变量 —— 性能差、难维护、无法持久化。
- 切换前先清除旧主题 class:
document.documentElement.classList.remove('theme-dark', 'theme-light') - 把主题存到
localStorage,并在页面加载初期同步读取并应用,避免首屏闪烁 - 监听系统偏好变化:
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ...),但仅当用户未手动设置主题时才响应
为什么 color-scheme: light dark 不能少
这个声明告诉浏览器你的页面支持两种主题,会直接影响表单控件(<input>、<select></select>)、滚动条、甚至焦点 outline 的默认样式。不加它,即使 CSS 变量全对,原生控件仍可能违和。
兼容性注意点:IE 完全不支持,但 IE 本身不支持 prefers-color-scheme,所以加了也无副作用;Firefox 96+、Chrome 93+、Safari 15.4+ 均已稳定支持。
- 必须写在
:root规则里,写在.theme-dark类里无效 - 值只能是
light、dark或两者空格分隔,不能写auto或其他字符串 - 它不控制你自己的 CSS 变量,只影响浏览器“画布之外”的渲染行为,属于必要但常被忽略的收尾动作
真正麻烦的从来不是切主题,而是确保所有边界场景都被覆盖:深色模式下 SVG 的 fill 颜色、disabled 状态的透明度、focus-visible 的 outline 颜色、甚至 canvas 绘图时用的 CSS 变量——这些地方一旦漏掉,用户就会在某个按钮上突然看到刺眼的白边。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











