CSS Grid 中 grid-template-rows 不支持 transition,因属离散布局函数;应改用 max-height(配合 JS 动态测量)或 transform: scaleY() 实现平滑展开动画,并配 grid-auto-rows: minmax(0, max-content) 防布局跳动。

Grid 中直接对 grid-template-rows 做 transition 无效
CSS Grid 的 grid-template-rows 和 grid-template-columns 是离散布局函数,浏览器不支持对其做插值动画。你写 transition: grid-template-rows 0.3s,实际完全没反应——这不是写错了,是规范限制。
常见错误现象:卡片点击后突然展开/收起,没有过渡;DevTools 里看到 transition 属性被标记为 “invalid” 或灰掉。
- 真正能触发平滑过渡的属性只有:opacity、transform、max-height(需设具体数值)、height(仅当元素有明确 height 时才可靠)
- 用
max-height是最常用折中方案,但必须设一个「足够大且确定」的值,不能用max-height: none(它无法插值) - 如果卡片内容高度差异大,硬写死
max-height: 500px可能导致收起时留白,或展开时被截断
用 max-height + overflow 实现可预测的展开动画
核心思路是让容器在收起时靠 max-height: 0 + overflow: hidden 隐藏内容,展开时过渡到一个大于实际内容高度的固定值。
关键不是“猜高度”,而是用 JS 测量并动态注入 —— 这样既保持 CSS 动画性能,又避免硬编码。
立即学习“前端免费学习笔记(深入)”;
- 给卡片容器加 class
card-expandable,初始状态设max-height: 0; overflow: hidden; - 展开时,先用
element.scrollHeight获取真实高度,再设置style.maxHeight = scrollHeight + 'px' - 动画结束后(监听
transitionend),清除内联max-height,改用auto保证后续内容变化仍可用(但此时不再触发动画) - 收起时反向操作:先设回
max-height到当前值(强制重排),再设0
const card = document.querySelector('.card');
const toggleBtn = document.querySelector('.toggle-btn');
toggleBtn.addEventListener('click', () => {
const isExpanded = card.classList.toggle('expanded');
if (isExpanded) {
card.style.maxHeight = card.scrollHeight + 'px';
} else {
card.style.maxHeight = '0';
}
});
card.addEventListener('transitionend', () => {
if (!card.classList.contains('expanded')) {
card.style.maxHeight = 'none';
}
});
Grid 容器需配合 grid-auto-rows 或 minmax() 避免布局跳动
卡片展开后高度变大,如果 Grid 容器用的是 grid-template-rows: 1fr 1fr 这类固定划分,新高度会撑开整行,导致下方卡片整体下移——这不是动画问题,是 Grid 行高没预留弹性空间。
- 把
grid-template-rows换成grid-auto-rows: minmax(0, max-content),让每行自动适应内容高度 - 或者用
grid-auto-rows: 100px+grid-row: span 2手动控制展开卡占据多行(适合卡片数量少、布局可控场景) - 避免在展开卡片上同时使用
align-items: center或justify-items: center,它们会干扰高度计算和 overflow 截断
用 transform 替代 height 更稳定,但需绕过 Grid 约束
如果对动画精度要求极高(比如要兼容 Safari 旧版或避免 layout thrashing),transform: scaleY() 是更安全的选择:它不触发重排,GPU 加速,且能从 0 → 1 平滑过渡。
但它和 Grid 冲突——Grid 会按原始尺寸分配空间,缩放后的视觉高度不影响布局,容易造成重叠或留空。
- 解决方案:把卡片内容包一层
<div class="card-content">,只对这个子元素做transform: scaleY() - 父卡片保持 Grid 占位不变,用
overflow: hidden裁剪超出部分 - 注意:
scaleY(0)会让子元素完全不可见,包括 focusable 元素,需同步管理aria-hidden和 tabindex
这种方案动画最顺,但语义和可访问性要额外处理,不是“设个 transition 就完事”的简单路径。


















