BEM命名规范是Block(块)、Element(元素)、Modifier(修饰符)的缩写,由Yandex提出,通过.block__element--modifier结构实现语义化、扁平化、可复用的CSS命名。

块嵌套:允许跨组件复用,不改变语义层级
块嵌套是指一个 Block 出现在另一个 Block 的 HTML 内部,但两者仍是平级、独立的组件。比如 header 里放了 logo 和 search-form,这三个都是各自独立的块,彼此不构成父子样式依赖关系。
这种嵌套只影响 DOM 结构和布局流,CSS 上它们各自维护自己的文件和作用域:logo.css 不该写 .header .logo,而应直接用 .logo;同理 search-form 样式也不该假设自己一定在 header 里。
- 块嵌套时,子块类名仍保持完整、无前缀,如
search-form,不是header__search-form - 块可以被复用到任意上下文(
footer或弹窗里也能放search-form),所以不能靠父块名做样式锚点 - 如果某个“子块”永远只出现在特定父块中,且无法独立存在,那它大概率不该是块,而是父块的元素
元素嵌套:必须绑定块名,禁止跨层或越级
元素是块的组成部分,语法上必须用 __ 连接块名,比如 card__title、menu__item。它不能脱离所属块单独使用,也不能出现在其他块内部——哪怕 DOM 上它物理嵌套在另一个块里。
常见错误是看到 HTML 嵌套深就盲目加层级:card__header__title 是非法的,因为 header 在 BEM 中若作为 card 的一部分,它本身不是独立块,而是 card__header 元素;其子内容应是 card__header-title(作为修饰符)或直接 card__title(提升为同级元素)。
-
card__title可以出现在<div class="card"><h2 class="card__title">...</h2></div>,但不能只写title或card-title - 不允许
card__header__title或card__content__text—— 元素不能嵌套元素 - 如果需要视觉分组,优先考虑新增一个元素(如
card__body),而不是在已有元素下再建“子元素”
怎么判断一个 DOM 节点该是块还是元素?看复用性和语义边界
关键不是它在 HTML 里嵌得多深,而是它有没有独立封装能力。一个节点如果满足以下任一条件,就该是块:
- 它在多个页面/模块中重复出现,且样式和行为一致(如
button、input-group) - 它有自己的状态逻辑(比如
tabs块管理激活态,modal控制显隐) - 它能脱离当前上下文单独测试或 Storybook 预览
反之,如果它只是某个容器的固定组成部分,换地方就失效,比如 card 里的 avatar,那就该是 card__avatar,不是独立块 avatar(除非项目里真有通用头像组件)。
CSS 选择器里别用嵌套模拟 BEM 关系
有人写 .card .card__title 或 .menu .menu__item,以为这样更“安全”,其实违背了 BEM 设计初衷。BEM 类名本身已表明归属,不需要靠祖先选择器限定作用域。
- 这类嵌套选择器会提高 CSS 特异性(specificity),后续覆盖困难
- 一旦块被动态插入到非预期位置(比如通过 Portal 渲染到
body底下),样式就断了 - 真正该做的,是确保每个块的样式只依赖自身类名,例如
.card__title { margin: 0; },而非.card .card__title
容易被忽略的一点:BEM 的约束力不在 HTML 结构,而在命名意图与团队共识。DOM 嵌套可以灵活,但类名一旦写成 card__title,它就只能属于 card,哪怕被 JS 移到了 body 最外层。

















