直接用prefers-color-scheme检测系统偏好并配合localstorage存储用户选择,可实现可靠暗黑模式:初始化优先读localstorage,无值时fallback至matchmedia;用data-theme属性而非class控制样式,css中通过html[data-theme="dark"]定义变量,按钮需为并支持aria-pressed与键盘操作。

直接用 prefers-color-scheme 检测系统偏好,再配合 localStorage 记住用户手动选择,就能做出可靠、无障碍友好的暗黑模式切换按钮——不需要 JS 框架,也不用监听 class 切换。
怎么用 prefers-color-scheme 做默认适配
CSS 媒体查询是暗黑模式的基石。浏览器会自动读取系统设置,@media (prefers-color-scheme: dark) 会生效,但注意它只反映系统级偏好,不响应用户点击切换。
- 必须写在
<style></style>或外部 CSS 中,不能用 JS 动态插入 media 查询 - 如果用户没开系统暗黑模式,又没手动点过按钮,页面就该按亮色渲染,
prefers-color-scheme不会“强制”变暗 - 推荐把基础样式(如
body { background: white; color: black; })写在普通规则里,暗黑覆盖写在 media 内,避免重复声明
localStorage 存什么、什么时候存
用户点一次按钮,就要立刻存状态,且优先级高于系统偏好。存的值建议用字符串 "light" 或 "dark",别用布尔值或数字,避免类型混淆。
- 初始化时先读
localStorage.getItem('theme'),有值就用它;没值才 fallback 到window.matchMedia('(prefers-color-scheme: dark)').matches - 切换按钮的
onclick里,除了切 class,必须同步调用localStorage.setItem('theme', 'dark')(或'light') - 不要在页面加载后延迟读取
localStorage再改 class——会造成闪屏。应在标签渲染前就决定 class,例如用内联 script 放在顶部
给 加 class 还是用 data-theme
用 data-theme="dark" 更稳妥。class 容易和业务 class 冲突(比如 dark 被其他组件复用),而 data- 属性语义清晰、CSS 选择器也明确:html[data-theme="dark"] { ... }。
- JS 设置:用
document.documentElement.setAttribute('data-theme', 'dark'),别用className - CSS 中避免写
.dark .btn这种依赖 class 层级的规则,改用html[data-theme="dark"] .btn,防止漏样式 - 如果用了 CSS 变量(如
--bg-color),记得在 :root 和html[data-theme="dark"]里分别定义,变量本身不会自动继承 theme 切换
按钮怎么写才不踩坑
按钮本身要支持键盘操作和屏幕阅读器。别用 <div onclick="...">,必须是 <code><button type="button"></button>,并加上 aria-pressed 实时反映状态。
- 初始状态要和当前 theme 一致:
aria-pressed="true"表示当前是暗黑模式 - 每次点击都要 toggle
aria-pressed值,并同步更新data-theme和localStorage - 图标建议用 SVG 内联,别用 background-image —— 否则暗黑模式下图标颜色可能不变,需额外控制 fill/stroke
- 别在按钮里写“切换到白天”,而用“切换主题”或带图标 + 文字组合,避免语义绑定具体模式
最麻烦的不是切换逻辑,而是 CSS 变量和第三方组件样式的一致性处理——比如一个用了 background: #fff 的轮播图组件,在暗黑模式下得单独重写,而且得确保它不被 prefers-color-scheme 自动覆盖掉。











