prefers-color-scheme 是唯一真正实现系统级自动切换的原生方案,无需 js、按钮或 localstorage,浏览器在系统主题变更时直接重绘样式;移动端自2021年起全面支持,读取系统设置并即时生效,最简写法即用 @media (prefers-color-scheme: dark) 配合 css 变量与 transition。

prefers-color-scheme 是唯一能真正实现“自动切换”的原生方案,不需要 JS 监听、不依赖按钮、不读写 localStorage —— 浏览器会在系统主题变更时直接重绘样式。
为什么只用 @media (prefers-color-scheme: dark) 就够了
移动端(iOS Safari、Chrome Android、Edge Mobile、Firefox for Android)从 2021 年起已全面支持该媒体查询。它不是“猜测”,而是读取系统级设置(如 iOS 设置 > 显示与亮度 > 深色模式,或 Android 设置 > 显示 > 深色主题)。只要用户开了系统深色模式,你的页面就会立即应用对应样式,且无需刷新。
常见误区是以为要配合 JS 才能“生效”——其实完全不用。下面这种写法就是最简、最稳的自动适配:
:root {
--bg: #ffffff;
--text: #333333;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a1a;
--text: #e0e0e0;
}
}
body {
background-color: var(--bg);
color: var(--text);
transition: background-color 0.2s, color 0.2s;
}
注意两点:transition 必须写在非媒体查询的 body 规则里;:root 中的变量定义不能被媒体查询内的同名变量覆盖(除非用 !important,但不推荐)。
prefers-color-scheme 在 WebView 中是否可靠
Android WebView(Chromium 内核)和 iOS WKWebView 均支持该特性,但有关键限制:
- Android WebView 需启用
WebSettings.setForceDark(WebSettings.FORCE_DARK_ON)或系统级深色模式开启后才触发,仅靠 CSS 不保证生效 - iOS WKWebView 默认继承系统偏好,但若网页被嵌入 Hybrid App 且未声明
viewport或禁用了颜色管理,可能 fallback 到no-preference - 部分国产浏览器(如 UC、QQ 浏览器)旧版本会忽略该查询,建议加一层兜底:用
@media (prefers-color-scheme: no-preference)定义亮色默认态
所以真实项目中建议这样组织:
@media (prefers-color-scheme: light) { /* 明确亮色 */ }
@media (prefers-color-scheme: dark) { /* 明确暗色 */ }
@media (prefers-color-scheme: no-preference) { /* 降级为亮色,避免白屏/黑屏 */ }
图片、SVG 和表单控件在夜间模式下的坑
纯 CSS 媒体查询不会自动修复内容层问题,这些地方最容易翻车:
-
<img>在dark模式下常因背景变黑而丢失细节,需单独处理:用picture+source media提供深色优化图,或加filter: brightness(1.2)微调(慎用invert(),会破坏图标语义) -
<input type="range">、<select></select>等原生控件在 iOS 上无法被 CSS 变量控制,必须用appearance: none+ 自定义样式,否则夜间模式下仍显示亮色边框 - SVG 内联图标若用
fill: currentColor,会随文字色自动适配;但若硬编码fill="#333",就得在媒体查询里重写 - 第三方组件(如地图、图表库)通常不响应
prefers-color-scheme,得查其文档看是否提供theme参数或 class 控制
真正难的不是写对媒体查询,而是确保所有视觉元素——包括字体抗锯齿、阴影深度、border-radius 的微妙差异、甚至 SVG path 的描边粗细——在两种模式下都保持一致的可读性与品牌感。系统级自动切换只是起点,手动微调才是落地关键。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











