hidden 和 md:block 不该用于导航栏,因二者无互斥逻辑,会导致闪动、错位及可访问性失效;正确方案是用 flex md:hidden 与 hidden md:flex 组合,并配合 checkbox+peer 实现无障碍展开动画。

直接用 hidden 和 md:block 组合控制导航栏显隐,大概率会闪动或错位——这不是你写错了类名,而是这两个类没有互斥逻辑,最终表现依赖 JIT 编译顺序或 CSS 加载时机,不可控。
为什么 hidden md:block 不该用在导航菜单上
Tailwind 的断点类不是“覆盖”,而是“条件生效”。hidden 在所有断点都生效(display: none),md:block 只在 ≥768px 时生效(display: block)。两者共存时,浏览器按 CSS 权重和声明顺序决定谁赢——而这个顺序在 JIT 模式下可能随构建变化。更糟的是:hidden 会彻底移除文档流,导致后续 transition、焦点管理、屏幕阅读器支持全部失效。
- 移动端默认应显示汉堡按钮,隐藏主菜单 → 用
flex md:hidden(按钮) +hidden md:flex(菜单) - 桌面端主菜单必须稳定显示 → 避免任何
hidden类出现在桌面专属区域 - 若需动画,绝不能靠
hidden切换:改用opacity-0 max-h-0+peer-checked:opacity-100 peer-checked:max-h-96模拟
纯 CSS 实现点击展开,必须用 input[type="checkbox"] + peer
Tailwind 本身不处理点击交互。peer 类只响应兄弟元素的伪状态(如 :checked),不能监听 click。想不写 JS 就让菜单展开,结构必须严格满足:
-
<input type="checkbox" id="mobile-menu" class="sr-only">放在最前,不能hidden或display: none(否则屏幕阅读器无法识别) - 汉堡按钮是
<label for="mobile-menu"></label>或带for属性的<button></button>,与input同级相邻 - 菜单容器是
input的下一个兄弟元素,并用peer-checked:xxx控制样式,例如peer-checked:translate-x-0 peer-checked:opacity-100 - 桌面端必须强制重置:加
lg:translate-x-0 lg:opacity-100 lg:static,否则窗口缩放后菜单卡在展开态
菜单展开后错位、被裁剪?先查这三处
点了汉堡但菜单只露一条缝,或者整个消失,通常不是类名写错,而是布局链断裂:
- 菜单容器父级有
overflow-hidden(比如 navbar 外层用了overflow-hidden),直接把下拉内容裁掉 - 菜单没设
absolute定位,仍走文档流,导致位置飘移;正确写法是absolute top-full left-0 w-full mt-1 bg-white - 用了
transform: scale(0)或opacity-0却没配合transition-transform/transition-opacity,动画不触发 - 小屏下菜单高度坍缩:检查是否漏了
min-h-16或h-16,flex 容器无内容时高度为 0
移动端展开后键盘用户无法 Tab 进链接?焦点管理被忽略了
纯 CSS 方案容易忽略可访问性:peer-checked 能控制显隐,但不会自动把焦点移到菜单上。用户用键盘打开菜单后,Tab 键仍停留在汉堡按钮,进不了链接。
- 必须手动在 JS 中监听
input#mobile-menu的change事件,展开时调用menuContainer.focus() - 菜单容器加
tabindex="-1",确保能被聚焦 - 每个菜单项加
focus:outline-none focus:ring-2 focus:ring-blue-500,提供视觉反馈 - 收起菜单时(如点击链接跳转),SPA 场景需在路由守卫中清除勾选状态,否则下次打开仍是展开态
最易被忽略的其实是「桌面端强制重置」和「焦点链补全」——前者让菜单在响应式切换时不卡住,后者让键盘用户真正可用。这两点不加,导航栏就算视觉上跑通了,也算不上完成。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











