遮罩层必须是 dialog__overlay,不可拆分为独立 Block;dialog__content 仅负责容器职责,业务元素需用独立 Element 命名;dialog--open 必须加在根节点;transition 必须写在基础类中。

遮罩层必须是 dialog__overlay,不能单独声明为 Block
遮罩不是独立功能单元,它和弹窗内容共享显隐状态、点击关闭逻辑、动画节奏。一旦拆成独立 Block(比如 overlay 或 dialog-overlay),JS 就得同时维护两个类名,稍有延迟就会出现遮罩已显示但内容没动、或遮罩已消失但内容还卡在半途的断裂体验。
常见错误包括:
-
dialog-overlay(缺双下划线,变成普通类,丢失层级归属) -
overlay(脱离dialog上下文,全局冲突风险高) -
dialog__content__overlay(BEM 禁止三层嵌套,语义错乱)
正确结构只有一层平级:<div class="dialog"><div class="dialog__overlay"></div><div class="dialog__content"></div></div>
dialog__content 只负责容器职责,不承载业务样式
dialog__content 的作用是包裹、定位、留白,不是“放所有内容的地方”。真实业务元素(标题、表单、按钮)必须用独立 Element 命名,比如 dialog__title、dialog__form、dialog__footer。
立即学习“前端免费学习笔记(深入)”;
容易踩的坑:
- 用
dialog__body——body是 HTML 元素名,语义污染,且和dialog__content职责重叠 - 写成
dialog__content__header—— BEM 不允许三层,应拆为dialog__header - 把滚动逻辑硬塞进
dialog__content—— 应用 Modifier:dialog__content--scrollable,而非新建块
dialog--open 必须加在根节点,不能挂在子元素上
显隐状态属于整个组件,不是某个子元素的局部表现。React 场景下,setIsOpen(true) 只需给最外层 <div class="dialog"> 加 dialog--open,其余靠 CSS 选择器联动:
.dialog--open .dialog__overlay { opacity: 1; visibility: visible; }
.dialog--open .dialog__content { transform: translateY(0); }
若加在 dialog__overlay 上 → 遮罩显示,但内容没动,视觉断裂
若用 dialog__overlay--visible 单独控制 → 状态粒度太细,违背 Modifier 只修饰自身变体的原则
transition 必须写在基础类里,不是 --visible 里
这是最容易被忽略的动画陷阱:把 transition 写在 dialog__overlay--visible 中,会导致隐藏时直接跳变——因为移除该类后,过渡属性就没了。
正确写法是:
.dialog__overlay {
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0.2s step-end;
}
.dialog__overlay--visible {
opacity: 1;
visibility: visible;
}
Modifier 只管值,不负责过渡定义;所有动画节奏必须由基础类统一锚定,否则状态切换不同步、难以复用、调试成本陡增


















