Breadcrumb
Part of the extras bundle, not included by default.
Intent
Breadcrumbs tell the user where they are in a hierarchy and
how to walk back up. They're a navigation landmark
(<nav>) wrapping an ordered list -- the
ordered-list semantics let assistive tech announce "1 of 3,
Home" / "2 of 3, Products" / "3 of 3, current page Widget".
The CSS layer just paints separators between items and styles
the current-page slot.
The current page sits in a <span
aria-current="page"> instead of a link --
aria-current is the API; brio's CSS binds to it
directly (P5).
Basic usage
<nav class="breadcrumb" aria-label="Breadcrumb">
<ol>
<li><a href="#">Home</a></li>
<li><a href="#">Products</a></li>
<li><span aria-current="page">Widget</span></li>
</ol>
</nav>Custom separator
The separator is a single CSS custom property -- override
--breadcrumb-separator on the root with any
string. Slashes are universal and survive plain-text fallback;
chevrons (›) read as crisper in dense UI but
depend on the font carrying the glyph.
<nav class="breadcrumb" aria-label="Breadcrumb" style="--breadcrumb-separator: '\203A'">
<ol>
<li><a href="#">Home</a></li>
<li><a href="#">Settings</a></li>
<li><span aria-current="page">Profile</span></li>
</ol>
</nav>Truncation -- per-crumb
Long labels (a category named "Email Validation Rules and
Patterns") blow out the inline width and force a wrap. Set
--breadcrumb-item-max-inline-size on the root to
cap each link / current-page span with
text-overflow: ellipsis. All crumbs stay visible;
long labels truncate. Pair with a title attribute
on the crumb so the full label is hover-revealable.
<docs-resize>
<nav class="breadcrumb" aria-label="Breadcrumb" style="--breadcrumb-item-max-inline-size: 8rem">
<ol>
<li><a href="#" title="Home">Home</a></li>
<li><a href="#" title="Documentation">Documentation</a></li>
<li><a href="#" title="Components">Components</a></li>
<li><a href="#" title="Forms and Inputs">Forms and Inputs</a></li>
<li><span aria-current="page" title="Email Validation Rules and Patterns">Email Validation Rules and Patterns</span></li>
</ol>
</nav>
</docs-resize>Truncation -- condensed (long trails)
Add .is-condensed to collapse middle crumbs when
the container is narrow. The variant sets
container-type: inline-size on the
<nav>; a container query then hides every
middle <li> below 30rem and replaces the
separator before the second-to-last item with
… /. Resize the demo below to see the collapse
kick in.
<docs-resize>
<nav class="breadcrumb is-condensed" aria-label="Breadcrumb">
<ol>
<li><a href="#">Home</a></li>
<li><a href="#">Acme Corp</a></li>
<li><a href="#">Engineering</a></li>
<li><a href="#">Web Platform</a></li>
<li><a href="#">Components</a></li>
<li><a href="#">Forms</a></li>
<li><a href="#">Validation</a></li>
<li><span aria-current="page">Email</span></li>
</ol>
</nav>
</docs-resize>
The truncation patterns compose -- combine
.is-condensed with
--breadcrumb-item-max-inline-size for long trails
where individual labels are also long.
Customization
Gap, font size, separator content, and separator color all
flow through scoped --breadcrumb-* tokens. Gap
pulls from the density grammar
(--gap-tight) and font size from the chrome
typography scale (--ui-text-sm) so breadcrumbs
sit comfortably alongside other navbar / page-header chrome.
Override either on the root for surrounding-context fit -- a
page header on a denser pattern might want
--breadcrumb-gap: var(--gap-compact); a breadcrumb
embedded in a card might want
--breadcrumb-font-size: var(--ui-text-xs).
Accessibility
-
Wrap the breadcrumb in
<nav aria-label="Breadcrumb">-- the landmark + label is what assistive tech surfaces when the user navigates by region. -
Use
<ol>(not<ul>) so screen readers announce "N of M" position. The order is the navigation hierarchy; that ordering is meaningful. -
Mark the current page with
aria-current="page"on a<span>(not a link -- the user is already on this page). brio's CSS binds visual treatment to the attribute, not a class. -
With
.is-condensed, hidden middle crumbs are removed from the accessibility tree at narrow container widths -- screen-reader users on narrow viewports won't hear the full trail. The full markup is still in the source for wider-viewport / desktop users. If your application demands the trail always be reachable regardless of width, use per-crumb truncation instead and let the trail wrap. -
Use the
titleattribute on truncated crumbs so the full label is available on hover (and to assistive tech that readstitle).
Markup
<!-- Standard -->
<nav class="breadcrumb" aria-label="Breadcrumb">
<ol>
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li><span aria-current="page">Widget</span></li>
</ol>
</nav>
<!-- Long labels: per-crumb truncation -->
<nav class="breadcrumb" aria-label="Breadcrumb"
style="--breadcrumb-item-max-inline-size: 10rem">
...
</nav>
<!-- Many crumbs: condensed middle -->
<nav class="breadcrumb is-condensed" aria-label="Breadcrumb">
...
</nav>
CSS Reference: Breadcrumb
Navigation path showing page hierarchy
Source: src/css/extras/breadcrumb.css
Tokens
| Token | Default | Description |
|---|---|---|
--breadcrumb-gap | var(--gap-tight) | Gap between crumb items and their separators |
--breadcrumb-font-size | var(--ui-text-sm) | Font size for breadcrumb text |
--breadcrumb-separator | "/" | Content string used as the visual separator between crumbs |
--breadcrumb-separator-color | var(--color-text-subtle) | Color of the separator character |
--breadcrumb-item-max-inline-size | none | Maximum width per crumb before text truncation engages |
Classes
| Class | Description |
|---|---|
.breadcrumb | component root |
.is-condensed | Collapses middle crumbs at narrow container widths |