nav和dropdown必须是两个独立block:nav仅负责导航结构(nav__list、nav__item等),dropdown专管下拉交互(触发、定位、显隐),二者职责分离、dom独立、禁止跨块命名如nav__dropdown或nav__item__submenu。

多层级导航菜单用 BEM,关键不是“怎么嵌套命名”,而是“哪些必须拆成独立 block”。把二级菜单硬塞进 nav__item 里写成 nav__item__submenu,90% 的 hover 闪退、状态错乱、样式污染都从这里开始。
nav 和 dropdown 必须是两个独立 block
导航结构(容器、列表、项、链接)归 nav 管;所有下拉行为(触发、面板定位、显隐过渡)必须由 dropdown 承担。二者职责不重叠,也不能靠前缀模拟父子关系。
-
nav下直接子元素只能是nav__list、nav__item、nav__link -
nav__item内部只放一个dropdown实例,不写任何dropdown__相关样式 - 错误写法:
nav__dropdown、nav__item__submenu、dropdown__menu__submenu - 正确结构:
<li class="nav__item">产品<div class="dropdown"></div> </li>
.dropdown__toggle:hover + .dropdown__menu 失效怎么办
这个选择器失效,几乎全是 HTML 结构问题,不是 CSS 写错了。BEM 要求 dropdown__toggle 和 dropdown__menu 在 DOM 中是相邻兄弟节点,或后者是前者的直接子/父节点。
- Vue 的
<transition></transition>、React 的<fragment></fragment>、手写的<div class="wrapper"> 都会插在中间,导致兄弟选择器断链 <li>临时绕过:用 <code>:has()(Chrome 105+、Safari 15.4+ 支持),但别当默认方案 - 加
min-width: max-content到dropdown__menu,防内容收缩后鼠标移出热区 - 加
transition-delay: 0.1s到opacity或transform,缓解视觉断连感 - 一级菜单:
<li class="nav__item">产品<div class="dropdown"></div> </li> - 二级菜单:在上层
dropdown__menu里再放一个<div class="dropdown"></div> - 差异靠 modifier 控制:
dropdown--mega、dropdown--inline合法;nav__dropdown--level-2违反职责分离 - 所有
dropdown共享基础样式,modifier 只表达视觉/行为差异,不绑定父级上下文 -
nav__list--mobile是布局级响应,应在页面加载时由matchMedia触发并添加 -
dropdown--expanded是用户操作态,生命周期短,不能和 --mobile 混用 - 所有依赖布局状态的样式,必须显式带上父级修饰符,例如
.nav__list--mobile .nav__item--active - 别在
nav.css里写媒体查询;响应式逻辑统一交给layout-responsive.css管理
多级嵌套时如何避免命名爆炸
看到 nav__item__submenu__link 就该停手——这不是层级深,是结构失焦。BEM 不允许跨 block 嵌套命名,所以不存在“三级”概念,只有多个 dropdown 实例依次嵌套。
响应式下 nav__list--mobile 和 dropdown 如何协同
小屏折叠后,nav__list 加了 nav__list--mobile 类,但里面的 dropdown 实例可能仍按桌面逻辑运行——这不是 bug,是状态没对齐。
真正难的不是写对类名,而是每次加新菜单项时,能立刻判断它该属于哪个 block、是否该拆出去——这种边界意识,比语法细节重要得多。











