面包屑bem结构必须分离语义:breadcrumb__item为容器,breadcrumb__link为可聚焦链接,当前页用breadcrumb__text替代;分隔符用breadcrumb__separator配合:not(:first-child)::before实现,禁用html内联字符。

面包屑的BEM结构怎么写才不踩坑
直接说结论:面包屑必须用 bem__item + bem__link 分离语义,不能把链接塞进 __item 里当子元素混用。否则会导致可访问性失效、样式耦合、焦点管理错乱。
常见错误是这样写:
.breadcrumb__item {
/* 把 a 标签直接放在这里 */
}
问题在于:屏幕阅读器会把整个 __item 当作一个不可聚焦的容器,而实际可点击的是 a;如果后续要加图标、分隔符或状态(如当前页禁用),结构就僵住了。
正确结构是:
-
__item是纯粹的包裹容器,只负责布局和间距 -
__link是独立块级元素,承载语义、焦点、hover/focus 状态 - 当前页用
__text替代__link,保持 DOM 层级一致
如何处理分隔符(separator)的BEM归属
分隔符不是独立组件,它属于面包屑内部视觉节奏的一部分,所以必须定义为 breadcrumb__separator,而不是单独建 separator 块。
原因很实际:分隔符的样式(颜色、间距、字体图标)高度依赖父容器状态。比如深色主题下,breadcrumb__separator 要随 breadcrumb--dark 变色;若抽成独立块,就得重复传 theme 参数或强依赖 CSS 变量注入,反而增加维护成本。
实操建议:
- 用伪元素
::before或::after实现分隔符,避免冗余 HTML - 只给第一个之后的
__item添加分隔符,用:not(:first-child)控制 - 图标类分隔符(如
/或›)统一用font-family: system-ui避免渲染差异
当前页(current)状态该挂在哪一级
当前页不是“禁用链接”,而是“非导航文本”,所以不能用 is-disabled 修饰 __link,而应替换元素类型:用 <span class="breadcrumb__text"></span> 替代 <a class="breadcrumb__link"></a>。
这样做有三个硬性好处:
- 语义正确:ARIA 不会误读为可跳转链接
- 样式隔离:
__text可独立设置cursor: default、color: var(--text-muted),不干扰__link的 hover/focus 样式 - JS 安全:事件委托时,不会意外捕获到当前页的 click
注意:不要用 aria-current="page" 加在 __link 上“假装”当前页——那只是补救措施,掩盖了结构缺陷。
响应式折叠逻辑该不该放进 BEM 类名里
不该。BEM 类名只描述静态结构与状态(如 breadcrumb__item--hidden),不描述行为逻辑(如 “折叠后显示省略号”)。
折叠是运行时行为,依赖容器宽度、字体大小、字符长度等动态条件,硬编码成类名会导致:
- HTML 中出现一堆
breadcrumb__item--hidden-md、--hidden-lg,无法维护 - 服务端渲染时无法预判视口,SSR 与 CSR 渲染不一致
- 无障碍模式下可能隐藏关键导航路径
正确做法是:用 JS 动态计算并添加 js-breadcrumb-collapsed 这类功能性类名(前缀 js- 表明由脚本控制),CSS 仅响应这个类做视觉收起,同时确保被折叠项仍保留在 DOM 中并可聚焦(用 clip-path 或 visibility: hidden,而非 display: none)。
最常被忽略的一点:折叠后必须保留分隔符的视觉连贯性——被隐藏的 __item 后面那个 __separator 也要同步隐藏,否则会出现孤立的 / 符号。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











