所有 BEM Modifier 必须由 bem 工厂函数生成,禁止手写字符串;CSS Modules 下需通过 styles[bem.m('mod')] 获取真实类名;多 modifier 需单独调用并传入 classNames;构建时应启用 postcss-bem-linter 和 eslint-plugin-css-modules 拦截非法用法。
直接写 classNames('button--primary', { 'button--disabled': disabled }) 看似省事,但会绕过 BEM 结构校验,导致 modifier 值拼错(比如 button--prmiary)、漏连字符、或混入非法组合(如 button__icon--primary 这种 Element 下挂 Modifier)。TypeScript 也完全无法捕获这类错误。
bem 工厂函数派生,例如 buttonBem.m('primary') 或 buttonBem.modifier('disabled')
classNames 参数中出现任何字面量字符串修饰符,包括插值形式:`button--${variant}` 是危险信号bem 函数加泛型约束,比如 bem('button'),其中 ButtonModifier = 'primary' | 'secondary' | 'danger'
在 Button.module.css 中,buttonBem.m('primary') 返回的是原始类名 button--primary,不是最终哈希后的类名。直接传给 classNames 会导致样式丢失——因为 DOM 上实际渲染的是类似 Button_button--primary__abc123 这样的名字。
styles[buttonBem.m('primary')] 获取真实类名,再传入 classNames
classNames(styles.button, 'button--primary') —— 字符串 'button--primary' 不会命中 styles 对象,也不受 CSS Modules 作用域保护cn(buttonBem.m('primary')),内部自动做 styles[...] 查找,避免每个组件都重复写常见错误是把多个 modifier 写成一个对象 key:{ ['button--primary button--large']: true }。这会让 classNames 把整个字符串当做一个类名处理,而 CSS Modules 的 styles 对象里根本不存在这个键,结果就是白屏级失效。
bem.m(),然后分别传入 classNames:classNames(styles.button, buttonBem.m('primary'), buttonBem.m('large'))
{ [styles[buttonBem.m('primary')]]: variant === 'primary', [styles[buttonBem.m('large')]]: size === 'large' }
它不会报错说 “你写了 button__label--disabled”,但它会拒绝这种写法——因为 BEM 规范里 Element(__label)下面不允许挂 Modifier(--disabled)。这类问题 runtime 完全无提示,只能靠构建时拦截。
.button__label--disabled(Element 下挂 Modifier)、.button-icon(缺双下划线)、.button .icon(带空格后代选择器)eslint-plugin-css-modules 可进一步限制 JSX 中只能使用当前模块声明过的 BEM 键名,比如 styles.button__icon 存在才允许用,styles.modal__close 直接报 ESLint 错误