暗黑模式切换应通过 CSS 自定义属性与 JavaScript 控制 body class 实现,预先在 :root 和 body.dark 中定义两套变量,首次加载时读取 localStorage 或系统偏好并立即应用 class,按钮使用 button + ARIA 确保可访问性,避免内联样式和 !important 冲突。

暗黑模式切换的核心是 CSS 自定义属性 + JavaScript 控制 class
直接改 document.body 的 class 是最轻量、兼容性最好、也最容易调试的方式。浏览器会自动响应 class 变化,触发 CSS 中对应的 :root 或 body.dark 规则重绘,不需要操作每个元素的样式。
关键不是“切换主题”,而是“让 CSS 知道当前该用哪套变量”。所以你要提前在 CSS 里定义两套颜色变量:一套给 body(默认浅色),一套给 body.dark(暗色)。
常见错误是试图用 JS 逐个设置 style.color 或写一堆内联样式——这不可维护,且无法利用 CSS 变量的级联优势。
- 在
:root中定义浅色变量(如--bg: #fff),再在body.dark下覆盖同名变量(如--bg: #121212) - 所有组件样式都用
var(--bg),而不是硬编码颜色值 - 切换时只执行
document.body.classList.toggle('dark'),别碰 localStorage 以外的存储逻辑
如何保存用户偏好并首次加载时生效
用户切一次就忘,体验极差。必须读取 localStorage,并在 DOMContentLoaded 阶段就应用 class,避免页面闪白/闪黑。
注意:不能等 window.onload,那太晚;也不能在 里直接写 JS(可能 DOM 还没 ready)。最佳时机是脚本放在 底部,或用 document.addEventListener('DOMContentLoaded', ...)。
- 读取
localStorage.getItem('theme'),值为'dark'或'light',默认 fallback 到系统偏好(window.matchMedia('(prefers-color-scheme: dark)').matches) - 立即设置
document.body.className,比如document.body.className = theme === 'dark' ? 'dark' : '' - 之后再绑定按钮点击事件,避免重复设置
- 别用
document.body.setAttribute('data-theme', ...)—— class 更语义、更易 CSS 选择器匹配
切换按钮的 HTML 和 JS 实现要点
按钮本身不重要,关键是它触发的行为要干净、可逆、可测试。不要用 硬套样式,容易丢失可访问性;推荐用 + ARIA 属性。
示例结构:
<button id="theme-toggle" aria-label="切换深色模式">?</button>
- 监听
click,调用document.body.classList.toggle('dark') - 同步更新
localStorage:先读当前状态(document.body.classList.contains('dark')),再存 - 更新按钮 ARIA 状态:
button.setAttribute('aria-pressed', isDark),方便屏幕阅读器感知 - 别用
innerHTML改图标文字——用 CSS 的::before或 SVG 切换更稳定
CSS 变量覆盖和边界情况处理
暗黑模式不是“把所有背景变黑、文字变白”这么简单。有些颜色需要微调(比如链接悬停色、表单聚焦框、阴影透明度),否则在暗背景下反而刺眼或看不清。
常见坑:图片、iframe、第三方组件(如地图、图表库)不会自动适配 CSS 变量,它们的颜色由自身控制。你只能通过 class 做 wrapper 层级覆盖,或加 filter: invert(1) hue-rotate(180deg)(慎用,会翻转所有颜色,破坏图片语义)。
- 对
img、svg、iframe单独写body.dark img { filter: brightness(0.8); }类似规则 - 表单控件(
input、select)在 Safari 和 Firefox 中部分样式无法被 CSS 变量接管,需用appearance: none+ 手动重绘 - 确保
color-scheme: light dark写在:root或body上,让浏览器知道你支持暗色,从而正确渲染原生控件(如滚动条、复选框)
CSS 变量的继承链和 !important 冲突最容易被忽略——一旦某个组件内部用了 !important 覆盖颜色,外部变量就失效了。检查时优先看 computed styles,而不是 styles 面板里的声明顺序。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











