直接用 flex + gap + 媒体断点就能做出语义正确、可访问、不依赖 js 的响应式面包屑,关键在层级结构和断点粒度控制;必须用包裹,分隔符用aria-hidden="true"的独立或伪元素实现,当前页加aria-current="page",中间项按断点显隐并用…替代。

直接用 flex + gap + 媒体断点就能做出语义正确、可访问、不依赖 JS 的响应式面包屑,关键在层级结构和断点粒度控制。
用 nav 和 ol 构建语义化结构
面包屑不是装饰性元素,必须用语义化 HTML 才能被屏幕阅读器识别。不能用 div 堆砌,也不能把分隔符写死成 / 字符。
正确写法是:
<nav aria-label="Breadcrumb"><ol class="flex items-center gap-2 text-sm">
<li><a href="/" class="text-blue-600 hover:underline">Home</a></li>
<li aria-hidden="true">›</li>
<li><a href="/docs" class="text-blue-600 hover:underline">Docs</a></li>
<li aria-hidden="true">›</li>
<li class="text-gray-500">Setup</li>
</ol></nav>
aria-hidden="true" 是给分隔符加的,避免读屏器重复播报;aria-label 在 nav 上声明用途,比用 role="navigation" 更可靠。
用 md:flex-row 和 sm:hidden 控制小屏折叠
移动端常需要收起中间层级,只留首尾+省略号。Tailwind 没有内置“折叠面包屑”工具类,得手动控制显隐和内容。
常见错误是只用 truncate 或 overflow-hidden,结果文字被裁切但没省略号,或者分隔符错位。
推荐做法:
- 给每个
li加sm:hidden,再对首项、末项、分隔符单独设sm:block - 中间项用
sm:hidden md:block,让它们在中屏及以上才显示 - 插入一个带
sm:block md:hidden的li,里面放…(注意用…) - 所有分隔符统一用
sm:hidden md:inline,避免小屏下堆叠
这样比纯 CSS text-ellipsis 更可控,且不依赖 white-space: nowrap 破坏流式布局。
用 before:content 替代硬编码分隔符更灵活
上面示例用了内联 ›,但实际项目中更推荐用伪元素生成分隔符——方便统一换图标、调间距、做动画,也避免 HTML 里混入视觉符号。
可以这样写:
注意三点:
-
before:content-['›']中的单引号不能少,否则编译失败 -
sm:before:hidden必须写在md:before:block前面,否则断点覆盖失效 - 用
pl-4配合绝对定位,避免分隔符影响文字对齐基准线
如果要用 SVG 图标,就换成 before:content-[url('/icons/chevron-right.svg')],但要注意路径需为 CSS 可解析的相对路径,且图标尺寸需额外用 before:w-3 before:h-3 控制。
别忽略焦点管理与键盘导航
默认情况下,面包屑里的链接都能被 Tab 键聚焦,这没问题;但一旦加了折叠逻辑(比如用 JS 切换显隐),就容易让隐藏项仍保留在 tab 顺序里,或焦点跳转后位置丢失。
纯 Tailwind 方案不涉及 JS,所以只需确认两件事:
- 所有
a标签都有href属性,不要用#或空字符串 - 折叠后的
…项不能是可聚焦元素,必须用tabindex="-1"或干脆不用a标签包裹
另外,aria-current="page" 要加在当前页 li 上(不是 a),例如:<li aria-current="page" class="text-gray-500">Setup</li>。这是屏幕阅读器判断“当前位置”的唯一依据,漏掉就等于没做可访问性。
真正难的不是怎么写响应式,而是确保每次折叠/展开后,焦点不卡住、读屏器不报错、缩放 200% 时文字不重叠——这些没法靠类名自动解决,得手动测。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











