抽屉导航需用JS框架管理布尔状态实现显隐控制,遮罩须与抽屉同级且z-index正确,内容区需滚动处理与焦点陷阱,响应式应避免混用class并优先条件渲染。

抽屉导航需要一个可切换的布尔状态
移动端抽屉本质是「显隐控制」,Tailwind 本身不提供状态管理,必须依赖外部 JS 或框架(如 React、Vue)驱动 hidden/block 切换。纯 CSS 方案(比如用 :checked + 隐藏 checkbox)可行但交互受限:无法点击遮罩关闭、不支持键盘 ESC 关闭、动画难同步。
推荐用 useState(React)或 ref(Vue)维护 isDrawerOpen 状态,再通过条件 class 控制显示:
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
// ...
<div className={`fixed inset-0 z-50 transition-transform duration-300 ease-in-out ${isDrawerOpen ? 'translate-x-0' : '-translate-x-full'}`}>
{/* 抽屉内容 */}
</div>注意:transition-transform 必须和 translate-x-* 搭配才生效;-translate-x-full 是关键,它把抽屉完全推到视口外左边界,而非用 hidden——后者会导致子元素无法获得焦点,影响可访问性。
遮罩层必须独立于抽屉容器且层级正确
遮罩(overlay)不是抽屉的子元素,而应与抽屉同级并覆盖在页面内容之上。否则抽屉内部滚动时遮罩会随动,失去“锁定背景”的效果。
立即学习“前端免费学习笔记(深入)”;
常见错误是把 div.overlay 写在抽屉内部,或漏掉 z-40(抽屉设 z-50,遮罩需略低但高于页面内容):
- 遮罩需
fixed定位 +inset-0覆盖全屏 - 背景色建议用
bg-black/50(Tailwind v3.3+ 支持 opacity 语法),旧版本写bg-black bg-opacity-50 - 必须加
cursor-pointer并绑定点击事件关闭抽屉,否则用户点遮罩无响应 - 为支持键盘关闭,遮罩需
tabIndex={0}并监听onKeyDown捕获 ESC
抽屉内容区域要处理滚动与焦点陷阱
抽屉高度通常超过屏幕,内容需可滚动,但滚动行为容易出错:父容器未设 h-screen 或 max-h-screen,导致滚动条出现在整个页面而非抽屉内。
解决方案:
- 抽屉根容器设
h-screen(占满视口高) - 内部内容区用
overflow-y-auto+max-h-[calc(100vh-80px)](预留头部高度)或更稳妥的flex flex-col+flex-1 overflow-y-auto - 首次打开时自动将焦点移到抽屉第一个可聚焦元素(如首项
<a></a>或<button></button>),用useEffect+ref.current?.focus() - 关闭时焦点需返回触发按钮,否则键盘用户会迷失
忽略焦点管理会导致 WCAG 2.1 2.4.3 失败,实测中很多团队在这里被审计卡住。
响应式断点要配合桌面端回退逻辑
Tailwind 的 md: 前缀不能只用来「隐藏抽屉」,更要主动「启用默认导航」。否则在桌面端仍保留抽屉结构,只是不可见,HTML 语义混乱且可能影响 SEO。
正确做法是桌面端完全不渲染抽屉 DOM,或用 md:hidden 隐藏抽屉容器,同时用 md:flex 显示传统导航栏:
<nav class="md:flex hidden">...</nav> <button class="md:hidden" onclick="toggleDrawer()">☰</button> <div class="md:hidden fixed ...">...</div>
注意:不要对同一元素混用 md:hidden 和 md:block,Tailwind 会按顺序覆盖,易引发意外交互。优先用显式条件渲染(JSX 中 {isDesktop && <nav>}</nav>)比纯 class 控制更可靠。
真正麻烦的是横向滚动兼容性——iOS Safari 对 transform: translateX 的硬件加速不一致,偶尔出现闪屏。上线前务必在真机上用 Safari 反复测试抽屉开合动画。



















