必须用 input[type="checkbox"],因其 :checked 伪类支持 CSS 过渡、键盘操作及屏幕阅读器;label 与 input id 必须严格匹配;nav 需同级兄弟结构;max-height 过渡优于 transform 以保持文档流;移动端需重置 flex-direction 为 column。

纯 CSS 实现汉堡菜单,input[type="checkbox"] 是唯一可靠的状态开关;用 div 或 button 模拟点击,后续所有动画和可访问性都会失效。
为什么必须用 input[type="checkbox"] 而不是 JS 控制显示
因为只有 :checked 伪类能触发 CSS 过渡,且天然支持键盘空格/回车切换、屏幕阅读器朗读状态。JS 控制 display 或 visibility 会导致动画卡顿、焦点管理混乱、iOS Safari 上偶尔失焦。
-
input必须有id,label的for属性必须与之严格匹配,否则点击无响应 - 不能设
display: none—— 会跳过辅助技术,应改用position: absolute; left: -9999px -
label和nav必须是同级兄弟(或input后紧跟label,再跟nav),才能用input:checked ~ nav选择器生效 - 所有参与汉堡图标的元素(
span、::before、::after)都要加user-select: none和pointer-events: none,否则点选区域错乱
max-height 过渡比 transform 更适合导航栏展开
虽然 transform: translateY 动画性能更好,但对导航栏来说,它不推挤主内容——main 会被盖住,而不是自然下移。而 max-height 让导航栏保留在文档流中,高度变化时能真实影响布局。
- 初始值必须是
max-height: 0; overflow: hidden,不能是height: 0(height 不支持 transition) - 展开值不能写死
max-height: 500px就完事——要略大于最大可能高度(比如 8 项 × 行高 48px = 384px,建议设420px) - 如果菜单含下拉子项,子菜单的
max-height也得单独设过渡,不能只靠父级控制 - 过渡时间建议
0.3s ease-out,太快显得突兀,太慢用户会误以为没响应
移动端垂直堆叠必须重置 flex-direction 和定位
桌面端用 display: flex; flex-direction: row 没问题,但到移动端若只隐藏菜单、不改布局方向,点击展开后链接仍会横向挤成一行——这不是 bug,是你忘了切布局流。
立即学习“前端免费学习笔记(深入)”;
- 在
@media (max-width: 768px)中,给.nav-menu加flex-direction: column和width: 100% - 不要给
nav设position: absolute或fixed—— 它会脱离文档流,导致主内容不自动下移 - 若需固定在顶部,应把整个
<header></header>设为position: sticky,让nav保持普通文档流位置 - 子菜单(如二级
ul)在移动端建议默认收起,用max-height: 0+opacity: 0双控,避免撑开父容器
真正难的不是写出能动的汉堡菜单,而是让它的每一帧都符合语义、可聚焦、不遮挡内容、且在 iOS 和 Android 上行为一致——这些细节藏在 input 的隐藏方式、max-height 的预估值、以及 flex-direction 切换的时机里。



















