纯 css 实现折叠面板平滑动画必须用 max-height 搭配固定值过渡,禁用 height: auto 和 display 切换;因浏览器无法对 auto 插值,max-height: 0→auto 失效,需设足够大但不过量的固定值(如 30em),并始终配合 overflow: hidden、显式 transition 声明及可访问性保障。

纯 CSS 实现折叠面板的平滑展开/收起,必须放弃 height: auto 和 display 切换——这两者无法参与 CSS 过渡。真正可行的路径只有一条:用 max-height 搭配固定数值过渡,并严格控制溢出与可访问性。
为什么 max-height: 0 → auto 动画会失效
浏览器无法对 auto 做插值计算,transition 直接跳过。哪怕写成 max-height: 0 → 100% 也无效,因为百分比在非 flex/grid 容器中无参考基准。
- 常见错误现象:
.panel { max-height: 0; transition: max-height 0.3s; }+.panel.open { max-height: auto; }→ 动画不触发,内容瞬间出现 - 安全做法是预设一个“足够大但不过量”的固定值,例如
max-height: 30em或max-height: 500px - 过大会导致收起时留白(动画结束但高度仍远大于 0);过小会截断内容(动画中途被卡住)
- 若内容高度波动大,纯 CSS 就不适合——得切回 JS 动态读取
scrollHeight并写入内联max-height
<details><summary></summary></details> 是最简解,但样式受限
原生 <details></details> 元素自带 open 属性切换、键盘支持(Space/Enter)、aria-expanded 同步,无需 JS。
- 禁用默认箭头只需
summary::marker { display: none; },别用display: none隐藏整个<summary></summary>,否则失去可聚焦性 - 自定义展开动画需覆盖默认行为:先
details[open] summary { margin-bottom: 0.5em; },再用details[open] .content { max-height: 30em; opacity: 1; } - 别在
<details></details>外层套div并对其用:focus-within控制动画——内部<summary></summary>虽可聚焦,但部分安卓 WebView 不响应其:focus-within
overflow: hidden 不是可选项,是硬性前提
max-height 过渡要生效,overflow: hidden 必须始终存在。漏掉它,内容会在动画过程中溢出、遮挡其他元素,甚至破坏布局流。
- 初始状态:
max-height: 0; overflow: hidden; opacity: 0; - 展开状态:
max-height: 30em; overflow: hidden; opacity: 1;(注意:不是overflow: visible) - 如果同时用
opacity,务必把transition显式写成transition: max-height 0.3s ease, opacity 0.3s ease,避免all触发意外重排 - Safari 旧版本可能忽略
max-height过渡,加transition-property: max-height, opacity提升兼容性
移动端点击无响应?大概率是焦点链断了
纯 CSS 折叠依赖用户交互触发状态变更(如 <details></details> 的 click、:focus 或 :focus-within),而不是监听事件。没有可聚焦能力,就等于没入口。
- 检查
<summary></summary>是否被替换成<div> 或 <code><span></span>—— 若必须自定义,至少加tabindex="0" - 别给父容器写
.panel:focus-within .content { ... },除非内部有明确可聚焦子元素(比如input、带tabindex的按钮) - 安卓 WebView 对无
tabindex的元素上的:focus-within支持极差,优先走原生<details></details> - 收起后底层内容被误点,不是“穿透”,而是
opacity: 0未同步关闭交互——必须配pointer-events: none或确保max-height: 0+overflow: hidden已生效
真正难的不是写出动画,而是让所有尺寸、焦点、语义、兼容性在同一套规则下不打架。预设高度值、死守 overflow: hidden、不碰 display、不绕开原生语义——这些约束看似笨重,恰恰是纯 CSS 方案能稳定落地的边界。











