手风琴动画(grid 折叠平滑展开动效)

CSSJS 许可:自研原创

手风琴动画组件:用 grid-template-rows 0fr 到 1fr 实现高度未知内容的平滑展开收起,独占式展开、箭头旋转、内容淡入,原生 button 无障碍,纯 CSS 加少量 JS。

效果演示(真实可交互)

点击任意标题,对应面板以平滑的高度过渡展开,右侧箭头旋转 180 度,内容淡入;再点其他标题,当前面板收起、新的面板展开,同一时间只保留一项展开。展开动画完全跟随内容实际高度,无论文字多长都不会出现「空跑」或跳变,收起时同样顺滑。

把内容容器设为 display: grid,再将 grid-template-rows 从 0fr 过渡到 1fr,高度未知的内容也能平滑展开,不再依赖 max-height 估算法,动画时长恒定且精确。

max-height 方案需要给一个足够大的上限值,展开过程会有一段「空跑」拖慢节奏;grid 0fr 到 1fr 的方案完全由内容实际高度撑开,快慢一致、无跳变。

可以。在折叠容器上监听 transitionend 事件即可,注意区分展开与收起的方向,并配合 transition-property 过滤掉其他属性的过渡事件,避免误触发。

标题使用原生 button 元素,天然支持 Tab 聚焦与回车、空格触发;同时维护 aria-expanded 属性反映展开状态,屏幕阅读器可以准确播报当前状态。

点击标题展开 / 收起;同一时间只保留一项展开

完整代码

HTML(标题按钮 + 双层内容容器):

<div class="acc-item open">
  <button class="acc-head" aria-expanded="true">标题文字</button>
  <div class="acc-body">
    <div class="acc-inner">内容文字,高度任意</div>
  </div>
</div>

CSS(grid 折叠动画的核心三行):

.acc-body {
  display: grid;
  grid-template-rows: 0fr;                /* 收起:行高为 0 */
  transition: grid-template-rows .4s ease;
}
.acc-item.open .acc-body { grid-template-rows: 1fr; }  /* 展开:撑满内容 */
.acc-inner { overflow: hidden; }          /* 防止内容溢出 */

JS(独占式展开,约 12 行):

const items = document.querySelectorAll('.acc-item');
items.forEach((item) => {
  item.querySelector('.acc-head').addEventListener('click', () => {
    const willOpen = !item.classList.contains('open');
    items.forEach((it) => {
      it.classList.remove('open');
      it.querySelector('.acc-head').setAttribute('aria-expanded', 'false');
    });
    if (willOpen) {
      item.classList.add('open');
      item.querySelector('.acc-head').setAttribute('aria-expanded', 'true');
    }
  });
});

为什么用 grid 而不是 max-height:传统方案需要猜一个足够大的 max-height 上限,内容少时动画会「空跑」,内容多时又被截断;而 grid-template-rows 的 0fr 到 1fr 过渡由浏览器直接按内容真实高度插值,时长恒定、精确贴合,是近年来最优雅的纯 CSS 折叠方案。内层 .acc-inner 负责裁切溢出,JS 只做「独占展开」与 aria 状态同步。

参数说明

参数位置说明
展开时长transition: ... .4s250-600ms,内容多可调长
缓动曲线transition 的 easingease 或 cubic-bezier 均可
箭头方向.acc-chev 的旋转角度45 度收起 / 225 度展开
内容淡入.acc-inner p 的 acc-fade可选,配合展开更有层次
展开策略JS 的 open 逻辑演示为独占式,可改多开

兼容性与性能

常见问题

Q: 展开时内容一闪而过没有动画? 检查浏览器版本是否支持 grid-template-rows 过渡(Chrome 107+)。也可以确认 .acc-inner 是否设置了 overflow: hidden,没有它内容会在动画期间溢出。

Q: 想支持同时展开多个面板? 把 JS 里「先全部收起」的循环去掉,只切换当前项的状态即可;此时每个面板的动画互不干扰,grid 方案天然支持多开。

Q: transitionend 回调触发多次? transition-property 默认监听所有过渡属性。若同时过渡了多个属性,在回调里过滤 e.propertyName 为 grid-template-rows 的再处理,可避免重复执行。

Q: 能商用吗? 可以。本组件为粤喵自研原创实现,免费商用无需署名。

许可与来源

  • 许可证:自研原创 —— 粤喵自研原创,可免费用于个人与商业项目,无需署名。查看来源公示与许可清单
  • 来源:粤喵团队原创自研(从效果思路到代码实现均为独立完成,未直接复制第三方代码)
  • 首次发布:2026-09-01 · 最近更新:2026-09-01
  • 修改日志:2026-09-01 首次发布;2026-09-13 补充许可证与来源标注、复制按钮说明与法务页入口
  • 纠错:联系页面 / min@cactus.asia,2 个工作日内核实处理

常见问题

这个组件可以商用吗?

可以。本组件为粤喵自研原创,可免费用于个人和商业项目,无需署名。

浏览器兼容性如何?

支持所有现代浏览器(Chrome / Firefox / Safari / Edge 最新版本)。旧版 IE 不支持 CSS 动画相关特性。

怎么修改动画参数?

直接调整 CSS 中的 animation-duration(时长)、animation-timing-function(缓动曲线)等属性即可,代码中有注释说明。

✍️ 作者与审核