横向导航必须用 display: flex;禁用 float 和 inline-block,因其在响应式下易塌陷、空隙难控;子菜单展开须用 :focus-within 而非 :hover;多列菜单改用 column-count;语义化必须用 。

横向导航必须用 display: flex,别碰 float 和 inline-block
横向排列失效、换行错位、小屏堆叠异常——八成是用了过时的 float: left 或 display: inline-block。这些方案在响应式场景下天然脆弱:父容器高度塌陷、项间空隙难控制、flex-wrap 折行逻辑混乱。
现代写法只有一条路:nav ul 设 display: flex,子项 li 默认撑满主轴,用 gap 控制间距。注意三点:
-
justify-content: space-around比space-between更适合文字长度不均的菜单项,两端留白更自然 - 禁用
white-space: nowrap——它会让小屏强制横向滚动,改用flex-wrap: wrap+ 媒体查询自动折行 - IE11 不支持
gap,如需兼容,得退回到margin-right并手动清除最后一项的右距
:focus-within 是多级联动的核心,:hover 只能当备选
纯 :hover 在触屏设备(iPad、Surface、安卓浏览器)上完全不触发,键盘用户按 Tab 进入菜单后也无法展开子项。这不是 bug,是规范行为。
真正可靠的展开机制靠 :focus-within,前提是结构干净:
立即学习“前端免费学习笔记(深入)”;
- 子菜单
ul必须是父li的**直接子元素**,不能隔div或span - 初始状态必须用
visibility: hidden; transform: scaleY(0)隐藏,不能用display: none——否则:focus-within失效 - 触发规则写成:
li:focus-within > ul { visibility: visible; transform: scaleY(1); } - 旧版 Android WebView(Android 9 及以下)不支持
:focus-within,需 JS 降级监听click+focus
三级及以上菜单必须用 column-count,别硬塞 flex-wrap
当二级菜单有 15+ 项、带图标或两行文字时,用 flex-wrap: wrap 会水平铺满再换行,导致列高严重不均、底部大片留白——这不是你 CSS 写错了,是 Flexbox 的设计逻辑如此。
正确做法是放弃 Flexbox,改用“报纸式”垂直流布局:
- 给子菜单
ul设column-count: 2(或 3),配合column-gap: 1rem - 每个子项
li加break-inside: avoid,防止文字被截断到不同列 - 用
float: left配合width: 100%确保列内项垂直堆叠,而非横向挤占 - 避免用
grid-template-columns: repeat(2, 1fr)——Grid 在内容高度差异大时同样列高不齐
nav 不是装饰标签,语义错误会导致焦点链断裂
写成 <div class="nav"> 看起来一样,但键盘用户 Tab 到这里会跳过整个区域,读屏器可能直接忽略,SEO 权重也会稀释。
<p>关键约束只有三条:</p>
<ul>
<li>必须用 <code><nav role="navigation"></nav> 包裹,role 属性不可省——部分旧读屏器依赖它识别导航意图
nav,它们该用 <search></search> 或 <form></form> 单独包裹class="active" 硬编码,得用 JS 动态匹配 location.pathname,注意处理结尾斜杠和哈希路由(如 /about/ vs /about)最易被忽略的是:移动端 Safari 对 :focus-within 支持良好(iOS 15.4+),但很多团队测试只跑 Chrome,结果上线后 iPad 用户点不开二级菜单——得真机测。



















