bem是block-element-modifier三段式命名规范,通过.block__element--modifier结构实现语义化、扁平化、可复用的css模块化设计,解决样式冲突与协作混乱问题。

开了 CSS Modules 也不代表命名就清晰了——.title 在 Card.module.css 和 Header.module.css 里都存在,编译后不冲突,但谁也不知道哪个 title 对应哪块 UI。BEM 不是“让 CSS 看起来更规范”的装饰,而是给类名注入可读、可定位、可协作的语义骨架。
为什么 CSS Modules + BEM 才算真正模块化
CSS Modules 解决的是「机器层面」的作用域隔离(哈希类名防冲突),BEM 解决的是「人层面」的语义组织(命名即文档)。两者缺一不可:
-
.button经 CSS Modules 编译成_button_abc123,机器认得,人看不懂;加上 BEM 后写成.button__icon,开发时保留语义,编译后仍是局部作用域 - 没有 BEM 的 CSS Modules,组件目录里全是
index.module.scss,文件名、类名、DOM 结构三者之间没映射关系,新人打开文件第一反应是“这到底渲染啥?” - DevTools 里看到
card__title_7f8a比看到_title_9b2c更快定位到源码位置和上下文
怎么在 .module.css 文件里写合规的 BEM 类名
直接写,别绕弯。BEM 是命名约定,不是语法限制,和是否启用模块化完全正交:
- ✅ 正确:
.card { }、.card__header { }、.card--fluid { } - ❌ 错误:
.header { }(脱离 block,无上下文)、.card .title { }(类型选择器,破坏封装) - 修饰符(
--)必须平级定义,不要嵌套:Sass 中禁止写.card { &--fluid { } },应单独写.card--fluid { } - Element 名必须是 Block 的直接子语义,
.card__footer__copyright违规;应拆成.card__footer+.copyright(新 block)
React 组件里怎么安全组合 BEM 类名
关键不是拼字符串,而是让结构意图在 JSX 中显性可维护:
- 用
clsx或classnames动态组合:className={clsx(styles.card, styles['card--fluid'], hasHeader && styles['card__header'])} - 禁止硬编码哈希后的类名(如
className="_card_abc123"),等于放弃 CSS Modules 的可维护性 - 如果用了 Tailwind,BEM 类名只用于结构标识(如
user-card),视觉细节交给tw`p-4 bg-white rounded-lg`,二者职责分明,不混写逻辑 - 组件根元素必须带 Block 名:
<div classname="{styles['user-card']}">,不能只传 <code>styles['user-card__header']BEM 最容易被忽略的实操约束
它不是写几个
__和--就完事,真正的门槛在边界意识:- Block 必须是独立、可复用的单元,
.left-sidebar这种含位置描述的命名违规;应为.sidebar,布局由父容器控制 - Modifier 描述的是状态或变体,不是样式值:
.button--blue错,.button--primary对;颜色应由设计系统变量或主题层统一管理 - 所有样式必须绑定到明确 Block,禁止无前缀通用类(如
.clearfix应写作.card__content--clearfix或抽成工具类并加u-前缀) - 嵌套超两级(如
form__field__input__label)说明结构设计错了,该拆 Block 而不是缩写类名
复杂点不在语法,而在团队对“每个类名必须能回答三个问题”:它属于哪个 Block?它在这个 Block 里扮演什么角色?它当前处于哪种状态?一旦这个契约松动,再严格的构建工具也救不了语义断层。
- Block 必须是独立、可复用的单元,











