必须用window.matchmedia('(prefers-color-scheme: dark)').addeventlistener('change', handler)监听系统主题变化,初始化时主动执行handler,并在handler中重新读取.matches值,不可缓存旧结果,否则无法响应运行时切换。

怎么用 window.matchMedia 检测系统暗黑偏好是否变化
不能只靠 CSS 媒体查询“猜”用户有没有切系统主题——prefers-color-scheme 本身不触发重绘监听,必须显式调用 matchMedia 并绑定 change 事件才能响应运行时切换。
常见错误是写了 window.matchMedia('(prefers-color-scheme: dark)').matches 就以为万事大吉,结果 macOS 切了深色模式,页面纹丝不动。
- 必须用
addEventListener('change', handler),且 handler 要重新读取.matches值,不能缓存旧结果 - Safari 14+、Chrome 87+、Firefox 96+ 才稳定支持该事件;旧版需降级为轮询或 reload(不推荐)
- 监听对象要复用,不要每次新建
matchMedia实例,否则无法正确移除监听 - 初始化和监听应放在
DOMContentLoaded后,避免 DOM 未就绪导致html元素操作失败
@media (prefers-color-scheme: dark) 为什么有时不生效
看似一行 CSS 就能搞定,但线上最常卡在这几个点:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 写成了
@media (prefers-color-scheme = dark)或@media prefers-dark-mode—— 等号、拼写、括号缺一不可 - CSS 被包裹在
@layer或预处理器嵌套中,Safari 13.1–14.0 会直接忽略,建议单独提一层写 - 样式表加载顺序错乱:暗色规则写在亮色规则前面,又没加
!important,导致被覆盖(别加!important,改顺序) - 安卓 WebView(尤其微信/QQ 内置浏览器)完全不支持该查询,得 fallback 到
localStorage.getItem('theme')
如何让 JS 切换和系统监听不互相打架
用户点了「切换按钮」,系统又同步切了主题,两边状态不同步就会闪屏或错乱。关键不是谁优先,而是统一决策入口:
- 所有主题来源(localStorage、系统 matchMedia、按钮点击)都汇总到一个函数,比如
setTheme(theme) -
setTheme必须同时更新:document.documentElement.setAttribute('data-theme', theme)+localStorage.setItem('theme', theme) - 系统监听的
change回调里,只调setTheme,不直接操作 DOM 或 storage - 初始化时按优先级执行:
localStorage>matchMedia.matches> 默认亮色,确保首次渲染即确定
哪些元素容易被 prefers-color-scheme 漏掉
它只影响你写的 CSS 规则,对原生控件、伪元素、内联样式、图片、SVG 都无感:
-
<input>、<select></select>、<textarea></textarea>的背景/边框/文字色不会自动变,得手动用[data-theme="dark"] input覆盖 -
::-webkit-scrollbar、::placeholder、::selection这些伪类必须单独写暗色版本 -
<img>不会反色,深色下图标发灰?得准备两套资源或用filter: brightness(0.8)微调 -
<svg></svg>中的fill、stroke默认继承 text color,但若写死#000就永远黑,得改成currentColor或变量
纯黑 #000000 在 OLED 屏上烧屏风险高,深色模式下背景用 #121212、文字用 #e0e0e0 更稳妥——这点连很多设计稿都忽略。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










