纯CSS驱动的checkbox+checked是JS失效时唯一保证基础可用性的方案;因其天然支持:checked伪类,配合兄弟选择器~可控制菜单显隐,而div/button无法通过CSS监听点击状态。

纯 CSS 驱动的 input[type="checkbox"] + :checked 是唯一能在 JS 失效时仍保证基础可用性的方案;其他依赖 JS 的实现,一旦脚本加载失败或被拦截,菜单就彻底不可见。
为什么不能用 div 或 button 做汉堡菜单开关
CSS 无法监听 div 或 button 的“点击后状态”,也就没法用 :hover 或伪类触发后续样式切换。而 input[type="checkbox"] 天然支持 :checked,配合兄弟选择器 ~ 就能精准控制 .nav-menu 显隐——这是零 JS 实现的核心前提。
- 写
<div class="hamburger"></div>再配 JS,等于把功能和可访问性全部押在 JS 上 -
label必须包裹或通过for关联该input,确保 iOS 触控区域 ≥ 44px - 别把
input放在.nav-menu后面——~只能选中后续兄弟元素,顺序错就失效
@media 断点下 display: none 和 max-height: 0 的取舍
想加展开收起动画?display: none 无法过渡,必须换用 max-height + overflow: hidden。
- 设
.nav-menu { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; } - 激活时设
input:checked ~ .nav-menu { max-height: 300px; }(数值需略大于所有子项总高) - 不要写死
300px:若菜单项动态增减,得预估最大高度,或改用 JS 补充计算 -
visibility: hidden不行——它仍占布局空间,会导致点击区域偏移、焦点错位
多级下拉菜单必须用嵌套 ul,且层级控制靠 > 和 :has()
用 div 堆嵌套结构,屏幕阅读器读不出父子关系,键盘 Tab 会跳过子菜单,移动端展开后定位也容易偏移。
立即学习“前端免费学习笔记(深入)”;
- 子菜单必须是对应
li的直接子元素:li > ul.nested,不能平级或外置 - 避免写
.menu-item.active ul { display: block; }——这会让所有深层子菜单一起弹出 - 正确写法:
.menu-item:has(> .nested).active > .nested { display: block; } -
:has()在 Safari 和 Chrome 已稳定支持,但 IE 完全不支持,需配合 class 降级(如class="has-submenu")
真正难的不是让菜单“动起来”,而是让它在 JS 失效、屏幕阅读器启用、键盘导航、缩放 200%、甚至网络极慢时,依然能被用户识别、抵达、操作——这些细节藏在 HTML 结构顺序、CSS 选择器精度和属性语义里,而不是动画时长或图标旋转角度中。



















