手风琴组件应使用原生details元素实现真正可访问性,它自带键盘支持、屏幕阅读器语义和焦点管理;需配合Tailwind的group/group-open、max-height过渡及summary样式增强,禁用JS模拟。

手风琴组件必须用 details 元素才真正可访问
很多教程用 div + aria-expanded + 手动切换类来模拟,但这是退化方案。原生 details 元素自带键盘支持(空格/回车展开)、屏幕阅读器语义和默认焦点管理,Tailwind 完全可以配合它工作,而不是绕开它。
关键点:
-
details的open属性控制展开状态,无需 JS 控制 DOM 类名 - Tailwind 的
group和group-open:可以精准作用于details > summary的后代元素 - 不要给
summary设display: none或移除它——这会破坏可访问性 - 若需禁用默认箭头,用
summary::marker { content: "" },而非隐藏整个summary
group-open: 是实现平滑过渡的核心,但不能直接用于 height
Tailwind 的 group-open:height-0 无效,因为 height 不是 CSS 可过渡属性(除非显式设为具体数值)。正确做法是结合 max-height + overflow-hidden,并用 transition 控制 max-height 变化。
实操建议:
- 给折叠内容容器(
details > *且非summary)加max-h-0 overflow-hidden transition-[max-height] duration-300 ease-in-out - 在
details[open]状态下,用group-open:max-h-[300px](值需略大于内容实际高度) - 避免用
group-open:max-h-full:CSS 无法对full做过渡动画 - 若内容高度差异大,可为不同区块设不同
max-h-值,或改用animate-[slide-down_300ms_ease-in-out]配合自定义 keyframes
多个手风琴项共存时,如何避免同时展开
details 默认行为就是单例展开(浏览器原生支持),无需 JS 干预。但如果你看到多个同时打开,大概率是因为:
- 误用了多个独立
details而没包裹在统一容器中 —— 实际上这完全合法,浏览器仍按单个元素处理 - 手动加了 JS 强制设置所有
open属性,覆盖了原生逻辑 - 用了第三方库或自定义组件封装,内部把
details当成普通 div 处理
真正需要「手风琴互斥」时,只有一种情况:你明确要求「最多一个展开,点击已展开项应收起」。这时只需确保每个 details 是独立的,并**不添加任何 JS 切换逻辑** —— 浏览器默认就满足该行为。
移动端点击区域小、视觉反馈弱?用 Tailwind 补足
summary 默认点击热区窄,且无 hover/focus 样式。Tailwind 可快速增强:
- 扩大点击区域:
summary:before { content: ""; position: absolute; inset: -8px; }(配合relative) - 添加焦点轮廓:
focus:outline-none focus:ring-2 focus:ring-blue-500(加在summary上) - 区分展开/收起状态图标:
group-open:rotate-180配合transform的svg或伪元素 - 禁用移动端长按选中:
select-none加在summary上,避免误触发
注意:所有这些增强都建立在保留 summary 语义的前提下。删掉它或用 div 替代,就等于放弃无障碍基础。
最常被忽略的其实是内容高度估算 —— max-h-[300px] 写死值容易导致动画卡顿或截断。如果内容动态加载或行高不固定,宁可用 JS 动态设 style.maxHeight,也别硬塞一个不准确的 max-h- 工具类。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











