bem类名本身就是文档,但需依赖工程链路约束才能生效:block名必须收口到typescript接口或json schema,css选择器须由sass的&__生成而非手写,ci阶段用stylelint-selector-bem-pattern拦截非法结构;tailwind和css-in-js中需将bem语义移至@layer components或css函数key名,禁止后代选择器,确保改一处即同步多处报错。

BEM 类名本身就是文档,不需要额外写“类名说明表”——但前提是团队严格执行语义约束、构建层校验和跨文件同步。
为什么直接照搬 BEM 规则写文档会失效
很多团队把 BEM 文档写成“命名示例合集”,比如列出 .card__title--large 是合法的、.card-title 是非法的,结果开发者依然写出 .user-card__user-info__name 或 .header__logo--blue。问题不在规则没讲清,而在文档没绑定到可执行的约束点。
- 纯文字文档无法拦截错误:没人会在写 CSS 时翻 Wiki 核对
__和--是否多写/少写 - 类名脱离上下文就失去意义:
btn--primary单独出现时,根本看不出它属于哪个 block,下游开发者必须查源码才能确认作用域 - 修饰符写成视觉描述(如
--red)后,换肤或主题切换时无法批量替换,文档里还得加备注“此修饰符仅用于品牌色”
真正起作用的三处文档锚点
让 BEM 发挥文档价值,靠的是把命名语义“钉死”在工程链路上,而不是堆砌规则条目:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
-
Block 名收口到 TypeScript interface 或 JSON Schema:定义
type BlockName = 'card' | 'teaser' | 'filter-panel',所有公开组件必须从此枚举取值;Sass 变量、React 组件 className、stylelint 规则全部引用该 source of truth -
CSS 文件禁用手写选择器:不允许出现
.card__header这种字面量,统一用 Sass 的&__header生成;否则 HTML 里写card__title而 CSS 里漏掉__,文档和实现立刻脱节 -
CI 阶段强制校验:用
stylelint-selector-bem-pattern拦截.card .header(后代选择器)、.button__icon--small(元素修饰符未带 block 前缀)、.card--featuredtitle(--紧贴元素名无__)等非法结构
Tailwind 或 CSS-in-JS 项目怎么保留文档能力
不是放弃 BEM,而是把语义逻辑从 class 字符串转移到构建层:
- Tailwind 中,
@layer components里定义的@apply块,key 名必须是完整 BEM 类名,例如.card__header { @apply p-4 font-bold };禁止散装写p-4 font-bold后再塞进 className - CSS-in-JS(如 Emotion)中,
css函数返回的对象 key 仍用card__header,再通过cx或clsx拼接;避免在 JSX 里写className="card card--large"同时又在样式里用&.card--large .card__header—— 后代选择器一出现,BEM 的文档价值就归零 - React 组件内不封装子类名变量,例如不写
const titleClass = 'card__title';DOM 里必须能一眼看到card__title--featured这样的完整语义串
最常被忽略的一点:BEM 的文档效力不来自“写得全”,而来自“改一处必同步多处”。一旦 block 名在 TypeScript interface 里改了,Sass 编译失败、stylelint 报错、React 组件 className 类型报错——这种强耦合才是文档真正落地的信号。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










