你以为 height: auto 只能靠 JS 算?今天 Chrome 把这件事用一行 CSS 彻底原生化了
每次做手风琴、折叠面板、下拉菜单,都会遇到同一个坎:想做个从 0 到 auto 的过渡动画,CSS 根本不支持——height: auto 不是数字,浏览器不知道怎么在中间插值。
于是前端圈子里流传了两套土办法。
方案一:JS 量尺寸。 监听展开事件,JS 读 scrollHeight,设成固定像素值,动画结束后再 snap 回 auto。整个流程要写十几行,还要处理 transitionend 回调,一不小心就死循环或者抖一下。
方案二:max-height 大法。 设一个足够大的 max-height 值,比如 9999px,假装「足够大就是 auto」。问题是浏览器要从 0 插值到 9999px,每一帧都在做多余的计算,而且这个数字选小了内容会被截,选大了动画慢得要死。
两套方案都在解决同一个问题:height: auto 历史上就不是可插值类型,CSS 不知道它的实际像素值是多少。
Chrome 129 把这件事用一行 CSS 原生化了:
:root {
interpolate-size: allow-keywords;
}
就这么一句。设到 :root 上,全局生效。
现在 height: 0 → height: auto 的过渡,浏览器自己会在动画开始时把 auto 解析成真实像素值,然后做线性插值。JS 不需要了,max-height 不需要了,transitionend 回调也不需要了。
原理很简单:以前浏览器默认 intrinsic size 关键字(auto / min-content / max-content / fit-content)不可插值,因为它们的值取决于内容,浏览器不知道中间帧该是多少。interpolate-size: allow-keywords 告诉浏览器「我愿意让你在动画时去算这个值,你就去算吧」。因为 CSS 规范担心默认开启会影响已有页面(很多老站点假设 auto 不能做动画),所以需要显式 opt-in。
性能收益是真实的。 原来的 JS 方案每帧都要读 scrollHeight,这是典型的 Forced Synchronous Layout,动画过程中主线程被反复阻塞。interpolate-size 让浏览器自己处理插值,浏览器可以在合成器线程做动画,完全不触发主线程。对于 INP(Interaction to Next Paint)指标来说,把 JS 从动画帧里摘出去,收益非常显著。
支持的值不只有 auto,还包括 min-content、max-content、fit-content,以及 flex-basis 的 content。适用场景覆盖所有「内容驱动高度」的交互:手风琴、评论折叠、FAQ、下拉菜单、浮动面板展开。
用法:
:root {
interpolate-size: allow-keywords;
}
.accordion {
height: 0;
overflow: hidden;
transition: height 0.3s ease;
}
.accordion.open {
height: auto;
}
渐进增强写法:
@supports (interpolate-size: allow-keywords) {
:root {
interpolate-size: allow-keywords;
}
}
不支持的浏览器内容直接 toggle,没有动画,体验降级但功能正常。
两个坑要注意。
第一,只支持「一端是固定值、一端是 intrinsic keyword」的过渡,两个 intrinsic 关键字之间不能做动画。如果要做 calc-size() 那样的运算,用 calc-size() 函数,它内置了 allow-keywords 行为。
第二,动画过程中浏览器会持续重新计算 intrinsic size,如果元素很大或者嵌套层级很深,动画帧率可能受影响。建议只对中小型组件使用,大面积内容切换还是直接 display: none / block 更划算。
这一行 CSS 的真实价值:把 JS 里那段「专门用来骗浏览器做动画」的代码彻底删掉。删掉之后,动画变快了,INP 变好了,代码变少了,页面变轻了。
评论区
登录后可评论。