+是最轻量语义方案,原生支持交互与无障碍,但iOS 15.4前无open动画;需自定义动画或兼容老iOS时改用++配合JS和CSS实现。

移动端折叠菜单的 HTML 结构怎么选
用 + 是最轻量、语义最准的方案,原生支持展开/收起、键盘操作(空格/回车)、屏幕阅读器,且无需 JS 就能工作。但要注意:iOS Safari 15.4 之前不支持 open 属性动画,且无法用 CSS 精确控制箭头方向或过渡效果。
如果需要自定义动画、多级嵌套或兼容老版本 iOS,就得换为 + + 结构,靠 JS 切换 aria-expanded 和类名,再用 CSS 控制 max-height 或 transform 实现折叠。
CSS 实现平滑折叠动画的关键点
纯 CSS 折叠动画不能直接对 height: auto 做过渡,否则无效。必须用以下任一方式:
- 用
max-height设一个足够大的固定值(如max-height: 500px),并配合overflow: hidden;缺点是内容超长时会截断 - 用
transform: scaleY()+origin-top,搭配opacity,性能更好,但需确保父容器有overflow: hidden - 用
clip-path(如clip-path: inset(0 0 100% 0)),动画更自然,但 Safari 15.2+ 才稳定支持
所有方案都必须显式声明 transition,且避免在 :focus-within 或伪类中漏掉过渡属性。
点击穿透和焦点管理的常见坑
移动端常因点击区域太小、touch-action: manipulation 缺失或 pointer-events 错误导致菜单点不响或连点失效。务必检查:
-
的type="button"是否明确设置,防止表单提交干扰 - 折叠菜单容器是否意外设置了
pointer-events: none(比如为了透传背景点击) - 使用
focus-visible区分键盘与鼠标焦点,避免移动端误加 outline - 收起菜单后,焦点应返回触发按钮(用
button.focus()),否则键盘用户会卡在页面底部
适配不同屏幕尺寸的断点策略
不要只按设备宽度切,而是按内容密度决定折叠时机。例如:
- 导航项 ≤ 4 个时,桌面端也保持展开;≥ 5 个才启用折叠
- 用
@container(需父容器设container-type: inline-size)替代媒体查询,让菜单响应其容器宽度而非视口 - 避免在
min-width: 768px就强制展开——平板横屏可能只有 834px 宽,但字体放大后实际可用空间更小
真正可靠的判断依据是:导航栏是否发生换行或文字被压缩。可借助 resize-observer 监听容器尺寸变化,动态切换模式。
.nav-menu {
--menu-height: 0;
max-height: var(--menu-height);
overflow: hidden;
transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.nav-menu[aria-expanded="true"] {
--menu-height: 300px;
}
复杂点在于:动画节奏要匹配手指操作反馈感,太快显得突兀,太慢又像卡顿;而最容易被忽略的是,折叠菜单一旦进入无障碍树,就必须同步维护 aria-hidden 和 tabindex 状态,否则 TalkBack 或 VoiceOver 会读出不可见项。










