最轻量可靠的深色主题方案是用 prefers-color-scheme 媒体查询配合 :root 中的 css 变量,无需 js 切换类,自动响应系统设置,性能高、兼容好、易维护。

直接用 prefers-color-scheme 媒体查询 + :root 中的 CSS 变量,是最轻量、最可靠、浏览器原生支持的深色主题实现方式。它不依赖 JS 切换类,也不需要手动注入样式或强制渲染,适配性好、性能高、可维护性强。
为什么不用 class 切换而用 CSS 变量?
类切换(如 body.darktheme)适合需要 JS 主动控制、支持用户手动覆盖系统偏好、或需兼容老浏览器的场景;但如果你的目标是「响应系统设置自动切换」,CSS 变量配合媒体查询更干净:变量定义一次,所有用到的地方自动更新,没有 JS 执行延迟,也不用担心 class 漏加/多加/冲突。
-
prefers-color-scheme是浏览器原生能力,2019 年起主流浏览器已全量支持(Chrome 76+、Firefox 67+、Safari 12.1+、Edge 79+) - 变量写在
:root下,天然全局可用,color: var(--text-color)比body.darktheme p { color: #eee; }更易复用和批量调整 - 无需监听
matchMedia或操作 DOM,减少 JS 负担,避免 SSR 渲染与客户端不一致问题
如何用 :root + prefers-color-scheme 定义两套变量?
把颜色、背景、边框等可变属性统一抽成变量,在浅色和深色媒体查询中分别赋值。注意:必须在 :root 中定义默认值(浅色),再用媒体查询覆盖深色值——不能反过来,否则未匹配时变量会失效。
/* 默认(浅色)变量 */
:root {
--bg-color: #fff;
--text-color: #333;
--border-color: #ddd;
--card-bg: #f9f9f9;
}
<p>/<em> 深色模式覆盖 </em>/
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #121212;
--text-color: #e0e0e0;
--border-color: #333;
--card-bg: #1e1e1e;
}
}</p><p>/<em> 全局使用 </em>/
body {
background-color: var(--bg-color);
color: var(--text-color);
transition: background-color 0.3s, color 0.3s;
}</p><p>.card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
}</p>
关键点:@media (prefers-color-scheme: dark) 必须写在 :root 外层,不能嵌套进选择器里;变量名保持语义化(如 --text-color 而非 --dark-text),避免逻辑倒置。
如何让深色模式也支持用户手动覆盖系统设置?
仅靠 prefers-color-scheme 无法让用户「强制深色」或「强制浅色」——它只读取系统偏好。若需提供手动开关,得引入 JS 控制一个自定义数据属性(如 data-theme="dark"),再用属性选择器覆盖媒体查询:
/* 默认按系统 */
:root { --bg-color: #fff; }
<p>@media (prefers-color-scheme: dark) {
:root { --bg-color: #121212; }
}</p><p>/<em> 用户手动指定时优先级更高 </em>/
:root[data-theme="dark"] {
--bg-color: #121212;
}
:root[data-theme="light"] {
--bg-color: #fff;
}</p>
JS 切换只需一行:document.documentElement.setAttribute('data-theme', 'dark')。注意:此时要禁用媒体查询的自动响应,可在 JS 中先移除 prefers-color-scheme 的影响(比如加个 !important 覆盖规则,或用更具体的选择器)。
常见错误:变量没生效或切换卡顿
变量未生效通常是因为:
- 变量名拼错(
var(--bg-colour)→ 应为var(--bg-color)) - 媒体查询写在了
:root内部(:root { @media (...) {...} }是无效语法) - 没有给过渡属性加
transition,导致颜色突变(尤其背景色) - 某些元素用了内联
style或高优先级!important,强行覆盖了var()
深色切换卡顿往往源于大量重绘——避免对 body 直接设 background-image 或复杂渐变;把动画限制在 color、background-color、opacity 这类可 GPU 加速的属性上。
真正难的是边界情况:比如深色模式下 SVG 图标颜色怎么同步?表单控件(input、select)如何保持可读性?这些没法靠变量一劳永逸,得结合伪元素、currentColor 或额外 class 单独处理。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











