用 grid-template-rows: 0fr → minmax(0, max-content) 可实现内容驱动的平滑高度动画,因 fr 单位可插值且浏览器能计算 max-content 实际高度,而 height: auto 不可插值;需满足父容器有确定高度上下文、显式声明 display: grid 和 grid-template-rows、内容区 overflow: hidden 三个前提。

直接用 height: auto 做过渡动画是不可能的——浏览器不支持从固定值到 auto 的插值,动画会直接跳变。但用 grid-template-rows: 0fr → 1fr 可以真正实现平滑、内容驱动的高度展开/收起。
为什么 0fr → 1fr 能过渡,而 height 不行
fr 是可插值的弹性单位,代表“剩余空间的份数”,不是像素值。0fr 表示该行轨道不参与分配(视觉高度为 0),1fr 表示它拿走所有可用空间。只要父容器有确定高度上下文,浏览器就能在 0fr 和 1fr 之间做线性插值,生成中间帧。
而 height: 0 → height: auto 中的 auto 是计算时才确定的值,无法被插值,CSS 动画引擎直接放弃过渡。
-
0fr≠height: 0,它是轨道尺寸为 0,需配合overflow: hidden才隐藏内容 - 必须显式声明
grid-template-rows,否则fr没有参照轨道 - 子元素不能设
height或min-height,否则会压制轨道收缩行为
必须满足的三个前提,缺一不可
动画不动、布局错乱、高度卡死,90% 都是因为漏了其中一条:
立即学习“前端免费学习笔记(深入)”;
- 父容器要有**确定的块级高度上下文**:比如设了
height/max-height,或处于flex: 1容器中,或由其他fr/px行固定总高 - 必须显式写
display: grid和grid-template-rows(哪怕初始是0fr) - 内容区必须加
overflow: hidden,否则即使轨道高度为 0,内容仍会溢出可见
怎么写才真正可靠:从 0fr 到 minmax(0, max-content)
纯 1fr 在内容极少时可能过度收缩(文字被截断),内容极多时又可能撑爆容器。更稳妥的写法是过渡到 minmax(0, max-content):
.accordion {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease;
overflow: hidden;
}
.accordion.active {
grid-template-rows: minmax(0, max-content);
}
-
0fr插值到minmax(0, max-content)仍可动画,因为浏览器将0fr视为 ≈ 0px,minmax()计算出的实际高度 ≈ 内容真实高度 - 如果内容含图片或异步加载,务必等
img.onload或requestAnimationFrame后再切换.active类,否则max-content计算不准 - 避免和
max-height混用——它会干扰fr的空间分配逻辑
Safari 兼容与性能提示
Chrome 66+、Firefox 63+ 支持稳定,但 Safari 14–15.6 对 0fr → 1fr 插值偶有卡顿或跳变。加一行 will-change: grid-template-rows 能显著改善:
.accordion {
will-change: grid-template-rows;
}
- 不要对每个 accordion 都加
will-change,只在触发前动态设置(比如mouseenter时),用完及时清除,避免过度触发合成层 - 内容高度变化(如换行、字体缩放)不会重新触发动画——这是预期行为,不是 bug;若需重播,得先切回
0fr再切过去 - 这个方案不依赖 magic number,比
max-height: 0 → max-height: 500px更健壮,但前提是父容器高度上下文必须真实存在


















