主题切换应将CSS变量统一定义在:root下,用@media(prefers-color-scheme:dark)同级覆盖,并支持data-theme手动切换与localStorage持久化,过渡需作用于具体属性而非变量名。

用媒体查询配合 CSS 变量实现主题切换,核心是让浏览器根据系统偏好自动计算变量值,不需要 JS 就能生效。关键不在“写多少代码”,而在于变量定义位置、覆盖逻辑和 fallback 是否可靠。
所有变量必须定义在 :root 下
CSS 变量靠继承传递,只有 :root(即 元素)能保证全页面任意节点——包括表单控件、伪元素、Shadow DOM 内部——都能读到。如果写成 .dark { --bg: #121212 },那这个变量只对 .dark 及其子元素有效,兄弟节点和 <input> 就拿不到。
正确做法是统一收口:
- 在
:root中定义默认(浅色)值 - 用
@media (prefers-color-scheme: dark)在同一级覆盖深色值 - 变量名保持语义化,比如
--text-color,不要叫--dark-text
媒体查询要写在 :root 外层,不能嵌套
@media 规则必须直接作用于 :root,不能把它包进某个选择器里。下面这样是错的:
立即学习“前端免费学习笔记(深入)”;
❌ 错误写法.theme-dark :root { --bg-color: #121212; }
正确写法是:
✅ 正确写法@media (prefers-color-scheme: dark) { :root { --bg-color: #121212; } }
注意:媒体查询必须写在 :root 声明之后,否则未匹配时变量会是 unset,导致样式失效。
手动切换时需用 class 覆盖媒体查询
用户点了“切深色”按钮,但系统设的是浅色——这时候仅靠 prefers-color-scheme 不够,得用 class 强制覆盖。推荐方案:
- 给
加data-theme="dark"或class="theme-dark" - 写高优先级规则,比如
html[data-theme="dark"] { --bg-color: #121212; } - 确保这类规则放在媒体查询之后,让它能压过系统偏好
- 用
localStorage记住用户选择,页面加载时优先读取它,再 fallback 到系统设置
过渡动画要作用在具体属性上,不是变量名
transition: --bg-color 0.3s 是无效的。CSS 的 transition 不支持变量名本身,只能作用于实际渲染属性,比如:
background-color: var(--bg-color); transition: background-color 0.3s;color: var(--text-color); transition: color 0.3s;- 多个属性可一起写:
transition: background-color 0.3s, color 0.3s, border-color 0.3s;
这样变量值一变,对应属性就会平滑过渡。



















