最轻量可靠方式是:root + @media (prefers-color-scheme: dark),但手动切换需配合data-theme属性与对应CSS选择器;自定义属性不响应HTML属性变化,必须用[data-theme="dark"] :root重声明全部变量;媒体查询须后置、过渡须作用于原生属性、系统变更需matchMedia监听。

直接用 :root + @media (prefers-color-scheme: dark) 是最轻量、最可靠的方式,无需 JS 就能自动响应系统设置;但若需用户手动切换,必须补上 document.documentElement.setAttribute('data-theme', 'dark') 和对应 CSS 选择器,否则点击按钮毫无反应。
为什么只改 data-theme 属性页面没变化
CSS 自定义属性不会监听 HTML 属性变化——它只在样式表解析时求值。执行 document.documentElement.setAttribute('data-theme', 'dark') 后,如果 CSS 里没写 [data-theme="dark"] :root 这类选择器并重新声明变量,var(--bg-color) 依然取默认值。
- 检查 DevTools → Elements →
:root节点,确认切换后变量值是否真被覆盖 - 正确选择器必须是
[data-theme="dark"] :root或html[data-theme="dark"] :root,body[data-theme="dark"]无效 - 变量重声明必须完整:漏掉
--border-color,而组件用了border: 1px solid var(--border-color),深色下就会显示浅灰边框
@media (prefers-color-scheme: dark) 必须写在 :root 默认声明之后
媒体查询不提升优先级,只靠声明顺序覆盖。如果深色规则写在浅色规则前,或嵌套在其他媒体查询里(比如 @media (max-width: 768px) { @media (prefers-color-scheme: dark) { ... } }),旧版 Safari 会直接忽略。
- 必须先写
:root { --bg-color: #fff; --text-color: #333; } - 再写
@media (prefers-color-scheme: dark) { :root { --bg-color: #121212; --text-color: #e0e0e0; } } - 所有变量都得有默认值,否则系统设为
no-preference或不支持时,var(--xxx)会回退到浏览器默认(通常是白底黑字)
过渡动画必须作用于原生属性,而非自定义属性
transition: --bg-color 0.3s 完全无效,CSS 规范不支持对自定义属性做动画。真正能过渡的是 background-color、color 这些最终渲染属性。
-
transition必须写在默认状态规则里,比如body { background-color: #fff; color: #333; transition: background-color 0.35s, color 0.35s; } -
.dark或[data-theme="dark"]里只提供目标值,不重复写transition - 避免用
transparent或inherit作为其中一端值,插值会中断或跳变 - 预设
或data-theme="dark"在 HTML 源码中,可防 JS 加载前的闪动
最容易被忽略的是:系统偏好变更后,@media (prefers-color-scheme: dark) 不会重触发,必须用 matchMedia 监听并手动更新 data-theme;而手动切换又不能直接改 style.setProperty,否则会屏蔽系统自动响应能力。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











