block名必须带业务前缀如onboard-step,禁用.step;元素名直属于block,如onboard-step__icon;修饰符用--且值表意图;html中element必须与block共存。

step组件的Block名必须带业务上下文前缀
直接叫 .step 或 .steps 是危险的——它会和第三方库、旧代码、甚至未来其他“步骤”语义的组件冲突。比如你项目叫「onboard」,就该用 onboard-step;如果是订单流程,用 order-step。关键不是“步骤”这个词,而是谁在用这个步骤。
常见错误:.step-wrapper、.step-container —— 这类名字没功能含义,只是临时布局占位符,后期无法被复用或单独测试。
判断标准:这个块能否脱离当前页面独立存在?比如 onboard-step 能用在注册页、设置向导、权限开通流程里,就是合格 Block;而 homepage-step 一换页面就失效,得重构。
step__icon、step__title 这类元素名不能带冗余父级语义
正确写法是 onboard-step__icon、onboard-step__title、onboard-step__description。禁止写成 onboard-step__step-icon 或 onboard-step__step-title —— 元素名本身已通过 __ 锚定到 block,再重复 “step” 属于语义冗余,且拉长类名、增加维护成本。
如果图标有多种用途(状态指示、加载态、错误提示),用 Modifier 区分:onboard-step__icon--status、onboard-step__icon--loading,而不是新建 onboard-step__status-icon。
注意:所有元素必须直属于 block,不允许嵌套层级,比如 onboard-step__icon__spinner 是非法的。若需更细粒度控制,应把 spinner 升为独立 block:spinner,或改用 onboard-step__icon--loading。
状态与变体统一用 --modifier,但值必须表达意图而非实现
修饰符要挂载在 block 或 element 上,且值必须可枚举、与业务对齐。例如:
-
onboard-step--horizontal✅(布局变体,意图明确) -
onboard-step__title--completed✅(标题完成态,状态语义) -
onboard-step__icon--active✅(当前步骤高亮) -
onboard-step--bg-blue❌(暴露样式细节,换主题时得全局搜) -
onboard-step__title--fs-14px❌(绑定具体尺寸,破坏响应性)
多个 modifier 并列使用:onboard-step__icon--active--error 不合法,应写成 onboard-step__icon--active onboard-step__icon--error。链式叠加会让工具链(如 stylelint-selector-bem-pattern)无法提取、VS Code 插件无法高亮。
JS 动态拼接类名时最容易漏掉 block 上下文
手拼字符串极易出错,比如:className={`onboard-step__icon ${isActive ? 'onboard-step__icon--active' : ''}` 看似没问题,但一旦复制到另一个 block(如 order-step)里复用,就会错用类名。
推荐封装生成函数:
const BLOCK = 'onboard-step';
const cn = (e, m) => `${BLOCK}${e ? '__' + e : ''}${m ? '--' + m : ''}`;
// 使用
cn('icon', 'active') // → 'onboard-step__icon--active'
cn('title') // → 'onboard-step__title'
这样能保证所有类名严格遵循 BEM 结构,且 pre-commit 阶段可通过正则校验暂存区 CSS 文件中是否出现非法 __ 或 -- 组合。
真正容易被忽略的是:HTML 中 element 必须和 block 共存。写 <div class="onboard-step__icon"> 却不加 <code>onboard-step,等于把元素从语义锚点中剥离——它不再属于任何 block,BEM 的隔离性和可维护性就崩了一角。











