一、核心认知
BEM 是一套命名约定,不是语法,全称:
- B Block 块:独立、可复用的组件模块
- E Element 元素:块内部从属节点,不能脱离块单独使用
- M Modifier 修饰符:描述块 / 元素的状态、变体、外观差异
目标: 消除样式命名歧义、降低选择器权重、避免深层嵌套、方便团队协作维护。
分隔符约定(业界主流)
- 块 __ 元素:双下划线
__ - 块 / 元素 -- 修饰符:双短横线
-- - 多单词使用单短横线
-连接
✅ 格式模板
block
block__element
block--modifier
block__element--modifier
二、逐部分拆解
1. Block|块
一个独立功能组件,自身有完整语义,可以单独复用。
命名:描述功能,不要描述样式
/* ✅ 好:语义化 */
.card
button-group
nav-header
/* ❌ 差:描述外观 */
.red-box
big-title
块内部可以包含多个元素。
2. Element|元素
属于某个块的子节点,不能脱离父块单独使用 。 用 __ 和块连接。
.card__title
.card__content
.card__footer
❌ 禁止链式多层元素:
card__content__text元素只允许一层从属,深层结构继续当作元素,不要无限嵌套。
3. Modifier|修饰符
用来标记状态、样式变体、尺寸。-- 连接块或者元素。
/* 块的修饰符 */
.card--dark
.card--borderless
/* 元素的修饰符 */
.card__title--primary
.card__title--small
修饰符不单独存在,必须依附在块 / 元素上。 修饰符只追加差异化样式,不要重写全部样式。
三、标准示例
HTML
<div class="card card--dark">
<h3 class="card__title card__title--primary">标题</h3>
<div class="card__desc">描述文本</div>
</div>
CSS
/* 基础块 */
.card {
padding: 16px;
border-radius: 8px;
}
/* 块修饰符 */
.card--dark {
background: #1d2129;
color: #fff;
}
/* 块内元素 */
.card__title {
font-size: 18px;
margin-bottom: 8px;
}
/* 元素修饰符 */
.card__title--primary {
color: #409eff;
}
.card__desc {
font-size: 14px;
color: #666;
}
四、搭配预处理器(Less / SCSS)写法
.card {
padding: 16px;
&--dark {
background: #1d2129;
}
&__title {
font-size: 18px;
&--primary {
color: #409eff;
}
}
}
注意:嵌套只是语法糖,编译出来依然是标准 BEM 类名,不会生成高权重后代选择器。
五、关键规则 & 避坑
-
不要用层级选择器 ❌
.card .title {}容易权重混乱,优先用 BEM 类名 -
元素不能跨块
nav__card-title错误,card 是独立块,应该card__title -
修饰符只做增量样式 不要把基础样式全部写在修饰符内
-
命名看语义,不看 DOM 结构 DOM 嵌套很深,类名依然保持一层
block__element -
通用工具类,不属于 BEM 间距、文字对齐这类工具类单独抽离,不和 BEM 混在一起
mt-12是工具类,不属于 BEM 体系。六、适用场景与边界
✅ 适合:中大型后台项目、组件库、团队协作项目、长期维护页面 ❌ 没必要:简单 Demo、一次性活动页、页面极其简单的场景
BEM 不是强制标准,是权衡方案: 小项目写起来略长,但换来极高可维护性。
七、和其他命名方案对比
- 原生随意命名:简短,极易冲突、难维护
- BEM:类名长,语义清晰,权重扁平,适合团队
- CSS Module:编译哈希,解决冲突,但可读性差,不方便直接覆盖调试
八、工程落地规范
- 统一分隔符,项目内不要混用多种 BEM 变体
- 多单词使用短横线:
search-form__input-wrap - 状态优先用修饰符,少用
is-xxx状态类(团队可统一约定) - 公共通用组件,块名全局唯一,避免跨页面重名冲突