动画修饰符必须以--开头并绑定到块级元素,如.modal--fading-out;错误命名如.modal-fade-out或.is-fading会导致工具链无法识别、选择器失效;修饰符需挂载在承载动画的元素上,显式声明animation或transition,严格匹配@keyframes名称与生命周期事件处理。

动画修饰符必须用--开头且绑定到块级元素
直接写.fade-in或.modal-fade-out会导致工具链(Stylelint、PostCSS 插件)无法识别为状态类,IDE 类名跳转失效,CSS 选择器也容易被覆盖。BEM 要求动画类必须是块的修饰符,语义上表达“谁在动、处于什么状态”,而不是“怎么动”。
正确命名只有一种形式:.modal--fading-out、.carousel--sliding、.card--fade-in。它们都满足:以--开头 + 绑定到具体 Block + 名称可预测(如--fade-in对应@keyframes fade-in)。
-
.modal-fade-out❌ 被解析为新 Block,脱离modal上下文 -
.is-fading❌ 不是 BEM 修饰符,工具不识别,开发者难定位来源 -
.modal__content--fading❌ 元素级修饰符不能承载整块动画逻辑(如 transform 在父级 track 上)
修饰符必须显式声明animation或transition
只加class="button--loading-spin"但 CSS 里没写animation属性,动画永远不会触发。浏览器不会自动补全,也不会继承父级的transition: all。
常见错误是把过渡逻辑全塞进基础类,结果修饰符只改了transform却没配transition,hover 或点击时直接跳变。
立即学习“前端免费学习笔记(深入)”;
- 用
transition(如缩放、颜色变化):基础类.button必须先声明transition: transform 0.2s ease,修饰符只负责改值(如transform: scale(1.05)) - 用
animation(如 loading 循环、入场序列):修饰符规则里必须完整写明animation: loading-spin 0.4s linear infinite - 禁用
* { transition: all .2s }:它会让margin、color等非预期属性也过渡,且阻断will-change优化
多个动画状态要平级组合,禁止嵌套修饰符
BEM 不允许--loading--disabled或--fade-in--scale这种链式修饰符。它们不是父子关系,而是并列布尔开关,应作为独立类平级添加。
JS 控制时用el.classList.toggle('modal--fade-in')和el.classList.toggle('modal--scale')分别操作,CSS 中各自定义效果。否则语义混乱、调试困难,且工具链校验失败。
- ✅ 正确:
class="modal modal--fade-in modal--scale" - ❌ 错误:
class="modal modal--fade-in--scale"(语法非法,解析器报错) - ❌ 错误:
class="modal--fade-in modal__content--scale"(跨层级,语义断裂)
JS 添加/移除动画类必须匹配生命周期事件
类名加完立刻移除,动画根本播不完;监听animationend却不检查event.animationName,可能误删其他动画的类。关键不是“怎么加”,而是“什么时候加、什么时候收”。
入场和退出动画逻辑不同,不能靠一个类来回切换。退出动画必须单独定义(如.card--fade-out),不能复用--fade-in反向播放——浏览器不保证 reverse 行为可预测。
- 入场:用
el.classList.add('card--fade-in')→ 等animationend→ 检查event.animationName === 'fade-in'→ 清理 - 退出:先
add('card--fade-out'),再remove('card--fade-in'),确保 CSS 能触发退出过渡 - 竞态处理:同一元素频繁切换时,先调用
el.getAnimations().forEach(a => a.cancel())中断旧动画
动画类名一旦脱离组件绑定(比如把.card--fade-in挪到.modal上),所有配套的 CSS 声明、JS 事件过滤、硬件加速条件(是否用了transform)都会失效——这不是命名格式问题,而是整个状态系统崩塌的起点。


















