原生 是最轻量语义化的折叠方案,无需 js、键盘可访问、屏幕阅读器友好;但需遵守 为首个子元素、不可嵌套等限制,高度动画须用 max-height + scrollheight 配合 js 实现。

用 <details></details> 和 <summary></summary> 实现原生折叠按钮
这是最轻量、最语义化、也最不容易出错的方案——不用写一行 JS,键盘可访问,屏幕阅读器能识别,连 ARIA 都不用补。
关键限制只有一个:<summary></summary> 必须是 <details></details> 的第一个子元素,否则点击无效;<details></details> 不能嵌套(Safari/Edge 会失效)。
- 默认收起:
<details><summary>标题</summary>内容</details> - 默认展开:加
open属性,即<details open>...</details> - 想换箭头?别碰
summary::marker在 Safari 里基本没用,改用summary::before+transform: rotate() - 不要给
<details></details>设height过渡——它不支持,强行加只会卡顿或失效
需要平滑高度动画?必须用 JS 控制 max-height
原生 <details></details> 没有高度过渡,要“滑动展开”,就得自己接管逻辑。核心不是 JS 多难写,而是 CSS 动画怎么骗过浏览器:不能对 height: auto 做 transition,只能靠 max-height 模拟。
- JS 获取内容真实高度:
element.scrollHeight,设为max-height并加transition - 收起时必须把
max-height设回0,同时加overflow: hidden,否则滚动条可能残留 - 别用
display: none切换——它无法动画,且会打断焦点流和可访问性 - 如果内容含图片或异步加载元素,
scrollHeight可能取早了,得监听load或用ResizeObserver
纯 CSS 方案:用 input[type=checkbox] + ~ 选择器
适合静态内容、高度可控的场景,完全零 JS,但语义弱、键盘操作受限(需额外处理 Tab 和 Space)。
-
<input type="checkbox" id="panel1">隐藏,<label for="panel1"></label>当按钮 - 内容区域用
label ~ .content或input:checked ~ .content控制显隐 - 动画靠
max-height过渡,但必须预设一个足够大的值(比如max-height: 500px),否则内容超长会截断 - 无法响应内容动态变化(比如 JS 插入新段落),也不支持单开手风琴模式
自定义按钮时容易忽略的可访问性细节
手动实现的按钮常被当成普通 <div> 或 <code><span></span>,导致键盘用户按空格/回车无反应,屏幕阅读器读不出状态。
- 触发元素必须是
<button></button>,或至少加role="button"+tabindex="0" - 动态更新
aria-expanded="true/false",并用aria-controls="id"关联内容区 - 内容区加
role="region",让辅助技术知道这是可展开的独立区块 - 不要只靠颜色区分展开/收起状态,加图标或文字提示(如“展开”→“收起”)
真正麻烦的从来不是“怎么让它动起来”,而是动完之后,键盘用户能不能继续操作、屏幕阅读器能不能正确播报、内容变多时动画会不会崩。选方案前,先问一句:这个面板谁在用?











