纯CSS导航菜单需语义化HTML(用)、flex布局、:hover实现下拉、后端或路由动态添加.active类;移动端需JS处理点击,CSS无法自动识别当前页。

纯 CSS 实现的导航菜单,不需要 JavaScript 就能完成基础交互(如悬停展开、当前页高亮),关键在 HTML 结构语义化 + :hover 和 .active 类的合理使用。
HTML 必须用 包裹
,别用 模拟
语义错误会导致可访问性问题,屏幕阅读器无法识别导航区域。浏览器默认对 有角色标注,
天然表达列表关系。
-
✅
-
❌(失去语义、键盘焦点混乱、SEO 不友好)
display: flex 是横向菜单最稳的布局方式,慎用 float
float 已过时,清除浮动易出错,响应式下难控制;flex 天然支持等宽/居中/自动换行,且兼容到 IE10+(加 -ms- 前缀可覆盖大部分旧项目)。
nav ul {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
nav li {
margin-right: 1rem;
}
nav a {
text-decoration: none;
padding: 0.5rem 1rem;
}
- 要让菜单项垂直居中:给
nav ul 加 align-items: center
- 移动端需折行:改用
flex-wrap: wrap,再配合媒体查询限制最大宽度
- IE10/11 中
flex 子项默认不收缩,需显式设 flex-shrink: 0
下拉菜单靠 :hover + 子
定位,不用 JS 也能生效
二级菜单本质是嵌套
,初始隐藏,父 悬停时显示。关键是定位方式和过渡控制。
- 子
必须放在对应 内部,否则 :hover 断开后菜单闪退
- 用
position: absolute + top: 100% 对齐父项底部,避免遮挡或偏移
- 加
opacity: 0; visibility: hidden; 配合 transition 实现淡入,比 display: none 更平滑(后者无法过渡)
- 移动端点击触发需额外处理——
:hover 在触摸设备上不可靠,得靠 JS 切换 .show 类
.active 类必须由后端或路由系统动态写入,CSS 无法自动判断当前页
CSS 没有“当前 URL 匹配”能力,:target 只适用于锚点,:is(:local-link) 兼容性极差(仅 Safari 15.4+)。所有「高亮当前菜单项」逻辑必须由运行时决定。
- 服务端渲染(如 PHP/Node.js):输出 HTML 前比对
request.url,给对应 加 class="active"
-
前端路由(如 React Router):用
useMatch() 或 NavLink 组件自动添加 active 类
- 纯静态页?只能手动维护——每个页面单独写死一个
.active,没捷径
最常被忽略的是触屏设备下的下拉交互逻辑,以及 .active 类的生成时机。这两个点一旦漏掉,菜单在手机上点不开、当前页标不中,用户第一印象就坏了。
语义错误会导致可访问性问题,屏幕阅读器无法识别导航区域。浏览器默认对 有角色标注, 天然表达列表关系。
-
✅ -
❌(失去语义、键盘焦点混乱、SEO 不友好)
display: flex 是横向菜单最稳的布局方式,慎用 float
float 已过时,清除浮动易出错,响应式下难控制;flex 天然支持等宽/居中/自动换行,且兼容到 IE10+(加 -ms- 前缀可覆盖大部分旧项目)。
nav ul {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
nav li {
margin-right: 1rem;
}
nav a {
text-decoration: none;
padding: 0.5rem 1rem;
}
- 要让菜单项垂直居中:给
nav ul加align-items: center - 移动端需折行:改用
flex-wrap: wrap,再配合媒体查询限制最大宽度 - IE10/11 中
flex子项默认不收缩,需显式设flex-shrink: 0
下拉菜单靠 :hover + 子
定位,不用 JS 也能生效
二级菜单本质是嵌套 ,初始隐藏,父 悬停时显示。关键是定位方式和过渡控制。
- 子
必须放在对应内部,否则:hover断开后菜单闪退 - 用
position: absolute+top: 100%对齐父项底部,避免遮挡或偏移 - 加
opacity: 0; visibility: hidden;配合transition实现淡入,比display: none更平滑(后者无法过渡) - 移动端点击触发需额外处理——
:hover在触摸设备上不可靠,得靠 JS 切换.show类
.active 类必须由后端或路由系统动态写入,CSS 无法自动判断当前页
CSS 没有“当前 URL 匹配”能力,:target 只适用于锚点,:is(:local-link) 兼容性极差(仅 Safari 15.4+)。所有「高亮当前菜单项」逻辑必须由运行时决定。
- 服务端渲染(如 PHP/Node.js):输出 HTML 前比对
request.url,给对应加class="active" -
前端路由(如 React Router):用
useMatch()或NavLink组件自动添加active类 - 纯静态页?只能手动维护——每个页面单独写死一个
.active,没捷径
最常被忽略的是触屏设备下的下拉交互逻辑,以及 .active 类的生成时机。这两个点一旦漏掉,菜单在手机上点不开、当前页标不中,用户第一印象就坏了。










