答案是FOUC(闪屏)问题,源于CSS先按默认主题渲染、JS后读取localStorage切换主题导致的100ms视觉闪烁;需在<head>最前用内联脚本预设data-theme,并将transition声明置于默认样式中作用于具体属性而非CSS变量。

页面刷新时闪一下浅色主题?这是FOUC,不是transition问题
这不是 transition 写错了,而是浏览器先用默认样式渲染、再等JS读取 localStorage 后才切主题——中间那100ms就是“闪”的来源。你给 body 加了 transition: background-color 0.3s,但动画根本没机会触发,因为颜色是突变的。
- 检查 HTML
<head>里有没有内联脚本提前设置data-theme:必须在任何 CSS 加载前执行 - 别把主题逻辑全扔进
window.addEventListener('DOMContentLoaded', ...)—— 它比 CSS 解析还晚 - 避免用
:root变量直接控制全局颜色,而是在body或html上用类名切换(如.theme-light/.theme-dark),并为这些类写明确定义的background-color和color
transition 写在 :root 上完全无效
:root { --bg: #fff; transition: --bg 0.3s; } 这种写法不会产生任何过渡效果。CSS变量本身不支持 transition,浏览器会静默忽略整条声明。你看到的“闪”,其实是变量值更新后,所有引用它的属性(比如 background-color: var(--bg))同步重绘,没有缓动过程。
- 真正要加
transition的是具体元素的具体属性,例如:body { background-color: var(--bg); transition: background-color 0.3s; } - 确保主题类(如
.theme-dark)只改body或html的 class,不要靠 JS 动态改style.setProperty—— 后者无法触发 transition - 如果必须用 JS 改变量,那就放弃 transition,改用 CSS 动画 +
@keyframes控制渐变过程(但更重,慎用)
Chrome DevTools里看不到transition生效?先看它是不是被吃了
打开 Elements 面板,选中 body 元素,在 Styles 侧边栏找 transition。如果显示为 transition: none 或干脆不出现,说明这条声明被浏览器丢弃了——常见原因不是语法错,而是它写在了错误的位置。
- 确认
transition声明写在初始状态(比如body默认样式),而不是只写在.theme-dark里 - 检查是否有更高优先级的规则覆盖了它,比如某个 reset.css 把
transition设成了initial - 注意媒体查询或嵌套作用域是否让该声明根本没生效(比如写在
@media (prefers-color-scheme: dark)里,但用户没开系统深色模式)
真机上还闪?Safari和低端安卓有特殊坑
桌面 Chrome 看着正常,一到 iOS Safari 或 Android 5.x 就闪得厉害,往往是因为硬件加速没真正启用,或者图层没升上去。这时候光靠 opacity 或 background-color 过渡是不够稳的。
立即学习“前端免费学习笔记(深入)”;
- 对
body或主题容器加transform: translateZ(0),强制创建合成层(比will-change更兼容) - 避免在主题切换时同时触发布局变化:比如改
font-size或padding,这会让transition被打断 - 如果用了 Swiper 或其它轮播组件,确认
translateZ(0)是加在每个.swiper-slide上,而不是父容器 —— 加错位置等于没加


















