@starting-style仅在元素首次裸渲染时生效,需满足零样式起点、动画显式覆盖、声明位置正确三条件;Chrome/Edge 119+支持,Firefox/Safari不支持,须配transition fallback。

@starting-style 只在元素首次渲染且完全“裸”(无任何样式)时生效,配合 animation 才能实现真正从设定值开始的淡入;直接写、加 class、服务端渲染后 hydrate 都会跳过它。
为什么写了 @starting-style 却看不到淡入效果?
最常见原因是:元素根本没进入 @starting-style 触发条件。浏览器只对「刚插入 DOM 且尚未计算任何样式」的节点应用它——一旦有 class、内联 style、JS 设置过 opacity,就直接跳过。
- React/Vue 组件挂载时通常已带 class,
@starting-style被静默忽略 - 服务端渲染(SSR)后的 HTML 已含样式,hydration 后 DOM 不再“首次”
- 写了
@starting-style { opacity: 0; },但动画 keyframes 里没定义opacity变化,该声明被丢弃 - 把
@starting-style放在@keyframes之后,CSS 解析顺序导致规则无效
怎么写出一个真正触发的淡入?
核心是让元素先“空着身子”进 DOM,再由 CSS 动画接管。必须满足三个硬性条件:零样式起点、动画显式覆盖、声明位置正确。
-
@starting-style必须写在@keyframes和动画规则之前(CSS 自上而下解析) - 动画必须包含
opacity的起止帧,例如@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } - 目标元素不能有任何 class 或 style 属性,连空格都不能有:
<div></div>可以,<div class=""></div>不行 - 必须设置
animation-fill-mode: both,否则动画一结束就回退到初始状态,掩盖了 starting 效果
React 中如何绕过 hydration 破坏?
服务端渲染或 SSR 框架下,DOM 初始就带 class,@starting-style 失效是常态。唯一可靠办法是延迟注入动画 class,让元素先“裸”渲染一次。
立即学习“前端免费学习笔记(深入)”;
- 用
useState+useEffect控制 class 注入时机:const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); - HTML 中写
<div classname="{mounted" :>,确保首次 render 时无 class <li>对应 CSS 中,<code>.fade-in必须包含animation和animation-fill-mode: both - 注意:若组件被
React.memo或shouldComponentUpdate拦截,可能跳过首次裸渲染,需检查更新逻辑 - 所有“入场”都得基于已参与样式计算的元素,所以只能用
opacity、transform、visibility这类属性 - 如果需要隐藏空间占用,用
height: 0+overflow: hidden或max-height替代display - 目前仅 Chromium 119+(Chrome/Edge)支持,Firefox 和 Safari 完全不识别,必须提供
transitionfallback
@starting-style 不支持 display,别试了
想用 @starting-style { display: none; opacity: 0; } 实现“从无到有”?浏览器会直接忽略 display 声明——它不是可动画属性,且 display: none 让元素退出渲染树,@starting-style 根本不触发。
真正难的不是写对语法,而是控制 DOM 插入那一刻的“纯净度”——框架、构建工具、甚至 HTML 模板里的空格,都可能悄悄破坏这个前提。


















