直接用@media (prefers-color-scheme: dark)可捕获系统暗色偏好,但必须写在CSS文件或<style>块中,不能JS动态插入;需同时声明light/dark媒体查询,配合CSS变量和window.matchMedia监听切换。

prefers-color-scheme 媒体查询怎么写才生效
直接用 @media (prefers-color-scheme: dark) 就能捕获系统级暗色偏好,但必须确保它写在 CSS 文件里(或 <style> 块中),不能只靠 JS 动态插入样式表——浏览器只在初始渲染时解析该媒体查询,后续系统切换不会触发重载。
常见错误是把它塞进 JS 的 insertRule 或 innerHTML 里,结果首次加载后就失效。正确做法是提前写好两套规则:
@media (prefers-color-scheme: dark) {
:root {
--bg: #121212;
--text: #e0e0e0;
}
}
@media (prefers-color-scheme: light) {
:root {
--bg: #ffffff;
--text: #333333;
}
}
JS 怎么监听系统主题变化
CSS 媒体查询是静态响应,但有时你需要在 JS 中做逻辑判断或主动更新状态(比如记录用户偏好、触发动画、上报埋点)。这时候得用 window.matchMedia:
- 调用
window.matchMedia('(prefers-color-scheme: dark)')返回一个MediaQueryList对象 - 它的
.matches属性实时反映当前匹配状态(true表示系统处于暗色模式) - 用
.addEventListener('change', handler)监听切换事件——注意不是onchange,必须用addEventListener
示例:
立即学习“前端免费学习笔记(深入)”;
const darkModeMedia = window.matchMedia('(prefers-color-scheme: dark)');
function handleColorSchemeChange(e) {
document.documentElement.dataset.theme = e.matches ? 'dark' : 'light';
}
handleColorSchemeChange(darkModeMedia); // 初始化
darkModeMedia.addEventListener('change', handleColorSchemeChange);
为什么页面刚加载时 theme 切换有延迟或闪烁
这是最常被忽略的性能陷阱:CSS 变量依赖 JS 注入、或主题 class 晚于 HTML 渲染添加,导致首屏闪白(light)或闪黑(dark)。
关键解决点:
- 把初始主题判断逻辑放到
<head>的内联<script>中,早于任何样式表和 DOM 渲染 - 避免依赖
DOMContentLoaded—— 此时 HTML 已解析完毕,但样式可能已按默认主题渲染过一帧 - 更稳妥的做法:服务端根据
Sec-CH-Prefers-Color-Scheme请求头(如果支持 Client Hints)输出初始 class;不支持时再 fallback 到客户端 JS
最小化客户端方案:
<html data-theme="light">
<script>
document.documentElement.dataset.theme =
window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
</script>
prefers-color-scheme 在哪些环境不工作
它不是万能开关,几个典型失效场景要心里有数:
- iOS Safari 13.4 之前版本完全不支持
prefers-color-scheme - 部分安卓 WebView(尤其旧版 Chrome Custom Tabs)未透传系统设置
- Electron 应用默认不继承系统偏好,需手动读取
nativeTheme.shouldUseDarkColors并同步到 CSS - Windows 上某些远程桌面或高对比度模式下,系统可能报告
no-preference,此时.matches为false,但不代表是亮色
应对策略:永远把 prefers-color-scheme 当作「建议」而非「事实」,允许用户手动覆盖,并把用户选择持久化到 localStorage,优先级高于系统检测。



















