body上加transition才生效,因html/:root非渲染盒;@media(prefers-color-scheme)须顶层独立声明;localStorage+DOMContentLoaded初始化防FOUC;View Transitions仅为可选动画增强。

transition加在body上才真正生效
给:root或html加transition没用,因为它们不是渲染盒,不触发视觉重绘。必须作用在实际参与布局和绘制的元素上,body是最直接、最稳妥的选择。
常见错误是写成:
html { transition: background-color 0.3s; }
结果切换时毫无过渡感。正确做法是:
body { transition: background-color 0.3s, color 0.3s, border-color 0.3s; }
- 只对支持过渡的属性生效:如
background-color、color、border-color;filter、opacity需单独声明 - 避免无差别写
transition: all 0.3s——可能意外过渡font-size或transform,造成布局抖动 - 如果用了
data-theme控制根节点,记得把transition也加到body,而不是只依赖html[data-theme="dark"]选择器内部的声明
@media (prefers-color-scheme: dark)不能嵌套
媒体查询必须是顶层 CSS 规则,不能包在.dark类、body选择器甚至@layer里,否则浏览器会忽略它,导致暗色变量根本不会被应用。
立即学习“前端免费学习笔记(深入)”;
错误写法示例:
.theme-container { @media (prefers-color-scheme: dark) { :root { --bg-color: #121212; } } }
这样写,prefers-color-scheme完全失效。正确结构只能是:
:root { --bg-color: #fff; --text-color: #333; } @media (prefers-color-scheme: dark) { :root { --bg-color: #121212; --text-color: #e0e0e0; } }
-
@media必须独立、平级,且紧贴:root定义之后 - 变量名必须完全一致,比如
--bg-color不能在暗色块里写成--bg-dark,否则var(--bg-color)会 fallback 到初始值甚至透明 - 记得同步加
html { color-scheme: light dark; },否则滚动条、<input>等原生控件颜色不会变
手动切换时 localStorage + class 必须配合初始化
用户点一次“切暗色”,刷新页面又变回亮色,是因为@media (prefers-color-scheme: dark)只响应系统设置,不保存用户选择。必须靠 JS 读写localStorage,并在 DOM 加载完成第一时间应用。
关键动作顺序不能错:
- 脚本要在
DOMContentLoaded事件里执行,不能等window.onload(太晚,已闪屏) - 先读
localStorage.getItem('theme'),没值再 fallback 到window.matchMedia('(prefers-color-scheme: dark)').matches - 立即设
document.body.classList.toggle('dark', isDark),而不是等按钮点击才开始 - 按钮点击时只负责更新
localStorage和切换 class,不要重复初始化逻辑
漏掉初始化这步,FOUC(Flash of Unstyled Content)几乎必然发生——尤其在 SSR 或静态站点中。
View Transitions API 是锦上添花,不是必需品
document.startViewTransition()能实现跨元素裁剪动画,但目前仅 Chromium 111+ 和 Safari 17.4+ 支持,Firefox 尚未落地。它解决的是“页面级主题切换时的瞬时跳变”,不是基础功能。
使用前提是:
- 切换操作必须包裹在
startViewTransition回调内,例如:document.startViewTransition(() => document.body.classList.toggle('dark')) - 必须搭配
::view-transition-old(root)和::view-transition-new(root)伪元素做自定义动画 - 不支持时必须降级到普通
transition,不能让整个切换逻辑崩溃
真正容易被忽略的点是:View Transitions 对 CSS 变量变更本身无加速作用——它只是包装了一次重绘。如果你的变量没正确定义或 class 没正确挂载,加了 API 也白搭。


















