最轻量且无需JS的深色模式方案是优先使用prefers-color-scheme媒体查询,再用localStorage+dark类实现用户手动覆盖;避免color-scheme meta标签误用及内联样式硬改,图标和图片需双资源适配。

用 prefers-color-scheme 检测系统偏好,最轻量且无需 JS
现代浏览器(Chrome 76+、Firefox 67+、Safari 12.1+)原生支持 prefers-color-scheme 媒体查询,能直接读取用户操作系统级的深色/浅色设置。这是加夜间模式的第一道防线,也是最可靠的基础。
- 它不依赖 JS,页面加载即生效,无闪烁、无延迟
- 用户在系统设置里切换主题,网页会自动响应(需刷新或监听
change事件才实时更新) - 优先级高于手动切换逻辑,适合作为默认 fallback
@media (prefers-color-scheme: dark) {
:root {
--bg: #1e1e1e;
--text: #e0e0e0;
}
}
@media (prefers-color-scheme: light) {
:root {
--bg: #ffffff;
--text: #333333;
}
}
用 localStorage + class 切换,支持用户手动覆盖系统设置
用户可能想“反系统”:比如系统设为浅色,但希望网页用深色。这时必须提供开关按钮,并把选择持久化到 localStorage,否则刷新就丢。
- 不要只靠
style标签或内联样式切换——难以维护、无法继承、CSS 优先级混乱 - 推荐给
或添加darkclass,再用 CSS 层叠控制样式 - 切换时要同步更新
localStorage和 class,还要处理prefers-color-scheme与手动设置的优先级关系
function setDarkMode(isDark) {
if (isDark) {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
}
}
// 初始化:先看 localStorage,再 fallback 到 prefers-color-scheme
const saved = localStorage.getItem('theme');
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (saved === 'dark' || (!saved && systemPrefersDark)) {
setDarkMode(true);
} else {
setDarkMode(false);
}
避免常见错误:color-scheme meta 标签和强制重绘陷阱
只影响浏览器 UI(如地址栏、滚动条),**不会改变你的页面样式**。别指望它替代 CSS。
- 用
document.body.style.backgroundColor这类内联样式硬改?会导致 CSS 变量失效、媒体查询被绕过、动画卡顿 - 频繁调用
getComputedStyle或强制重排(如读写 offsetHeight)可能引发布局抖动,尤其在 scroll 或 toggle 事件中 - 深色模式下慎用
box-shadow和半透明背景(rgba(0,0,0,0.1)在黑底上几乎不可见)
图片和 SVG 的夜间适配不能只靠 CSS filter
filter: brightness(0.8) contrast(1.2) 看似简单,但对图标、图表、照片效果极不稳定:可能让灰度图变糊、让红色失真、让 SVG 渐变断裂。
- 对关键图标(如 logo、按钮 icon),准备两套资源,用
picture+media切换 - SVG 内联时,可直接用
.dark svg path { fill: var(--icon-dark); }控制颜色 - 背景图若用 CSS 设置,建议用
background-image: image-set(...)(支持有限)或 JS 动态换src
真正麻烦的是第三方内容(如 embed 的视频、iframe 广告、富文本里的 )——它们不受你 CSS 控制,得靠 invert() + hue-rotate() 补救,但代价是色彩失真。这种边界情况,往往比开关本身更耗时间。










