CSS Modules中直接写className="card__header card--loading"会失效,因类名必须通过styles.card__header等模块对象访问,且文件名、CSS类名、JSX访问三者须严格一致。

直接在 JSX 里写 className="card__header card--loading" 就会失效——CSS Modules 根本不认这个字符串,它只认 styles.card__header 这种从模块对象里取出来的值。
文件名、CSS类名、JSX访问三者必须严格一致
这是所有问题的起点。React + CSS Modules + BEM 不是“能用就行”,而是“错一个字符就静默挂掉”。
-
Card.tsx必须配Card.module.css,不能是card.module.css或Card.css - CSS 文件里只能出现以
.card开头的类:.card、.card__footer、.card--hovered——.ui-card或.product-card会被忽略 - JSX 中访问必须用
styles['card__footer'](推荐),而不是styles.card__footer(点号访问在连字符场景下会语法报错) - 验证方法:打开编译后生成的 CSS 文件,看类名前缀是不是
Card_开头;或者console.log(styles)看输出里有没有你写的 key
别手写 BEM 字符串,用 bem 工厂函数固化 Block 名
手写 `card ${isHovered ? 'card--hovered' : ''}` 看似省事,实际埋了三个雷:漏空格、拼错连字符、传入 null/undefined 导致多余 class。
- 定义一次:
const cardBem = bem('card'); - 派生 Element:
cardBem.e('header')→'card__header' - 派生 Modifier:
cardBem.m('loading')→'card--loading' - Element 下挂 Modifier(如
card__icon--disabled)也支持:cardBem.e('icon').m('disabled') - 所有传给
classnames的值,必须来自cardBem,禁止混入字面量字符串(如'card--disabled')
classnames 必须配合 styles 对象做二次映射
BEM 工厂函数只生成原始类名(如 'card__footer'),而 CSS Modules 编译后的真实类名是类似 Card_card__footer__abc123 的哈希值。中间这层映射不能跳过。
立即学习“前端免费学习笔记(深入)”;
- 错误写法:
className={cn('card', 'card--loading')}—— 字符串绕过 CSS Modules,样式不生效 - 正确写法:
className={cn(styles.card, styles[cardBem.m('loading')], { [styles[cardBem.e('footer')]]: hasFooter })} - 如果用 TypeScript,可给
bem加泛型约束 modifier 值域,比如bem('button'),拼错直接编译失败
postcss-bem-linter 是唯一能守住底线的校验工具
它不是锦上添花,而是防止团队协作中 BEM 结构被悄悄破坏的最后一道闸。
- 它会拦截:
.card .header(非 BEM 写法)、.card-button(跨 Block 混写)、.card_header(错用单下划线) - 配置后,任何违反 BEM 规范的 CSS 行都会在构建时报错,不让你 commit
- 它不检查 JS 层逻辑,所以仍需靠
cardBem+styles[]组合来封住 JSX 侧漏洞
最常被忽略的是:BEM 的 --modifier 必须对应明确的状态枚举,不是视觉描述词。传 theme="dark;alert(1)" 进去,cardBem.m(theme) 就会吐出非法类名——所以 modifier 值必须受控,不能直接透传 props。


















