// pattern

Accessible Breadcrumb Pattern

A breadcrumb navigation that uses native <nav> with aria-label, an ordered list for hierarchy, aria-current="page" on the current page, and CSS-only separators. Zero JavaScript required.

native-html beginner zero-js

// 01 · live demo

Live Demo

Use Tab to move between breadcrumb links. Press Enter to follow a link.

Inaccessible version (div-based):

Home>Products>Electronics>Headphones

No <nav>, no list structure, no aria-current, and the > separator text is announced by screen readers. Not keyboard-navigable.

// 02 · the code

The Code

HTML
<nav aria-label="Breadcrumb">
  <ol>
    <li><a href="/">Home</a></li>
    <li><a href="/products/">Products</a></li>
    <li><a href="/products/electronics/">Electronics</a></li>
    <li><span aria-current="page">Headphones</span></li>
  </ol>
</nav>
CSS
nav[aria-label="Breadcrumb"] ol {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
}

nav[aria-label="Breadcrumb"] li {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.875rem;
}

/* CSS separator — decorative, not announced */
nav[aria-label="Breadcrumb"] li:not(:last-child)::after {
  content: "/";
  color: #6b7280;
  pointer-events: none;
}

nav[aria-label="Breadcrumb"] a {
  color: #5b2a86;
  text-decoration: underline;
  text-underline-offset: 2px;
  padding: 0.25rem 0.125rem;
  border-radius: 0.25rem;
  /* SC 2.5.8: minimum target size */
  min-height: 1.5rem;
  display: inline-flex;
  align-items: center;
}

nav[aria-label="Breadcrumb"] a:hover {
  text-decoration-thickness: 2px;
}

/* SC 2.4.13: visible focus indicator */
nav[aria-label="Breadcrumb"] a:focus-visible {
  outline: 2px solid #5b2a86;
  outline-offset: 2px;
  border-radius: 0.25rem;
}

/* Current page — not a link */
nav[aria-label="Breadcrumb"] [aria-current="page"] {
  color: #1f2937;
  font-weight: 600;
}
No JavaScript needed! Breadcrumbs are a purely structural pattern. The <nav> element, <ol> list, and aria-current="page" attribute handle all the semantics. CSS ::after pseudo-elements handle the visual separators. There is no interactive behavior that requires JavaScript.

// 03 · why these decisions

Why These Decisions

Why <nav> with aria-label="Breadcrumb"

The <nav> element creates a navigation landmark that screen readers list alongside other landmarks on the page. The aria-label="Breadcrumb" distinguishes this navigation from others (like the main navigation or footer navigation). Without the label, screen readers announce it as a generic "navigation" landmark, making it hard for users to tell which navigation region is which.

Why <ol> instead of <ul> or plain divs

An ordered list conveys hierarchy — the items have a meaningful sequence from the root page to the current page. Screen readers announce "list with 4 items" and each item's position ("1 of 4", "2 of 4"), giving users a clear sense of the navigation depth. An unordered list would still be better than divs, but an ordered list communicates that the order matters. Plain divs provide no list semantics at all.

Why aria-current="page" on the last item

The aria-current="page" attribute tells screen readers that this item represents the current page. Without it, screen readers have no programmatic way to distinguish the current page from the other breadcrumb items. Users would have to guess based on the fact that the last item isn't a link — but that's a visual cue that doesn't translate to assistive technology.

Why CSS separators instead of text characters

Using CSS ::after pseudo-elements for separators makes them purely decorative. Screen readers skip CSS-generated content in this context, so users don't hear "Home slash Products slash Electronics" between every breadcrumb item. If you use actual text characters like > or / in the HTML, screen readers announce them, creating a noisy and confusing experience.

Why the current page is a <span>, not a link

The current page should not link to itself. A self-referencing link is confusing — it appears interactive but reloads the same page, providing no value. Using a <span> removes the item from the tab order and signals that it is not a navigation target. Combined with aria-current="page", this clearly communicates to all users that they are already on this page.

// 04 · keyboard interaction

Keyboard Interaction

Key Action
Tab Moves focus to the next breadcrumb link. The current page item (a <span>) is not in the tab order.
Shift + Tab Moves focus to the previous breadcrumb link
Enter Follows the focused breadcrumb link, navigating to that page
No custom keyboard handling needed Because breadcrumbs use native <a> elements, all keyboard behavior comes for free. Links are focusable, tabbable, and activatable with Enter by default. There is no need for arrow key navigation, roving tabindex, or any JavaScript keyboard handlers.

// 05 · wcag 2.2 success criteria

WCAG 2.2 Success Criteria

