动画类名必须是--modifier,不能用is-或独立状态类,否则CSS不匹配、工具链识别失败;--modifier必须绑定Block或Element,禁止嵌套,需与transition/animation规则严格对齐。

动画类名必须是 --modifier,不能是 is- 或独立状态类
直接加 is-open 或 animating 类,CSS 规则不会匹配,工具链(如 Stylelint、PurgeCSS)也识别不出这是 BEM 修饰符,导致样式漏编译、IDE 跳转失效、CI 检查报错。BEM 只认 -- 开头的修饰符,且必须绑定到具体 Block 或 Element。
常见错误现象:
-
modal is-open写成两个 class,但 CSS 只写了.modal--open,样式完全不生效 -
.card-is-loading缺双横线,被stylelint-selector-bem-pattern当作非法命名拦截 - JS 动态拼出
card__content--loading却漏了card块级类,DOM 中只有元素类,语义断裂
正确做法是让修饰符和组件强绑定:.card--loading 表示「这张卡片正处于加载中」,所有动画逻辑(骨架屏、旋转图标、禁用交互)都围绕这个语义展开;.card__content--loading 则只控制内容区域的占位表现。
--modifier 必须与 animation 或 transition 声明严格对齐
只加类名不写对应动画规则,等于没写。浏览器不会继承父级的 transition,也不会自动补全 animation 属性。常见错误是基础类 .drawer 没定义 transition: max-height 0.3s ease,只在 .drawer--expanded 里写了 max-height: 500px——结果展开有动画,收起直接跳变。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- 用
transition(如 hover 缩放、高度展开):基础类必须先声明transition,修饰符只改触发属性值 - 用
animation(如 loading 循环、入场序列):修饰符里必须完整写animation: spin 0.4s linear infinite,不能只写animation-name - 禁用
* { transition: all .2s }:它会让margin、color等非预期属性也过渡,且阻断will-change优化
JS 控制类名增删时,必须校验节点是否仍归属原 Block
动态插入的节点如果没带 Block 类,--modifier 就失去上下文。比如用 document.createElement('div') 创建新 card__image,却忘了加 card 类,那 .card__image--loaded 的样式永远不会生效——选择器要求祖先必须有 .card。
容易踩的坑:
- 用
innerHTML插入片段时,只写了class="card__image--loaded",漏了card - 从模板克隆节点后,没调用
el.classList.add('card')补全 Block 类 - 第三方库(如 Alpine.js)动态渲染时,未在根元素上显式声明 Block 类
推荐做法:封装创建函数,强制 Block 类为必填项,例如:
function createCardImage(src) {<br> const el = document.createElement('img');<br> el.className = 'card__image card__image--loading';<br> el.src = src;<br> return el;<br>}
多个动画状态共存,必须平级添加、禁止嵌套修饰符
BEM 不允许 .modal--fade-in--scale 或 .card--loading--disabled 这种链式写法。它们不是父子关系,而是并列布尔开关,必须平级添加、独立控制。否则工具链无法提取、PurgeCSS 容易误删、语义也变得模糊。
正确方式是写:class="modal modal--fade-in modal--scale modal--loading",然后分别定义:
.modal--fade-in .modal__overlay { animation: modal-fade-in 0.25s ease; }<br>.modal--scale .modal__content { animation: modal-scale-in 0.25s ease; }<br>.modal--loading .modal__spinner { animation: spinner-spin 0.6s linear infinite; }关键点:
- 每个修饰符只负责一个可感知的状态,不耦合其他逻辑
- JS 切换时用
classList.toggle()独立操作,不拼接字符串 - 若需防止竞态(如快速连续点击),用
el.getAnimations().forEach(a => a.cancel())主动中断旧动画
最易被忽略的是:动态节点一旦脱离原始 Block 上下文,--modifier 就失效——它不是通用工具类,没有 Block,修饰符就只是个无效字符串。


















