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: &#39;\203A&#39;">
    <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 title attribute on truncated crumbs so the full label is available on hover (and to assistive tech that reads title).

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

TokenDefaultDescription
--breadcrumb-gapvar(--gap-tight)Gap between crumb items and their separators
--breadcrumb-font-sizevar(--ui-text-sm)Font size for breadcrumb text
--breadcrumb-separator"/"Content string used as the visual separator between crumbs
--breadcrumb-separator-colorvar(--color-text-subtle)Color of the separator character
--breadcrumb-item-max-inline-sizenoneMaximum width per crumb before text truncation engages
ClassDescription
.breadcrumbcomponent root
.is-condensedCollapses middle crumbs at narrow container widths