旧版浏览器不支持 prefers-color-scheme,需用 class + localstorage + 内联 script 在 css 解析前设置主题,并配合 color-scheme 声明以确保原生控件适配。

prefers-color-scheme 在 iOS 12 / Android 9 以下根本不存在
不是你的 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,不能只靠 CSS 变量
很多人用 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 加上,才重绘——这中间的“白帧”在移动端尤其刺眼。
- 解决方案是:在
里插入内联<script></script>,读取localStorage或 URL 参数后立刻给html加class,赶在 CSS 解析前完成 - 别等
DOMContentLoaded或load事件,那时首帧早已渲染完毕 - 配合
<meta name="color-scheme" content="light dark">提前声明,部分浏览器能据此优化初始渲染
color-scheme 和 @media 规则必须同时存在才有效
color-scheme: light dark 本身不改你的 background-color 或 color,它只影响表单边框、<progress></progress> 轨道、::-webkit-scrollbar 等原生控件。漏掉它,<input>、<select></select> 在深色背景下仍是刺眼的白色。
- 必须在
:root或html上声明color-scheme: light dark -
@media (prefers-color-scheme: dark)规则要后置,且所有变量必须先在:root里声明默认值,不能靠媒体查询“新增”变量 - 漏掉任意一个变量(比如
--border-color),对应组件就会失色(如按钮无阴影、输入框无边框)
prefers-color-scheme 是只读信号,无法被 JS 主动触发或模拟;而旧浏览器连这个信号都不发——这时候,class + localStorage + 内联 script 就不是备选方案,是必选项。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











