Breadcrumb
Breadcrumb displays a trail of links tracing the current page's position
within a site's hierarchy, from the root down to where someone actually is.
Each step is a plain <a> you write yourself, so your framework's own link
component keeps its client-side navigation and prefetching. The final step is the current page.
When to use
Add a breadcrumb once someone can be more than one level deep in your site, such as a product inside a category or a document inside a folder, so they can always see where they landed and step back without hitting the browser's back button. Skip it on flat, top-level pages where there's nowhere meaningful to trace back to, and never let it be the only way to navigate. It should complement a real primary navigation, not replace one.
Install & usage
Pick a framework in the toolbar above and these snippets adapt.
npm install @eunomia/elements
import "@eunomia/elements/breadcrumb.js";
<eun-breadcrumb aria-label="Breadcrumb">
<a href="/">Home</a>
<a href="/catalogue">Catalogue</a>
<span>Kitchen mixer tap</span>
</eun-breadcrumb>
Importing the file registers <eun-breadcrumb> as a custom element, with
no further setup needed. Each direct child is one step: an <a>, or a
plain element such as a <span> for a step that isn't a page. The breadcrumb wraps every step in its own list item, adds the
separators, and marks the last step as the current page.
npm install @eunomia/elements
<script type="module">
import "@eunomia/elements/breadcrumb.js";
</script>
<eun-breadcrumb aria-label="Breadcrumb">
<a href="/">Home</a>
<a href="/catalogue">Catalogue</a>
<span>Kitchen mixer tap</span>
</eun-breadcrumb>
npm install @eunomia/elements
import { Link } from "react-router-dom";
import "@eunomia/elements/breadcrumb.js";
function ProductBreadcrumb() {
return (
<eun-breadcrumb aria-label="Breadcrumb">
<Link to="/">Home</Link>
<Link to="/catalogue">Catalogue</Link>
<span>Kitchen mixer tap</span>
</eun-breadcrumb>
);
}
npm install @eunomia/elements
"use client";
import Link from "next/link";
import "@eunomia/elements/breadcrumb.js";
export function ProductBreadcrumb() {
return (
<eun-breadcrumb aria-label="Breadcrumb">
<Link href="/">Home</Link>
<Link href="/catalogue">Catalogue</Link>
<span>Kitchen mixer tap</span>
</eun-breadcrumb>
);
}
<Link> renders a real <a> in the server HTML, so the trail is crawlable
and keeps Next's client-side navigation and prefetching, with no router
bridge needed.
npm install @eunomia/elements
<script setup>
import "@eunomia/elements/breadcrumb.js";
</script>
<template>
<eun-breadcrumb aria-label="Breadcrumb">
<router-link to="/">Home</router-link>
<router-link to="/catalogue">Catalogue</router-link>
<span>Kitchen mixer tap</span>
</eun-breadcrumb>
</template>
npm install @eunomia/elements
import { CUSTOM_ELEMENTS_SCHEMA, Component } from "@angular/core";
import { RouterLink } from "@angular/router";
import "@eunomia/elements/breadcrumb.js";
@Component({
selector: "app-product-breadcrumb",
imports: [RouterLink],
template: `
<eun-breadcrumb aria-label="Breadcrumb">
<a routerLink="/">Home</a>
<a routerLink="/catalogue">Catalogue</a>
<span>Kitchen mixer tap</span>
</eun-breadcrumb>
`,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class ProductBreadcrumbComponent {}
Guidance
- Use
eun-breadcrumbon any page nested more than one level deep, so people can see where they are and jump back to an ancestor page in one click - Write each step with your framework's own link component, so navigation stays client-side without any router bridge
- Keep labels short, since long ones truncate with an ellipsis past
--breadcrumb-item-max-width(see the Examples tab) - Pair an icon with visible text, since an icon alone has no accessible name (see the Accessibility tab)
- Using it on top-level pages, since a one- or two-step trail rarely earns its place
- Wrapping the steps in another element, such as a
<div>or a fragment that renders one: each direct child is a step, so the wrapper would become a single step - Using it as the page's only means of navigating back. Pair it with a proper primary navigation, not instead of one
Live testing
Properties
eun-breadcrumb only needs its steps as children, and aria-label
(defaults to "Breadcrumb", in the active locale).
Breadcrumb <eun-breadcrumb>
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
| aria-label | string | — | The accessible label for the navigation landmark. Defaults to the localized string for the current locale (English: "Breadcrumb") |
Import the exact TypeScript type behind any property above, see
Properties
JS-only — no matching HTML attribute, set these from a script or a template binding.
| Name | Type | Default | Description |
|---|---|---|---|
| resolvedLocale | — | This instance's own `locale` when set, the shared default locale otherwise. Used for `Intl` formatting, so dates and numbers follow the same locale as dictionary. | |
| dictionary | — | This instance's locale-resolved string dictionary — see the class doc for the resolution order. |
Slots
| Name | Description |
|---|---|
| (default) | The steps, one per direct child : an anchor, or a plain element for an unlinked step |
CSS custom properties
| Name | Description |
|---|---|
| --breadcrumb-font-size | Sets the font size of the whole trail, 14px by default |
| --breadcrumb-link-color | Sets the color of linked steps |
| --breadcrumb-link-color-hover | Sets the color of linked steps on hover or focus |
| --breadcrumb-link-text-decoration | Sets the text decoration of linked steps, underline by default, such as none |
| --breadcrumb-link-text-decoration-hover | Sets the text decoration of linked steps on hover or focus, underline by default |
| --breadcrumb-current-color | Sets the color of the current page and unlinked steps |
| --breadcrumb-focus-outline-color | Sets the color of a linked step's focus outline |
| --breadcrumb-separator | Sets the separator between steps, as a quoted CSS string, such as '>' |
| --breadcrumb-separator-color | Sets the color of the separator |
| --breadcrumb-separator-gap | Sets the gap around the separator |
| --breadcrumb-item-max-width | Sets the maximum width of each step, beyond which its label truncates with an ellipsis |
| --breadcrumb-gap | Sets the row gap, used when the trail wraps onto multiple lines |
| --breadcrumb-transition-duration | Sets the duration of the link hover color transition |
Basic
<eun-breadcrumb aria-label="Breadcrumb">
<a href="/">Home</a>
<a href="/catalogue">Catalogue</a>
<span>Kitchen mixer tap</span>
</eun-breadcrumb>
Steps
Two steps
<eun-breadcrumb aria-label="Breadcrumb">
<a href="/">Home</a>
<span>Catalogue</span>
</eun-breadcrumb>
Unlinked intermediate step
Any element other than a link renders as plain text, useful for a category
that groups pages without being a page itself. Mark the actual current page
with aria-current="page" here, since the breadcrumb only marks the last
step when no step is marked yet.
<eun-breadcrumb aria-label="Breadcrumb">
<a href="/">Home</a>
<span>Faucets</span>
<span aria-current="page">Kitchen mixer tap</span>
</eun-breadcrumb>
Linked current page
The current page can stay a link, for instance to reload it with its default filters. It's then styled as the current page rather than as a link.
<eun-breadcrumb aria-label="Breadcrumb">
<a href="/">Home</a>
<a href="/catalogue">Catalogue</a>
</eun-breadcrumb>
Display
Long labels truncate
Each step truncates with an ellipsis past --breadcrumb-item-max-width
(240px by default), so one long label can't push the trail off-screen.
Custom separator
--breadcrumb-separator takes any quoted CSS string.
<eun-breadcrumb aria-label="Breadcrumb" style="--breadcrumb-separator: '>';">
...
</eun-breadcrumb>
Integration
SPA navigation
The steps are your own links, rendered in the light DOM: a router's link
component, such as Next's <Link>, React Router's <Link>, Vue's
<router-link> or Angular's routerLink, handles the click itself, with
client-side navigation and prefetching. eun-breadcrumb fires no event of
its own, and needs no router bridge.
import Link from "next/link";
<eun-breadcrumb aria-label="Breadcrumb">
<Link href="/">Home</Link>
<Link href={`/categories/${category.slug}`}>{category.name}</Link>
<span>{article.name}</span>
</eun-breadcrumb>;
Server-side rendering
Before the breadcrumb upgrades, its steps are plain links run together, with
no separator. Link breadcrumb-ssr.css in the page's <head> to draw the
final trail from the first paint:
<link
rel="stylesheet"
href="node_modules/@eunomia/elements/dist/ssr/breadcrumb-ssr.css"
/>
A bundler that resolves package exports can reach it as
@eunomia/elements/breadcrumb-ssr.css.
It lays the steps out in a row with their separators, styles the links and
the current page, and covers the --breadcrumb-* properties. A step long
enough to truncate counts its separator in its maximum width until the
upgrade, so the steps after it can move slightly then.
Custom
Override the --breadcrumb-* CSS variables, listed in full in the API tab.
Links are underlined by default: --breadcrumb-link-text-decoration: none
removes it, and the underline still shows on hover.
<eun-breadcrumb class="custom-breadcrumb" aria-label="Breadcrumb">
...
</eun-breadcrumb>
.custom-breadcrumb {
--breadcrumb-link-color: #be185d;
--breadcrumb-link-color-hover: #9d174d;
--breadcrumb-link-text-decoration: none;
--breadcrumb-separator: ">";
}
The custom properties that change colors, borders, shadows and corners, at a glance. The API tab lists every property of the component.
| Variable | Sets |
|---|---|
--breadcrumb-link-color |
Sets the color of linked steps |
--breadcrumb-link-color-hover |
Sets the color of linked steps on hover or focus |
--breadcrumb-current-color |
Sets the color of the current page and unlinked steps |
--breadcrumb-focus-outline-color |
Sets the color of a linked step's focus outline |
--breadcrumb-separator-color |
Sets the color of the separator |
Keyboard interactions
eun-breadcrumb adds no custom keyboard handling of its own. Every step is
your own link (or plain text), so standard link navigation already applies:
| Key | Action |
|---|---|
Tab / Shift+Tab |
Moves focus between linked steps, in trail order |
Enter |
Follows the focused step's link |
Semantics and screen readers
Aria attributes and rules
- Rendered as
<nav aria-label="Breadcrumb">wrapping an<ol role="list">, the standard breadcrumb pattern recommended by the WAI-ARIA APG. Its default label follows the active locale, andaria-labeloverrides it for a specific context. role="list"is set explicitly despite<ol>having it implicitly: Safari drops the implicit role oncelist-style: noneis applied (needed here for the visual flex layout), which would otherwise silently skip the list for VoiceOver users.- Each step sits in its own
<li>through its own slot, so assistive technology reads a list with one item per step, while the links themselves stay in your markup. - The separators are
aria-hidden, so they're never read out.
The current page
The current page carries aria-current="page". Unless a step already sets
it, the breadcrumb marks the last step
itself, and moves that mark along when the steps change. Set it yourself
when the current page isn't the last step, or to have it in the server
HTML before the component loads.
Label accessibility
A step's accessible name comes entirely from its content. An icon-only
step has no accessible name, since eun-icon renders with
aria-hidden="true" by design (see the Icon component). Always pair an
icon with visible text, or set aria-label on the link if the text truly
can't be visible.
Long labels
Labels truncate visually past --breadcrumb-item-max-width, but the full
text stays in the DOM. Screen readers always read the complete label,
regardless of how much of it is visually clipped.
Reference links