BEM 类名天然承载组件归属、结构角色与业务状态三层语义,如teaser__title--featured可直读为“teaser组件的标题元素处于精选状态”,无需查逻辑或设计稿;其文档价值依赖语义一致性、构建层校验及跨文件同步约束。

类名本身就能当文档看,不用额外写注释
因为 BEM 类名天然携带三层语义:block 表示组件归属,block__element 表示结构角色,block--modifier 表示状态或变体。比如看到 teaser__title--featured,立刻知道这是 teaser 组件的标题元素、处于“精选”业务状态——不需要查 JS 逻辑或翻设计稿。
这在样式库交付时特别关键:下游开发者扫一眼 HTML 或 DevTools 就能理解组件能力边界,而不是靠猜或翻 Wiki。
避免“命名即文档”失效的三个典型错误
常见错误不是写错语法,而是破坏语义一致性:
-
user-card__user-info__name—— 元素嵌套过深,违反 BEM 的扁平原则;实际应拆成user-card__name或提升user-info为独立 block -
header__logo--blue—— 用纯视觉修饰符(--blue)替代业务语义(如--brand),导致换肤时无法批量替换 -
btn primary混用通用类 ——primary脱离btn上下文后含义模糊,文档里必须反复说明“仅限按钮使用”,而btn--primary自带约束
配合样式库文档的最小必要动作
真正让 BEM 发挥文档价值,不靠命名本身,而靠收敛和校验:
立即学习“前端免费学习笔记(深入)”;
- 所有公开的 block 名必须收口到一个 JSON Schema 或 TypeScript interface 中(如
type BlockName = 'card' | 'teaser' | 'filter-panel'),作为文档源 - 禁止在 CSS 文件里手写
.card__header这类选择器,统一用 Sass 的&__header生成,确保 HTML 和 CSS 类名严格对齐 - 用
stylelint-selector-bem-pattern在 CI 阶段拦截.card .header或.button__icon--small这类非 BEM 写法,防止文档和代码脱节
Tailwind 或 CSS-in-JS 项目里怎么保留这份文档能力
不是放弃 BEM,而是把它的语义逻辑转移到构建层:
- Tailwind 项目中,
@layer components里定义@apply块时,命名必须遵循 BEM 结构,比如.card__header { @apply p-4 font-bold },而非散装p-4 font-bold - CSS-in-JS(如 Emotion)中,用
css函数生成样式对象时,key 名仍用card__header,再通过cx或clsx动态拼接,保持类名可读性 - 绝对不要在 React 组件里写
className="card card--large"同时又在 CSS 文件里写.card--large .card__header—— 后代选择器一出现,BEM 的文档价值就归零
最常被忽略的一点:BEM 的文档效力不来自“写得全”,而来自“改一处必同步多处”。只要 block 名变更,所有 block__* 和 block--* 都得跟着重命名——这个成本反而是保障文档真实性的护栏。


















