纯css折叠面板需用:checked伪类控制input状态,配合~兄弟选择器切换内容显隐;用max-height过渡替代height动画,注意overflow:hidden和初始值设置;radio互斥需name相同但无法原生取消,推荐checkbox方案;移动端需扩大label点击区域并注意无障碍缺陷。

用 :checked 配合隐藏单选/复选框实现折叠逻辑
纯 CSS 折叠面板的核心是把用户点击行为映射到表单控件的状态变化上,:checked 是唯一能触发状态切换且无需 JS 的伪类。必须用 <input type="checkbox"> 或 <input type="radio"> 作为控制开关——不能用 <button></button> 或 <div> 直接模拟,否则无法触发伪类响应。
<p>常见错误是把 <code>input 放在折叠内容之后,导致 ~(通用兄弟选择器)失效;正确做法是让 input 紧邻 label,再让 label 和折叠内容成为同级兄弟元素:
<input id="panel-1" type="checkbox"><label for="panel-1">标题</label> <div class="panel-content">内容</div>
这样就能用 input:checked ~ .panel-content 控制显隐。
用 max-height + overflow: hidden 实现平滑过渡
CSS 无法直接对 height: auto 做动画,所以必须用 max-height 替代。关键点在于:值要足够大以容纳所有可能内容,但又不能过大(否则动画过长)。常见取值是 max-height: 500px 或 600px,配合 transition: max-height 0.3s ease-in-out。
注意两点:
-
overflow: hidden必须加在折叠容器上,否则内容会溢出 - 初始状态设为
max-height: 0时,需同时设padding: 0和opacity: 0(可选),避免视觉残留 - 若内容含内边距或子元素有 margin,需在展开态统一重置,否则高度计算不准
解决多个面板互斥时的 radio 陷阱
想实现“一次只开一个”(手风琴效果),要用 type="radio" 并确保所有 radio 的 name 属性相同。但容易忽略的是:radio 默认没有“关闭”状态——一旦选中就无法取消,除非再点一次已选中的项(浏览器不支持原生取消)。
绕过方法只有两个:
- 用 JavaScript 补充取消逻辑(违背“无 JS”前提,排除)
- 接受设计妥协:允许用户点击已展开项再次收起,此时需给
input[type="radio"]添加:checked后的收起样式,并用~选择器反向控制自身后的面板
更稳妥的做法其实是回到 checkbox,靠 SCSS 循环生成唯一 id 和对应选择器,放弃互斥要求——多数真实项目里,允许多开比强求手风琴更实用。
移动端点击区域与可访问性补救
默认 input 不可见,仅靠 label 响应点击。但小屏设备上,如果 label 内容太短(比如只有「▶」图标),点击热区不足。必须显式扩大可点击范围:
给 label 加 display: block 和足够 padding(至少 12px),并确保 for 属性与 input 的 id 严格匹配。别用 label 包裹 input 再包内容的方式——那样会导致点击 label 时 checkbox 状态被意外翻转两次(一次由 label 触发,一次由内部 input 触发)。
另外,screen reader 依赖 aria-expanded 和 aria-controls,纯 CSS 方案无法动态更新这些属性,所以对无障碍要求高的场景,这个方案本质是有缺陷的——得承认这点,而不是硬凑。











