自定义属性必须声明在基础块类(如.btn)上,修饰符(如.btn--primary)仅覆盖变量值;若仅在修饰符中声明变量,因BEM要求修饰符须与基础类共存,变量将不生效。

自定义属性必须绑定到基础块类,不能只写修饰符规则
直接给 .btn--primary 单独设 --bg-color 变量,等于白写。浏览器匹配样式时,.btn--primary 规则本身不生效(BEM 要求修饰符必须和基础类共存),变量自然不会被读取。正确做法是把变量声明在基础块上,再用修饰符覆盖其值。
常见错误现象:button 点击后背景没变,检查发现 CSS 里只有 .btn--primary { --bg-color: blue; },但 HTML 是 <button class="btn btn--primary"></button>——变量没挂载到 .btn 上,压根没机会参与计算。
.btn { --bg-color: var(--color-default); background-color: var(--bg-color); }.btn--primary { --bg-color: var(--color-primary); }.btn--disabled { --bg-color: var(--color-disabled); }
修饰符覆盖变量时,必须确保变量名一致且大小写敏感
BEM 修饰符改的是变量的“值”,不是变量的“存在性”。如果基础类里用 --bg-color,修饰符却写 --Bg-Color 或 --background-color,CSS 会当成两个不同变量,后者不会覆盖前者。
使用场景:设计系统中统一维护一套 CSS 变量,比如 :root { --color-success: #27ae60; --color-warning: #f39c12; },所有修饰符都复用这些变量名,避免重复定义。
立即学习“前端免费学习笔记(深入)”;
容易踩的坑:.card--featured { --card-bg: var(--color-featured); } 和 .card { background-color: var(--card-bg); } 看似合理,但一旦其他组件也用了 --card-bg,就会互相污染。应统一前缀,如 --card-bg-color,并严格保持大小写。
动态切换修饰符时,变量更新是即时的,但动画需额外声明 transition
加/删 .btn--hovered 类后,background-color 会立刻取新变量值,但如果没有显式声明 transition,就不会有过渡效果。BEM 修饰符本身不带动效逻辑,变量也不自动触发动画。
参数差异:基础类负责声明可动画的属性和缓动,修饰符只负责提供目标值。例如:
.btn { transition: background-color 0.2s ease, transform 0.15s ease; }.btn--hovered { --bg-color: var(--color-hover); transform: scale(1.02); }
性能影响:变量本身无运行时开销,但若在高频交互中(如滚动监听)频繁切换修饰符,可能触发重排重绘。建议用 will-change: background-color 配合硬件加速,且只对真正需要动画的属性启用。
JS 动态设置变量值时,别绕过修饰符直接操作 style
有人为“更灵活”直接 el.style.setProperty('--bg-color', '#ff6b6b'),这破坏了 BEM 的语义约束:你无法从 HTML 看出这个按钮当前属于哪种设计意图(primary?danger?),后续维护、工具链校验、样式复用全失效。
正确路径始终是:JS 控制修饰符开关 → CSS 用变量承接 → 基础类引用变量。这样即使某天要全局调整 --color-danger,所有 .btn--danger 都自动响应,不用翻 JS 代码。
最易被忽略的是变量作用域链:如果 .btn 在 shadow DOM 里,:root 定义的变量默认不可见,得在 shadow root 内部显式 set,或用 inherit 向下透传——这点和普通 class 行为完全不同。


















