z-index必须绑定到bem block根元素,不能写在element类上;block需显式设position+z-index创建层叠上下文,子元素通过var(--z-modal)或calc()继承或微调,禁用硬编码与inherit。

z-index 必须绑定到 BEM Block 根元素,不能写在 Element 类上
写了 z-index 却没效果,八成是因为你把它塞进了 .modal__overlay 或 .dropdown__menu 这类 Element 类里。BEM 的 Element 类只描述结构角色(“这是遮罩”“这是菜单项”),不承担全局层级决策责任。浏览器会直接忽略它——尤其当父 Block 没创建层叠上下文时,子元素的 z-index 根本不参与任何比较。
正确做法是:.modal、.dropdown、.tooltip 这些 Block 类必须自己设 position + z-index,成为独立上下文起点:
.modal { position: fixed; z-index: var(--z-modal); }.dropdown { position: relative; z-index: var(--z-dropdown); }- 所有子元素(如
.modal__content)默认继承父上下文,无需额外z-index,除非需内部微调(用calc(var(--z-modal) - 1))
Block 必须显式创建层叠上下文,不能依赖父容器或隐式触发
z-index 在 position: static 元素上永远无效——而 BEM Block 默认就是 static。开发者工具里该属性变灰、Computed 面板显示 auto,就是最直接的信号。
常见错误包括:
- 只给
.modal__overlay加z-index,却忘了给.modal加position: relative或fixed - 父容器用了
transform: translateY(0)、opacity: 0.99或will-change: transform,意外创建新上下文,把整个 Block 锁死在局部层级里 - 用
position: sticky但父容器没设position: relative,导致粘性失效且z-index不生效
解决方式很直接:每个需要“浮出”的 Block 主动声明上下文,不等、不靠、不猜。
用 CSS 自定义属性统一管理语义化层级值,禁止硬编码数字
z-index: 9999 是反模式。它不可维护、无法推演、一改全崩。BEM 不是命名游戏,而是边界约束——z-index 的值源必须唯一、可追溯、有语义。
推荐结构:
- 在
:root或预处理器变量文件中定义:--z-modal: 1000、--z-dropdown: 900、--z-tooltip: 800 - Block 类名绑定层级意图:
.modal { z-index: var(--z-modal); } - 子元素只做相对偏移:
.modal__backdrop { z-index: calc(var(--z-modal) - 1); } - 禁用
z-index: inherit或z-index: unset——这等于放弃控制权
多个同类型弹窗共存时,修饰符必须作用于 Block 根节点,且不可复用
把 modal--visible 加在 .modal__overlay 上,会导致遮罩显示但内容不动,或 JS 状态与 DOM 脱节。更危险的是,如果所有弹窗都共享同一个修饰符类名,关闭一个可能误删全部。
关键约束:
-
modal--visible必须加在<div class="modal modal--visible"> 这样的根节点上<li>CSS 联动写法:<code>.modal--visible .modal__overlay控制遮罩,.modal--visible .modal__content控制动画 - 多个弹窗实例应使用不同 Block 名(如
.confirm-modal、.delete-modal)或唯一 ID 挂载点,避免修饰符冲突
最易被忽略的不是数值大小,而是上下文边界的归属——BEM 让你能快速定位是哪个 Block 没设 position,或哪个父容器偷偷加了 transform。问题不在命名,而在结构前提是否成立。











