必须用@media (prefers-color-scheme: dark)语法,定义在:root中并通过媒体查询批量更新CSS变量,兼顾兼容性与用户手动切换场景。

prefers-color-scheme 媒体查询怎么写才生效
必须用 @media (prefers-color-scheme: dark),不能漏掉括号或写成 prefers-dark-mode 这类不存在的语法。这个媒体查询只在系统级暗色偏好开启时触发,和浏览器是否支持暗色 UI 无关,但依赖操作系统/桌面环境正确设置(macOS 系统设置 → 通用 → 外观;Windows 设置 → 个性化 → 颜色 → 选择默认应用模式)。
常见错误是把它当 CSS 类名用,比如写成 .prefers-color-scheme-dark { ... } —— 这完全无效,它不是 class,是媒体特性。
CSS 变量定义位置影响作用域和覆盖逻辑
变量必须定义在 :root 里才能全局可用,但要注意:媒体查询内的变量声明不会“覆盖”外层同名变量,而是“重新赋值”。所以推荐把所有主题变量集中定义在 :root,再用媒体查询批量更新:
:root {
--bg: #fff;
--text: #333;
--border: #e0e0e0;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a1a;
--text: #e0e0e0;
--border: #333;
}
}
不推荐在每个选择器里重复写 media 查询,否则维护成本高、易遗漏;也不建议用 JavaScript 动态 setProperty,会绕过原生响应能力,且首次渲染可能闪屏。
立即学习“前端免费学习笔记(深入)”;
如何让暗色模式真正“可感知”而不仅是背景变黑
只改 --bg 和 --text 往往不够——按钮悬停、输入框 focus、阴影、SVG 图标颜色、甚至 hr 或 blockquote 的边框都容易被忽略,导致视觉断裂。
- 检查所有用到
color、background、border-color、box-shadow、fill(SVG)、stroke的地方,统一用变量 - 避免硬编码
#000或#fff,它们在暗色下可能对比度不足或反光刺眼 - 对图片、iframe 等外部内容,可加
filter: invert(1) hue-rotate(180deg)应急,但优先用语义化变量控制背景/蒙层 - 注意
system-ui字体栈在深色下渲染更清晰,可配合font-smooth: auto(部分 Safari 支持)
兼容性与降级策略不能只靠 prefers-color-scheme
prefers-color-scheme 在 Chrome 76+、Firefox 67+、Safari 12.1+ 支持,但旧版 iOS Safari(12.0)仅部分支持,Android WebView 差异更大。如果业务需覆盖更广用户,得准备 fallback:
最简方案是默认用亮色,不声明任何暗色样式——这样老浏览器自然显示为亮色,无异常;若必须支持手动切换,就用 class 控制(如 html[data-theme="dark"]),再用 JS 监听 matchMedia('(prefers-color-scheme: dark)').addEventListener 同步状态,但注意不要覆盖用户主动选择。
真正难处理的是混合场景:比如系统设为暗色,但用户在网站内点了“强制亮色”,这时 prefers-color-scheme 仍为 dark,CSS 变量就得靠 JS 注入更高优先级的 style 标签来打破 cascade —— 这个层级关系很容易出错,建议把用户偏好存在 localStorage,并在页面加载初期就计算最终主题,再一次性注入 :root 变量。


















