支持 prefers-color-scheme 的判断方式是执行 window.matchMedia('(prefers-color-scheme: dark)').matches,若始终返回 false 则需 JS + 存储兜底;iOS 13+、Android 10+、Chrome 76+ 才稳定支持 change 事件,旧版 Safari 和微信/厂商 WebView 均不支持或仅部分支持。

prefers-color-scheme 在 iOS 12 / Android 9 及以下根本不会生效,不是写法问题,是浏览器没实现这个 API —— 必须用 JS + 存储兜底。
怎么判断当前系统是否支持 prefers-color-scheme
别猜,直接在控制台执行:window.matchMedia('(prefers-color-scheme: dark)').matches。如果始终返回 false(哪怕系统已设深色),说明环境不支持,立刻切换 fallback 策略。
- iOS 13+、Android 10+、Chrome 76+ 才稳定支持
window.matchMedia的change事件 - 微信内置浏览器、部分安卓厂商 WebView(如华为、小米旧版壳)即使系统是深色,
matches也恒为false - 旧 Safari(12.0 及以下)不支持嵌套媒体查询,也不触发
change,但至少matches能读一次
fallback 必须用 localStorage 或 URL 参数,不能只靠时间逻辑
用户主动选择的偏好优先级永远高于系统自动检测。首次加载时,应按顺序检查:
- 读
localStorage.getItem('themeMode'),存在则直接应用(比如'dark'或'light') - 若为空,再看 URL 是否含
theme=dark这类参数 - 两者都无,才考虑时间逻辑兜底(如
hour >= 19 || hour < 6),但别默认亮色——护眼需求下用户可能正需要暗色
注意:不要把时间逻辑当主路径,它只是保底;用户手动切换后必须存回 localStorage,否则刷新即失效。
立即学习“前端免费学习笔记(深入)”;
JS 初始化必须提前,且同步设置 data-theme 属性
闪屏的根本原因是 CSS 渲染早于 JS 执行。解决方案不是等 DOM 加载完再操作,而是:
- 把初始化脚本放在
<head>里,或用type="module"提前执行 - 立即读取存储/URL/系统状态,然后同步设置:
document.documentElement.setAttribute('data-theme', 'dark') - CSS 中对应写
[data-theme="dark"] { ... },和@media (prefers-color-scheme: dark)并行使用,互不干扰 - 避免只用 class 切换(如
document.body.classList.add('dark')),因为 body 可能还没解析,而document.documentElement总是可用
变量声明顺序和 fallback 缺一不可
只在 @media 块里定义 --color-bg,浅色系统下该变量就是未声明状态,var(--color-bg) 会 fallback 到 unset,导致背景透明或继承黑色 —— 尤其在 Safari 12.1+ 上极易白屏/黑屏。
-
:root必须先声明所有变量的浅色默认值:--bg: #fff; --text: #1a1a1a; - 再在
@media (prefers-color-scheme: dark)里重定义::root { --bg: #121212; --text: #e6e6e6; } - 所有使用处加 fallback:
background: var(--bg, #ffffff); color: var(--text, #1a1a1a); - 不要依赖 PostCSS 插件提前展开变量,它会让整个媒体查询块失效
真正容易被忽略的是:第三方组件(如 Ant Design)的深色类名往往和你的 data-theme 冲突,必须用更高优先级选择器覆盖,比如 html[data-theme="dark"] .ant-btn,而不是简单写 .ant-btn。


















