Breadcrumbs
stableBreadcrumbs show users where they are within a site hierarchy and let them navigate back to parent sections. The component is CSS-only — no JavaScript required. Colors adapt automatically to any surface or theme context.
Anatomy
Wrap an <ol> in a <nav> with aria-label="Breadcrumb".
Each list item gets the .breadcrumb-item class. The current page's link carries
aria-current="page". The › separator is generated by CSS so it never
appears in the DOM or gets read by screen readers.
<nav aria-label="Breadcrumb">
<ol class="breadcrumb" role="list">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item"><a href="/components">Components</a></li>
<li class="breadcrumb-item"><a href="/components/breadcrumbs" aria-current="page">Breadcrumbs</a></li>
</ol>
</nav> Usage guidance
Use breadcrumbs when the site has two or more levels of hierarchy and users are likely to have arrived deep in the tree — from search, a direct link, or a previous session. Skip them on flat sites, landing pages, or anywhere the hierarchy has fewer than two meaningful levels.
| Situation | Guidance |
|---|---|
| Current page link | Keep as an <a> with aria-current="page". Avoid plain <span> — it breaks keyboard navigation consistency. |
| Depth | Show the full path from root. Truncating intermediate crumbs hides context without saving meaningful space. |
| Placement | Render above the page <h1>, below the primary navigation bar. |
| Mobile | .breadcrumb uses flex-wrap: wrap so long paths reflow naturally. If the path is very deep, consider showing only the immediate parent on small viewports. |
Accessibility
The correct landmark and ARIA attributes are required — they are not optional polish.
| Attribute | Where | Why |
|---|---|---|
aria-label="Breadcrumb" | <nav> | Distinguishes this nav landmark from the primary navigation when both appear on the same page. |
role="list" | <ol> | Safari/VoiceOver removes list semantics (item count, list navigation) from any <ol> with list-style: none. role="list" restores them regardless of CSS. The <nav> wrapper mitigates this on modern Safari, but role="list" is the robust solution for all versions and wrapper contexts. |
aria-current="page" | Last <a> | Tells screen readers which link represents the current page. Without it, sighted and non-sighted users get different information. |
| CSS separator | ::after pseudo-element | Generated via content: '›' / '' — the empty alt value prevents screen readers from announcing the separator character. |
CSS reference
Shipped in dist/farn-components.css. Load alongside farn.css or
farn-tokens.css. Colors are semantic — they adapt automatically under
data-theme and data-surface without any overrides.
.breadcrumb {
display: flex;
flex-wrap: wrap;
align-items: center;
list-style: none;
padding: 0;
margin: 0;
font-size: 0.875rem;
}
.breadcrumb-item {
display: flex;
align-items: center;
color: var(--color-text-secondary);
}
.breadcrumb-item:not(:last-child)::after {
content: '›'; /* fallback: visible in all browsers */
content: '›' / ''; /* modern: suppresses screen-reader announcement */
margin: 0 var(--space-xs);
}
.breadcrumb-item a {
color: inherit;
text-decoration: none;
transition: color var(--duration-fast) var(--ease-out);
}
.breadcrumb-item a:hover,
.breadcrumb-item a[aria-current="page"] {
color: var(--color-text);
}