prefers-color-scheme 是 css 媒体查询关键字,用于原生响应系统深色/浅色偏好,需同时定义 dark 和 light 规则以避免样式失效,不支持 ie,配合 localstorage 与 class 切换可实现手动覆盖。

怎么用 prefers-color-scheme 检测系统偏好
浏览器原生支持深色模式检测,不用 JS 就能响应系统设置。prefers-color-scheme 是 CSS 媒体查询关键字,不是 JS API,别在 window.matchMedia 里拼错成 prefers-dark-mode —— 这个写法根本无效。
常见错误:只写 @media (prefers-color-scheme: dark) 却没配 light 回退,导致深色系统下浅色组件文字看不见(比如白字配白底)。
- 必须同时定义
light和dark两套规则,或至少确保默认样式在浅色下可用 - 它只响应系统级设置,不响应用户手动切换;想支持手动开关,得另加逻辑
- Safari 12.1+、Chrome 76+、Firefox 96+ 支持,IE 完全不支持 —— 别给 IE 写 fallback 媒体查询,没用
@media (prefers-color-scheme: dark) {
:root {
--bg: #121212;
--text: #e0e0e0;
}
}
@media (prefers-color-scheme: light) {
:root {
--bg: #ffffff;
--text: #333333;
}
}
怎么让手动开关真正覆盖系统偏好
用户点一下“切换深色”,就得立刻生效,且下次打开网页还记住这个选择 —— 这需要三件事协同:JS 修改 class、CSS 优先级设计、本地存储保存状态。
容易踩的坑:document.documentElement.classList.toggle('dark') 后没清掉旧的 light class,导致 class 冲突;或者把 dark class 加在 上,结果 CSS 里写的 html.dark 根本不匹配。
立即学习“前端免费学习笔记(深入)”;
- 统一加在
上,CSS 用html.dark { ... }覆盖系统媒体查询 - 切换前先读
localStorage.getItem('theme'),避免 JS 执行时闪一下默认主题 - 监听系统变化时(
window.matchMedia('(prefers-color-scheme: dark)').addEventListener),只在用户没手动设置过时才更新 UI
color-scheme meta 标签有什么用
这个 <meta name="color-scheme" content="light dark"> 不是控制页面主题的,它只告诉浏览器:“我支持深色,你可放心渲染表单控件、滚动条、内置选择器等原生元素”。不加它,某些安卓 WebView 或旧版 Safari 会强制把输入框背景变白,破坏深色体验。
注意:content 值只能是 light、dark 或空格分隔的组合,不能写 auto 或 system —— 这些值会被忽略。
- 必须放在
里,且在任何 CSS 加载前 - 它不影响自定义样式,只影响浏览器“画”的那部分 UI
- 不加不会报错,但 iOS 上日期选择器、复选框等可能突兀地亮着
为什么 localStorage 里存字符串而不是布尔值
因为 localStorage 只存字符串,localStorage.setItem('theme', true) 实际存的是 "true"。如果后续用 === 严格比较 localStorage.getItem('theme') === true,永远为 false。
更麻烦的是,用户首次访问时 getItem 返回 null,直接转布尔会变成 false,误判为“要浅色”——其实应按系统偏好来。
- 统一用
JSON.stringify()存,JSON.parse()读,或直接约定字符串值为"dark"/"light" - 判断逻辑写成
stored === 'dark',别依赖隐式类型转换 - 别在
DOMContentLoaded之后才读取,否则页面会先闪一次默认主题