This pattern satisfies the following WCAG 2.2 success criteria:

  • 2.4.8 Location Level AAA — Breadcrumbs provide information about the user's location within the site hierarchy, helping users understand where they are and how to navigate back to higher-level pages.
  • 1.3.1 Info and Relationships Level A — The <nav> landmark, <ol> list structure, and aria-current="page" attribute programmatically convey the breadcrumb structure and the user's current location.
  • 2.4.4 Link Purpose (In Context) Level A — Each breadcrumb link has descriptive text that clearly identifies its destination page.
  • 2.4.3 Focus Order Level A — Focus moves through breadcrumb links in the same left-to-right order as the visual presentation. The current page is not focusable, preventing confusion.
  • 2.4.13 Focus Appearance Level AAA — Breadcrumb links have a visible 2px solid outline focus indicator with sufficient contrast.
  • 2.5.8 Target Size (Minimum) Level AA — Breadcrumb links have padding and a minimum height to meet the 24x24px minimum target size requirement.

// 06 · screen reader behavior

Screen Reader Behavior

When navigating to the breadcrumb region

  • NVDA: "Breadcrumb, navigation landmark" — announces the aria-label and the landmark role, then "list with 4 items" when entering the list
  • JAWS: "Breadcrumb navigation region" — announces the landmark, then "list of 4 items, Home, link, 1 of 4"
  • VoiceOver: "Breadcrumb, navigation" — announces the landmark label and role, then reads list items with their positions

When navigating through the links

  • NVDA: "Home, link, 1 of 4" then "Products, link, 2 of 4" then "Electronics, link, 3 of 4" — announces each link text and position. CSS separators are not announced.
  • JAWS: "Home, link" then "Products, link" then "Electronics, link" — announces link text and role
  • VoiceOver: "Home, link, 1 of 4" then "Products, link, 2 of 4" — includes position information from the ordered list

When reaching the current page

  • NVDA: "Headphones, current page, 4 of 4" — announces the text and the aria-current="page" state
  • JAWS: "Headphones, current page" — clearly indicates this is the current page, not a link
  • VoiceOver: "Headphones, current page, text, 4 of 4" — announces the current page state and position

Why CSS separators matter

Because the separators are rendered with CSS ::after pseudo-elements, screen readers do not announce them. If the separators were actual text characters (like >) in the HTML, screen readers would announce "Home, greater than, Products, greater than, Electronics" — a much noisier experience that adds no useful information.

// 07 · common mistakes

Common Mistakes

Using <div> elements instead of <nav> and <ol> Without the <nav> landmark, screen readers cannot identify the breadcrumb as a navigation region. Without the <ol>, there is no list structure — screen readers cannot announce the number of items or each item's position. The combination of <nav> and <ol> gives assistive technology everything it needs to understand the breadcrumb structure.
Missing aria-label on the <nav> element Most pages have multiple <nav> elements (main navigation, footer navigation, breadcrumbs). Without an aria-label, screen readers announce them all as "navigation" with no way to distinguish them. Adding aria-label="Breadcrumb" lets users identify and jump to the breadcrumb navigation from any landmark list.
Making the current page a link The current page should not link to itself. A self-referencing link confuses users — clicking it reloads the same page, providing no value. Use a <span> with aria-current="page" instead. This removes the item from the tab order and clearly signals that it represents the user's current location, not a navigation target.
Using visual-only text separators like > in the HTML Text characters placed between breadcrumb items are announced by screen readers. Users hear "Home, greater than, Products, greater than" instead of just "Home, Products." Use CSS ::after pseudo-elements for separators — they are decorative and not announced.
Not using aria-current="page" Without aria-current="page", screen readers have no programmatic way to identify which breadcrumb item represents the current page. Sighted users can see the visual difference (no underline, bold text), but this information must also be conveyed to assistive technology.
Breadcrumbs that don't match the actual page hierarchy Breadcrumbs must reflect the real site structure and the user's actual location. If the breadcrumb trail doesn't match the URL hierarchy or the page the user is on, it creates a confusing and misleading navigation experience. Always generate breadcrumbs from the actual page hierarchy, not from arbitrary categories or marketing labels.

// 08 · native html vs. aria

Native HTML vs. ARIA

Breadcrumbs are one of the patterns that mostly use native HTML, needing only minimal ARIA. This table shows what each approach contributes.

Feature Native HTML ARIA
Navigation landmark <nav> — creates a navigation landmark automatically role="navigation" — equivalent, but unnecessary when using <nav>
Landmark label None — <nav> alone doesn't provide a distinguishing label aria-label="Breadcrumb" — required to identify the navigation purpose
List structure <ol> — ordered list conveys hierarchy, item count, and positions Not needed — native list semantics handle this entirely
Link semantics <a> — focusable, keyboard-activatable, announces as "link" Not needed — native links provide full accessibility
Current page indicator None — HTML has no native "current page" attribute aria-current="page" — required to identify the current location
Separators CSS ::after — decorative, not announced by screen readers Not needed — CSS handles visual separators without ARIA
Keyboard navigation Native <a> elements — tabbable and activatable by default Not needed — no custom keyboard handling required
Focus management Native focus order follows DOM order, which matches visual order Not needed — no focus management required
The verdict Breadcrumbs are a great example of the "native HTML first, ARIA only when needed" principle. The <nav> element, <ol> list, and <a> links provide the vast majority of the accessibility. The only ARIA needed is aria-label to name the landmark and aria-current="page" to identify the current page — two simple attributes.