垂直导航栏应使用语义化的 ul+li 结构,外层用 nav 包裹;a 标签需设 display: block 和 width: 100% 并配合 box-sizing: border-box;激活项必须用 aria-current="page" 而非仅 class;同时支持 :hover、:focus 和 :focus-within 以保障键盘与触屏可访问性。

用 ul + li 做结构,别用 div 堆砌
垂直导航栏本质是语义化的列表,ul 是最自然的选择。浏览器默认会把 li 垂直堆叠,省去手动排布的麻烦。如果用一堆 div,反而要写更多 CSS 控制间距、对齐和焦点状态,还容易破坏可访问性。
常见错误是给每个菜单项加独立 ID 或 class,比如 nav-home、nav-about——没必要。统一用 ul 包裹,所有 li 共享样式,维护成本更低。
实操建议:
- 外层用
nav标签包裹ul,提升语义和屏幕阅读器识别率 -
li内部只放a,不要嵌套div或span作为点击区域 - 避免给
a设固定高度,优先用padding控制点击热区
display: block 和 width: 100% 是点击区域的关键
默认情况下 a 是行内元素,宽度仅包裹文字,导致鼠标悬停或点击时容易漏掉空白区域。必须让它撑满父 li 的宽度,并具备块级行为。
常见错误是只写 display: block 却忘了设宽度,结果在某些布局下(比如 li 有 padding)出现点击错位;或者用 width: 100% 但没处理盒模型,导致内容溢出。
实操建议:
- 给
a同时设置display: block和width: 100% - 用
box-sizing: border-box避免padding加宽后超出容器 - 移除
a默认的text-decoration,用border-left或背景色做选中态更可控
当前激活项用 aria-current="page" 而不是仅靠 class
很多教程只教加个 class="active" 然后配 CSS 高亮,但这对屏幕阅读器不友好。用户依赖辅助技术时,光看样式无法获知“当前在哪”,必须有明确的语义标记。
错误做法是用 JavaScript 动态切换 class 却忽略 ARIA 属性;或者把 aria-current 写死在 HTML 里却不随路由更新。
实操建议:
- 服务端渲染或静态页:直接在对应
a上写aria-current="page" - 前端路由(如 React Router、Vue Router):用对应钩子(
useMatch/useRoute)动态绑定该属性 - CSS 中可用
a[aria-current="page"]选择器替代.active,避免 class 泄露业务逻辑
悬停/聚焦效果别只靠 :hover,补上 :focus-within 和键盘支持
纯 :hover 在触屏设备或键盘导航时完全失效。用户按 Tab 键切换焦点,如果菜单项没有视觉反馈,就卡在不可见的交互黑洞里。
常见错误是只写 a:hover { background: #eee },却没处理 a:focus,甚至禁用 outline 导致键盘用户彻底迷失。
实操建议:
- 始终保留
outline或用box-shadow模拟可访问的焦点环 - 用
li:focus-within > a触发父级高亮,适配键盘导航进入整个li区域的场景 - 避免用
transition过长(如 300ms+),影响快速连续操作的响应感
aria-current 的同步——这两点不花五分钟检查,上线后可能被无障碍审计直接标为阻断级问题。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











