prefers-color-scheme 在 ios 12/android 9 及以下不支持,需用 localstorage 或 url 参数 fallback,并通过 class 切换主题、内联 script 提前设置、顶层初始化 css 变量及声明 color-scheme 来确保深色模式兼容性。

prefers-color-scheme 在 iOS 12 / Android 9 以下根本不返回 true
不是你 CSS 写错了,是浏览器压根没实现这个 API。iOS 13+、Android 10+、Chrome 76+ 才开始稳定支持 window.matchMedia('(prefers-color-scheme: dark)');旧 WebView(比如微信内置浏览器、部分安卓厂商定制壳)会直接返回 matches: false,哪怕系统已设为深色。
验证方法很简单:打开控制台,执行 console.log(window.matchMedia('(prefers-color-scheme: dark)').matches)。如果始终是 false,就别再调媒体查询了——得换策略。
- 不要依赖
@media (prefers-color-scheme: dark)做唯一判断,它在低版本里等于不存在 - 初始化时必须 fallback 到 localStorage 或 URL 参数,例如读
localStorage.getItem('themeMode') - 若两者都为空,再用时间逻辑兜底(如 19:00–6:00 强制暗色),但别默认亮色——用户可能正需要暗色护眼
JS 切换主题时 class 模式比 style.setProperty 更可靠
很多人用 document.documentElement.style.setProperty('--bg', '#121212') 覆盖变量,但第三方组件、伪元素、SVG、表单控件往往不响应 CSS 变量,只认 class 或 data 属性。
更稳的做法是给 html 元素加 class="dark",然后所有深色规则写成 .dark .btn、.dark input 这种形式。Tailwind 用户请确认 tailwind.config.js 中 darkMode: 'class' 已启用。
- 避免在 JS 中反复 setProperty 大量变量——易漏、难维护、无法触发 UA 样式重绘(比如滚动条颜色)
- 用 class 控制后,CSS 里可直接写
.dark { color-scheme: dark; },让浏览器接管表单、按钮等原生样式 - 切换时务必同步
localStorage.setItem('themeMode', 'dark'),否则刷新就还原
为什么手动切了主题,页面还是闪一下白底
闪屏不是动画问题,是渲染时机没卡准:HTML 解析完、CSS 加载前,浏览器按默认浅色渲染第一帧;等 JS 执行完、class 加上,才重绘——这中间的“白帧”在移动端尤其刺眼。
解法只有一个:把主题判定逻辑提到 HTML 最顶部,用内联 script 阻塞式执行。
- 在
里放一段<script></script>,立刻读localStorage或matchMedia,并同步设置document.documentElement.classList.add('dark') - 别等
DOMContentLoaded或load,那太晚了 - 如果用了服务端渲染(SSR),更得在 HTML 输出阶段就把
class="dark"写进标签里
media 查询和 class 模式能共存,但优先级必须理清
你可以同时保留 @media (prefers-color-scheme: dark) 和 .dark class,但它们不是“或”的关系,而是“用户选择 > 系统偏好”的覆盖链。
CSS 层叠顺序决定谁赢:.dark body 的优先级天然高于 @media (prefers-color-scheme: dark) body,因为 class 是显式声明,媒体查询只是条件包裹。
- 不要在媒体查询里定义新变量名(如
--bg-dark-only),否则用户切回浅色时该变量丢失,fallback 会崩 - 所有变量必须在顶层
:root初始化(以浅色为准),媒体查询和 class 规则都只做覆盖,不新增 - 最易被忽略的是
color-scheme声明:没加html.dark { color-scheme: dark; },iOS 表单控件依然白底黑字
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











