最简手风琴效果首选原生+:语义正确、键盘可访问(空格/回车触发)、无需js;默认收起,加open属性可默认展开;须为首个子元素;动画需用max-height过渡模拟,因height: auto不可过渡。

用 details + summary 实现最简手风琴
纯 CSS 实现手风琴,最直接的方式就是用原生 HTML5 的 details 和 summary 元素——它们天生支持展开/收起,无需 JS,语义正确,还自带可访问性(键盘 Enter / Space 可触发)。
常见错误是试图用 div + :hover 或 :focus-within 模拟,结果无法持久保持状态、不支持键盘操作、屏幕阅读器识别不了。
实操建议:
-
details默认是关闭的,加open属性可默认展开:<details open><summary>标题</summary>内容</details> -
summary前的三角图标是浏览器默认样式,可用summary::marker控制(Chrome/Firefox 支持,Safari 18.1+ 才支持) - 想禁用默认箭头并自定义:先
summary::marker { content: "" },再用summary::before插入图标或旋转效果 - 动画需配合
details[open] > *:not(summary)选择器控制内容区域的max-height过渡(注意:height: auto无法过渡,必须设具体值或用max-height)
给 details 添加平滑展开动画
原生 details 不支持 CSS 过渡,但可以通过包裹内容 + max-height 模拟动画。关键在于避免硬编码高度值,否则响应式下易出错。
实操建议:
- 内容区用
<div class="accordion-content">... 包裹,不要直接放文本节点<li>CSS 中写:<code>details[open] .accordion-content { max-height: 500px; transition: max-height 0.3s ease-in-out; } - 初始状态设
.accordion-content { max-height: 0; overflow: hidden; } - 500px 是“足够高”的安全值,实际内容若超过此高度会截断;更稳妥的做法是用 JS 测量后设内联
style,但那就不是“纯 CSS”了 - 不要用
opacity或transform单独做动画——它们不改变文档流,收起后仍占位 - 给所有
details加同一 class,如js-accordion-group - JS 只做一件事:当某个
details触发toggle且处于open状态时,遍历同组其余details并设open = false - 别用
click事件代替toggle——它无法捕获键盘触发的展开 - 如果坚持纯 CSS,只能接受多开(这是原生行为,其实更符合无障碍原则)
- 检测支持性:
if (!("open" in document.createElement("details"))) { /* 降级为 JS 方案 */ } - 降级时优先复用同一套 CSS 类名,仅替换 HTML 结构和绑定逻辑,避免样式重复维护
- 别用
@supports (display: details)——该特性无对应 CSS 特性可查,@supports查不到 - 真要兼容 IE,就放弃纯 CSS 路线,直接上轻量 JS(比如 20 行以内的
classList.toggle+max-height计算)
多个面板互斥(单选展开)需要 JS 吗?
纯 CSS 无法监听其他 details 的状态变化,所以「点击一个展开时自动收起其他」必须用 JS。但可以最小化侵入:只监听 toggle 事件,不操作 DOM 结构。
实操建议:
Safari 旧版本兼容问题怎么绕过?
Safari ≤ 15.6 对 details 的 transition 支持极差,max-height 动画会卡顿或失效;部分安卓 WebView 也不支持 details。
实操建议:
手风琴看着简单,真正落地时最常被忽略的是键盘交互和屏幕阅读器反馈——details 天然解决这两点,比任何 DIY 的 div + aria-expanded 都省心。动画只是锦上添花,别让它拖垮可访问性。











