浏览器不支持 height: 0 → height: auto 的过渡动画,因 auto 无法插值;grid-template-rows 的 0fr → 1fr 则通过比例插值得到原生平滑缩放,需配合 overflow: hidden 和父容器 grid 布局实现。

为什么直接用 height: 0 → height: auto 不会动
浏览器根本不支持对 auto 值做插值计算,transition 只能作用于“可量化、可线性变化”的数值。你写 height: auto,CSS 引擎拿到的是一个计算后的具体像素值(比如 42px),但过渡引擎无法从 0 推出这个 42 是怎么来的,更没法在帧之间生成中间状态。所以它直接跳变,没有动画。
grid-template-rows 切换是少数真正支持 0 ↔ auto 语义的方案
Grid 的 grid-template-rows 允许使用 0fr 和 1fr(或 fit-content()、max-content)来控制行高,且现代浏览器(Chrome 66+、Firefox 70+、Safari 16.4+)对 0fr → 1fr 的切换做了原生插值支持——它不是在插值像素,而是在插值“分配比例”,因此能平滑缩放行高。
关键点:
-
0fr行实际高度为0(内容不可见,不占空间),1fr行则按剩余空间自适应撑开 - 必须配合
overflow: hidden防止内容溢出破坏折叠效果 - 不能用
display: grid直接套在要动画的容器上——要把它作为父容器,让目标内容成为其子项(即网格项) -
grid-template-rows: 0fr和grid-template-rows: 1fr必须写在同一个父元素上,靠 class 切换触发
示例结构:
立即学习“前端免费学习笔记(深入)”;
.accordion {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease-in-out;
overflow: hidden;
}
.accordion.open {
grid-template-rows: 1fr;
}
.accordion > * {
/* 内容项,自动填满当前行高 */
margin: 0;
}
和 max-height 方案比,grid-template-rows 的真实优势与限制
它不是“模拟”,而是语义级支持:不需要预估最大高度,不担心内容超长被裁剪,也不依赖 JS 测量。但要注意:
- 只适用于单行内容容器(比如一个
<div>包裹全部展开内容),多行网格布局不适用 - Safari 16.4 之前版本不支持
0fr→1fr过渡,旧版需降级到max-height - 如果内部有
position: absolute元素,它们脱离文档流,高度可能不参与1fr计算,导致视觉错位 -
transition时间不能设太短(如0.1s),否则 Safari 下容易跳帧;推荐0.25s–0.35s
别漏掉的兼容兜底和调试技巧
实际项目中,不能只信 grid-template-rows。建议加一层渐进增强:
- 先写
max-height方案作为 fallback(用@supports not (grid-template-rows: 0fr)包裹) - 用
getComputedStyle(el).gridTemplateRows在控制台验证是否生效,返回值应为"0fr"或"1fr",而不是"none" - 若动画卡顿,检查是否同时设置了
margin或padding过渡——它们会触发 layout,拖慢 grid 过渡 - 移动端 Safari 对
fr插值较敏感,避免在:hover上直接切,改用click+ class 切换更稳
真正难的不是写对那两行 CSS,而是判断什么时候该用 grid-template-rows,什么时候该退回去用 JS 算高——尤其当内容高度动态变化(比如异步加载后 DOM 增长)时,1fr 不会自动重算,得手动触发布局更新。


















