遮罩层不应作为独立Block,而应作为弹窗Block的Element(如modal__overlay),通过修饰符(如--visible)控制状态与动画,状态类(如modal--open)须挂载在Block级以确保语义准确、复用安全和层级可控。

全屏遮罩层(.overlay)本身不是独立功能块,直接套用 overlay__backdrop 或 overlay--visible 容易导致语义错位、状态失控和动画耦合——真正该建模的是「弹窗容器」这个 Block,遮罩只是它的一个视觉组成部分。
为什么不能把遮罩单独定义为 block
常见错误是写 .overlay {} + .overlay__backdrop {} + .overlay__content {},结果发现:遮罩常需跟随弹窗开关、z-index、过渡时机同步变化,但又在多个弹窗间复用;一旦 .overlay 被多个组件(如 modal、drawer、confirm)共用,它的 --open 修饰符就无法精准控制单个实例的状态。
更实际的问题是:CSS 动画触发依赖类名增删,而 .overlay--open 若全局生效,会导致所有遮罩同时淡入——这违背“每个弹窗独立控制”的基本需求。
所以遮罩不该是顶层 Block,而应作为弹窗 Block 的一个 Element:
-
modal__overlay表示“属于 modal 的遮罩”,天然绑定作用域 -
confirm__overlay和modal__overlay可共享同一套 CSS 规则(通过@apply或extend),但不共享类名或状态逻辑 - DOM 结构中,
modal__overlay必须是modal的直接子元素,否则 BEM 关系断裂
如何给遮罩加过渡动画且不污染其他元素
关键不在 class 名,而在 CSS 声明位置和选择器权重。BEM 要求只用 class 选择器,禁用标签或后代选择器,所以不能写 .modal .modal__overlay ——这既违反规则,又让动画样式难以复用。
正确做法是把过渡规则写在 Element 级,用修饰符开关:
.modal__overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.5);
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0.2s step-end;
}
<p>.modal__overlay--visible {
opacity: 1;
visibility: visible;
}</p>
这样动画完全由 modal__overlay--visible 控制,且可被任意 Block 复制使用(比如 drawer__overlay--visible 只需复用相同声明)。
容易踩的坑:
- 把
transition写在--visible修饰符里 → 动画只在显示时触发,隐藏无过渡 - 用
display: none配合修饰符 →display不可过渡,必须用visibility+opacity组合 - 给
modal__overlay加z-index却没设position→ 遮罩不覆盖内容
弹窗打开/关闭状态该挂在哪一级
状态修饰符必须落在能真实反映“可交互切换”行为的节点上。不是 modal__overlay--open,而是 modal--open。
原因很直接:用户操作的是弹窗整体(点击按钮 → 弹窗出现),遮罩只是副产品;JS 控制状态时,也只会增删 modal--open 这个类,再由它联动子元素:
<div class="modal modal--open"> <div class="modal__overlay modal__overlay--visible"></div> <div class="modal__content">...</div> </div>
此时 CSS 可以安全地写:
.modal--open .modal__overlay {
/* 仅当 modal--open 存在时才生效,但注意:这不是 BEM 推荐写法 */
}
但更推荐解耦:JS 同时操作两级类名,或用 CSS-in-JS 自动同步(如 emotion 的 css + cx),避免依赖 DOM 层级。
典型误用:
-
modal__overlay--open→ 遮罩没有“打开”动作,只有“可见”状态;--open是动词,--visible是形容词,语义不符 -
modal--show→--show和--visible混用,团队内不统一,grep 查不到全部显示逻辑 - 在
body上加has-modal-open类来控制滚动锁定 → 违反 BEM “不依赖外部上下文”原则,且难维护
多个弹窗嵌套时如何避免 z-index 冲突
BEM 不管 z-index,但它强制你把层级决策显式写进命名里。例如:
-
modal--priority-high对应z-index: 2100 -
confirm--priority-medium对应z-index: 2050 -
tooltip--priority-low对应z-index: 1000
所有修饰符都作用于 Block 本身,而非遮罩或内容。这样 JS 切换弹窗时,只需改一个 class,z-index、动画、遮罩可见性全部联动。
真正容易被忽略的是:遮罩的 z-index 必须比其所属 Block 低 1 级(比如 modal 是 2100,modal__overlay 就设 2099),否则内容可能被自己遮住。
如果你用 CSS 变量管理 z-index,建议按 Block 类型分组:
:root {
--z-modal: 2100;
--z-modal-overlay: calc(var(--z-modal) - 1);
--z-confirm: 2050;
--z-confirm-overlay: calc(var(--z-confirm) - 1);
}
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











