transition 对 CSS 变量本身无效,必须作用于 background-color 等原生可动画属性;需在使用变量的元素(如 body)上声明,且两端值明确可插值,class 必须同步挂载以避免闪屏。

transition 作用于 CSS 变量本身完全无效,必须落到 background-color、color 等原生可动画属性上,且声明位置和值的确定性比写对变量名更重要。
为什么 transition: --bg-color 0.3s 一点反应都没有
CSS 规范明确不支持对自定义属性名做插值动画。浏览器解析到这行就直接跳过,既不报错也不警告。变量 --bg-color 只是占位符,真正参与过渡的是它被 var(--bg-color) 替换后的具体颜色值(比如 #ffffff → #121212)。所以过渡目标不是“变量变了”,而是“某个元素的 background-color 值变了”。
- 过渡必须写在使用该变量的元素上,比如
body或.card,不能只写在:root - 两端颜色值都得是明确的、可插值的(避免用
transparent、inherit或未定义的 fallback) -
transition必须在默认状态(非.dark类)中声明,否则首次切换仍会闪一下
body 上必须显式写 transition,而不是 html 或 :root
html 和 :root 不是渲染盒,不触发视觉重绘。给它们加 transition 看似合理,实则毫无效果。
- 正确写法:
body { background-color: #ffffff; color: #333; transition: background-color 0.35s ease-in-out, color 0.35s ease-in-out; } -
.dark类里只负责提供目标值:body.dark { background-color: #121212; color: #e0e0e0; } - 如果用了
data-theme,也要确保transition写在body上,而非仅靠html[data-theme="dark"]内部声明
首屏不闪的关键:class 必须在 DOM 解析前同步挂载
哪怕 CSS 写得再准,JS 切换逻辑再严谨,只要 document.documentElement.classList.add('dark') 是等 DOMContentLoaded 后才执行,iOS Safari 和 Android WebView 就大概率闪白屏。
- 必须在
里内联轻量 JS,用matchMedia('(prefers-color-scheme: dark)')同步检测 - 立即读取
localStorage.getItem('theme'),并同步设置document.documentElement.classList - 不要等
window.onload,它太晚;DOMContentLoaded是底线,但最好更早
用 :root + .dark 类联动时,变量覆盖要严格一致
很多人在 :root 定义 --bg-color,却在 .dark 里改成了 --bg-dark,结果 var(--bg-color) fallback 到初始值甚至透明,造成不可预期的空白或跳变。
- 统一在
:root声明所有变量:--bg-color: #ffffff; -
:root.dark { --bg-color: #121212; }—— 变量名必须完全相同 - 记得加
html { color-scheme: light dark; },否则原生控件(如、滚动条)不会自动适配 - 别依赖
color-scheme实现主题逻辑,它只是声明,不是执行器
最常被忽略的不是“怎么写 transition”,而是“谁来触发重绘”——document.documentElement.classList.toggle('dark') 这一行之后,浏览器重算样式,前提是变量已定义、属性已声明、过渡已在基础规则中启用。漏掉任意一环,平滑就只剩幻觉。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











