手风琴菜单必须用details元素才真正“纯css”,因其原生支持open属性和:open伪类,天然支持多层独立嵌套,无需js即可实现语义化、可访问、seo友好的折叠效果。

手风琴菜单必须用 details 元素才真正“纯CSS”
想不写一行 JS 就实现多层级展开收起,details 是唯一被现代浏览器原生支持的语义化可折叠元素。它自带 open 属性和 :open 伪类,无需监听点击、无需切换 class、无需处理事件冒泡——其他所有“纯CSS方案”(比如靠 checkbox + label 模拟)在多层嵌套时都会因选择器失效或状态同步失败而崩掉。
常见错误现象:input[type="checkbox"] + ul 套两层后,子级 checkbox 点击无法独立控制父级状态;或者 CSS 选择器写成 input:checked ~ ul ul,结果一展开父级,所有子 ul 全弹出来。
-
details天然支持嵌套:子details不受父级open状态影响,各自独立响应点击 - 移动端需加
ontouchstart兼容 iOS Safari 15.4 之前版本的点击延迟(仅需空事件,不执行逻辑) - 不能用
display: none隐藏summary,否则失去可访问性;要用visibility: hidden; position: absolute;配合 aria-label 替代
summary 样式重置必须处理三个关键点
默认 summary 有加号、有默认字体、点击区域小,不重置根本没法用在移动端。
- 移除原生箭头:
summary::marker { content: "" }(注意:Firefox 目前不支持,得补list-style: none+padding-left: 0) - 扩大点击热区:给
summary设min-height: 44px(iOS 最小触控尺寸),并确保内部文字垂直居中 - 禁用用户选中:
user-select: none,否则长按可能触发文本选择光标
示例关键样式:
summary {
min-height: 44px;
padding: 0 16px;
line-height: 44px;
user-select: none;
list-style: none;
}
summary::marker {
content: "";
}
details[open] > summary {
border-bottom: 1px solid #eee;
}
多层级动画只能靠 max-height + overflow 模拟
CSS 无法直接对 height: auto 做过渡,所以必须用 max-height 限定一个“足够大但不过量”的值。设太小会截断内容,设太大则动画慢且不精准。
- 推荐值:
max-height: 50vh(比内容实际高度略大,又不会撑满全屏) - 必须配
overflow: hidden,否则展开时内容会溢出 - 过渡只写
max-height和opacity,不要加height或transform——后者会导致子details内部布局错乱 - 关闭时用
max-height: 0,但要留padding-top/bottom的空间感,否则收缩太突兀
注意:iOS Safari 对 max-height 过渡有渲染 bug,若出现闪烁,加 will-change: max-height 可缓解。
无障碍与 SEO 必须保留 details 语义结构
用 div + aria-expanded 模拟虽然能动,但搜索引擎不识别折叠关系,读屏器也无法正确播报层级。真正的多级手风琴必须保持 HTML 结构语义化。
- 每个
details必须有summary作为第一子元素(否则不符合规范,部分读屏器跳过) - 子菜单用
details嵌套在父details的summary后面,不要放在summary内部 - 避免在
summary里放button——它会干扰details的原生行为,且触发两次 click - 如果需要图标,用伪元素
::before插入,并通过[open]切换transform: rotate(90deg)
最易被忽略的一点:所有 details 默认是关闭的,但首次加载时,你可能希望某一项默认展开。这时只能用 <details open></details>,不能靠 JS 设置——否则就不是“纯CSS”了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











