直接读取window.matchmedia('(prefers-color-scheme: dark)').matches是唯一可靠方式,需监听change事件实时响应系统切换,并优先读取localstorage确保用户选择不被覆盖,同时挂载dark类到根节点、声明以支持原生控件和seo。

如何检测系统是否启用暗黑模式
直接读取 window.matchMedia 是唯一可靠方式,prefers-color-scheme 媒体查询不能靠 CSS 类名或 localStorage 伪造判断。
常见错误是只在页面加载时检查一次,导致系统主题切换后 UI 不同步。必须监听变化:
-
window.matchMedia('(prefers-color-scheme: dark)').matches返回布尔值,仅反映当前状态 - 用
mediaQuery.addListener(handler)或现代写法mediaQuery.addEventListener('change', handler)持续响应 - 注意 Safari 14+ 才支持
addEventListener,旧版需回退到addListener
DOM结构调整该在哪一层做
别把 dark class 加在 上就完事——这容易和第三方组件的样式冲突,也难以控制伪元素、SVG 图标、<input type="color"> 等原生控件的暗色行为。
推荐挂载在 根节点,并配合 color-scheme: dark CSS 声明:
-
document.documentElement.classList.toggle('dark', isDark)是最轻量的 DOM 更新方式 - 同时在
中插入<meta name="color-scheme" content="light dark">,让浏览器知道你支持双模式 - 某些图标字体(如 Font Awesome)依赖
:root变量,所以:root.dark的 CSS 变量重定义必须存在,不能只靠 class 覆盖
为什么不能只靠 CSS 变量切换颜色
CSS 自定义属性确实能快速换色,但对图片、SVG 内联 fill/stroke、<canvas></canvas> 绘图、甚至 background-image: url(...) 完全无效——这些资源不会随变量自动替换。
真正需要 DOM 结构调整的场景:
- SVG 使用
<use href="#icon-sun"></use>时,得根据模式切换href指向#icon-moon或反之 -
<img src="logo-light.png">必须换成<picture></picture>+<source media="(prefers-color-scheme: dark)" srcset="logo-dark.png"></source> - 某些第三方图表库(如 Chart.js)渲染后无法重绘主题,只能销毁再 init,这就要求外层容器有明确的
data-theme属性供 JS 识别
localStorage 和系统模式不一致怎么办
用户手动切换主题时,localStorage.setItem('theme', 'dark') 会覆盖系统偏好。但下次系统变回浅色,页面仍保持暗色——这不是 bug,是预期行为,但必须显式处理同步逻辑。
关键点:
- 初始化时优先读
localStorage.theme,若不存在才 fallback 到matchMedia - 监听系统变化时,**仅当 localStorage 未设值**才自动更新页面主题;否则保持用户选择
- 提供「跟随系统」开关,点击后清空
localStorage.theme,并触发一次matchMedia同步 - 别用
localStorage存布尔值,存字符串'light'/'dark'/'auto'更易扩展
最常被忽略的是:服务端渲染(SSR)页面首次加载时,JS 还没执行,matchMedia 不可用,必须靠 配合服务端 UA + IP 地理位置粗略判断,否则首屏闪白/闪黑。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











