prefers-color-scheme 媒体查询用于检测系统主题,需配合 window.matchmedia 监听 change 事件实现响应式切换,css 变量应按 :root 默认→媒体查询覆盖顺序定义,并结合 localstorage 优先级管理用户手动选择与系统偏好冲突。

如何用 prefers-color-scheme 检测系统主题
浏览器原生支持系统级暗黑/亮色偏好,关键就靠 prefers-color-scheme 媒体查询。它不是 JavaScript API,不能“监听切换”,只在页面加载或媒体查询重计算时生效(比如窗口 resize、系统主题变更后刷新页面)。
常见错误是以为加个 window.matchMedia 监听就能实时响应——其实可以,但必须手动绑定 addEventListener('change'),否则主题切了页面没反应。
- 基础写法:
@media (prefers-color-scheme: dark) { ... }用于 CSS - JS 检测当前值:
window.matchMedia('(prefers-color-scheme: dark)').matches - 监听变化(必须写):
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', handler) - 注意:Safari 14+、Chrome 87+、Firefox 96+ 才稳定支持 change 事件;旧版只能 reload 或轮询
为什么直接写 color-scheme: dark 不够用
color-scheme CSS 属性只是告诉浏览器:“我支持暗黑模式,请按需调整原生控件(如 <input type="range">、<select></select>)”。它不改变你写的任何样式,也不影响自定义组件。
典型误用场景:只加了 color-scheme: dark 就以为页面变暗了——结果文字还是黑的、背景还是白的,表单控件倒是变灰了,整体反而更难读。
- 必须配合
@media (prefers-color-scheme: dark)重写所有颜色变量和背景 -
color-scheme推荐写在:root或html上,且值应为light dark(双值),否则部分浏览器会忽略暗色适配 - 它对
<textarea></textarea>的滚动条、焦点 outline 等有隐式影响,但不可控;仍需用::-webkit-scrollbar等单独处理
CSS 变量 + 媒体查询实现主题切换的实操要点
用 CSS 自定义属性(--bg、--text)配合媒体查询是最轻量、零 JS 的方案,但容易踩变量作用域和覆盖顺序的坑。
核心原则:变量定义位置决定优先级。把默认(亮色)变量写在 :root 顶层,暗色变量用媒体查询覆盖——反过来写会导致暗色模式下变量未定义。
- 正确顺序:
:root { --bg: #fff; --text: #000; } @media (prefers-color-scheme: dark) { :root { --bg: #1a1a1a; --text: #e6e6e6; } } - 避免在组件内部重复定义同一变量,否则媒体查询无法穿透覆盖
- 图片、SVG 图标等非 CSS 元素不会自动反转,需额外用
picture+media或 JS 切换src - 性能提示:变量本身无开销,但大量嵌套
var(--x, var(--y))在低端设备上可能触发重排
用户手动切换时怎么和系统主题共存
纯依赖 prefers-color-scheme 无法满足“用户点了暗黑按钮,就强制暗黑,不管系统设啥”的需求。必须引入状态管理,且要解决“系统改了,页面不该回退”这个冲突点。
关键逻辑是:用户选择 > 系统偏好。存储用户选择(localStorage),每次加载先读取,再 fallback 到媒体查询。
- JS 初始化时:
const saved = localStorage.getItem('theme'); const isDark = saved ? saved === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches; - 切换时同时写入
localStorage和document.documentElement.dataset.theme,再用 CSS 类(如.theme-dark)控制样式 - 监听系统变化时,仅当用户从未手动设置过主题,才更新页面——用
localStorage.getItem('theme') === null判断 - 别忘了给
加class或data-theme,否则 CSS 无法精准作用到全局
最常被忽略的是:系统主题变更后,如果用户之前手动选过主题,页面必须保持不动;而很多实现一监听到 change 就无条件同步,导致用户偏好被覆盖。这个判断逻辑不能省。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











