# Breadcrumbs that mark the current page only by its look

Source: https://easeweb.dev/learn/breadcrumbs
Topics: Menus and navigation > Breadcrumbs show where you are (WCAG 1.3.1)
The fix: Put the trail in a nav with its own name, as an ordered list, and say in the code which crumb is the current page. Either keep the last crumb and add aria-current="page" to it, drawing its look from that attribute, or end the trail at the section above and let the page's heading name the page.
Test with: screen reader, keyboard, forced colors
References: https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html https://www.w3.org/WAI/WCAG22/Techniques/html/H97 https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/ https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-current https://design-system.service.gov.uk/components/breadcrumbs/ https://www.aaron-gustafson.com/notebook/identifying-accessibility-data-gaps-in-codegen-models/

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

A shop's category page for mugs opens with a breadcrumb trail: Home / Kitchen / Mugs. The design is tidy and familiar. Home and Kitchen are blue underlined links, and Mugs, the page you are on, is grey with no underline, so a sighted shopper who arrives from a search engine sees at once that they are in Mugs, under Kitchen, and can go up a level with one click.

A screen reader user pressing Tab through the same page hears "Home, link", "Kitchen, link", "Mugs, link". Nothing says that Mugs is the page they are on, or that the three links are a trail at all. Reading line by line, the slashes between the crumbs can be read out too. The trail is not announced as navigation and doesn't appear in the screen reader's list of landmarks, so it can't be found by jumping to it; it is only three more links among the header's.

## Why it fails

The trail shows relationships: its links are a set for moving around the site, they run in order from the home page down to this one, and the last of them is the page being shown. WCAG 1.3.1 Info and Relationships requires information shown by presentation to be available in the code, or in the text. Here the grouping is only in the layout, because the trail is a `div`, which groups nothing for assistive technology; technique H97 meets 1.3.1 by putting related links in a `nav`. The order is only in the line of text, with no list to say how many crumbs there are. And the current page is only in the look: the `current` class turns Mugs grey and means nothing to the browser.

## Who is affected

Breadcrumbs sit on nearly every shop category, product page, help centre and documentation site, and they usually come from one shared component, so one fix reaches every page. They matter most on pages people reach from a search engine or a shared link, where the trail is the quickest way to find out where they have landed, and on large sites with several levels.

## What automation and AI miss

A scanner finds three links with names and passes them. It can't tell that one is drawn as current, because a class called `current` or `active` means nothing to it, and it can't tell that a row of links in a `div` is a trail. EaseWeb's audit reports a crumb that links to the page being shown, in a trail named so by its class or name, that is drawn unlike the crumbs beside it and has neither `aria-current` nor text saying it is current. A trail outside any navigation landmark goes to a person as a review lead, with its links. AI code generators don't reliably fill the gap: Aaron Gustafson's tests of GPT-4, published in 2025, found it writing breadcrumb trails with no name on the `nav` and no `aria-current="page"`. Listening to the trail on two pages is the only way to confirm it says where you are.

## Before: a trail of loose links, the last one marked by a class

```html
<div class="breadcrumbs">
  <a href="/">Home</a>
  <span class="sep">/</span>
  <a href="/kitchen">Kitchen</a>
  <span class="sep">/</span>
  <a href="/kitchen/mugs" class="current">Mugs</a>
</div>
<h1>Mugs</h1>
```

```css
.breadcrumbs {
  display: flex;
  gap: 0.5rem;
}
.breadcrumbs .sep {
  color: #5b6670;
}
.breadcrumbs .current {
  color: #5b6670;
  text-decoration: none;
}
```

The `current` class gives Mugs its look and nothing else, and the `div` groups nothing. A screen reader reads "Home, link", "Kitchen, link", "Mugs, link", with the slashes between them at some punctuation settings.

## After: a named nav, a list and aria-current

```html
<nav class="breadcrumbs" aria-label="Breadcrumb">
  <ol>
    <li>
      <a href="/">Home</a>
    </li>
    <li>
      <a href="/kitchen">Kitchen</a>
    </li>
    <li>
      <a href="/kitchen/mugs" aria-current="page">Mugs</a>
    </li>
  </ol>
</nav>
<h1>Mugs</h1>
```

```css
.breadcrumbs ol {
  display: flex;
  gap: 0.5rem;
  list-style: none;
  padding: 0;
}
.breadcrumbs li + li::before {
  content: "";
  display: inline-block;
  height: 0.8em;
  margin-right: 0.5rem;
  border-right: 1px solid #5b6670;
  transform: rotate(15deg);
}
.breadcrumbs [aria-current="page"] {
  color: #5b6670;
  text-decoration: none;
}
```

The design is unchanged: every crumb looks as it was drawn. The trail is now a `nav` with its own name, so a screen reader announces "Breadcrumb, navigation" on the way in and lists it among its landmarks, and an ordered list, read as "list, 3 items" from the top down. `aria-current="page"` on Mugs makes screen readers say "current page" there, and the grey now selects on the attribute, so the look and the code can't mark different crumbs. The separator is a thin rotated line drawn by a border, with no text for a screen reader to read.

## After: a trail that ends at the section above

```html
<nav class="breadcrumbs" aria-label="Breadcrumb">
  <ol>
    <li>
      <a href="/">Home</a>
    </li>
    <li>
      <a href="/kitchen">Kitchen</a>
    </li>
  </ol>
</nav>
<h1>Mugs</h1>
```

```css
.breadcrumbs ol {
  display: flex;
  gap: 0.5rem;
  list-style: none;
  padding: 0;
}
.breadcrumbs li + li::before {
  content: "";
  display: inline-block;
  height: 0.8em;
  margin-right: 0.5rem;
  border-right: 1px solid #5b6670;
  transform: rotate(15deg);
}
```

This changes the resting look: the trail stops at Kitchen, and the page's heading right under it names the page. Nothing in the trail is drawn as current, so there is no current page left to say in code, and nothing for the look and the code to disagree on. A screen reader reads "Breadcrumb, navigation", "list, 2 items", "Home, link", "Kitchen, link", and then the heading "Mugs". The GOV.UK Design System draws its breadcrumbs this way, ending with the parent section of the current page. The trail is still a named `nav` and a list, which is what makes it a trail for assistive technology.

## Which option to use

Both pass 1.3.1. Keep the last crumb, with `aria-current="page"`, when the design shows it, or when the heading under the trail is far away or worded differently from the crumb: the design stays as drawn, and the look comes from the attribute. End the trail at the section above when the heading sits right under it and says the same thing: every crumb is then a link that goes somewhere, and there is no state to keep in step.

## Implementation decisions

The trail around the fix has a few more choices:

- A last crumb in plain text: the ARIA Authoring Practices example keeps the last crumb as a link with `aria-current="page"`, as here. A crumb in plain text with `aria-current="page"` passes too, and saves a link that only reloads the page.
- Hidden text instead of the attribute: where a theme or a content system lets you change a crumb's text but not its attributes, add visually hidden text such as "(current page)" inside it, as [navigation that marks the current page only by its look](/learn/current-page) explains for menus.
- Separators: draw them in CSS with no text, as a border or a background image, or put them in markup inside `aria-hidden="true"`. Text in `content`, such as `"/"` or `"›"`, is read by screen readers in most browsers.
- The name: "Breadcrumb" is enough, because the screen reader adds "navigation" itself; see [navigation landmarks that all sound the same](/learn/landmark-names).
- Lists: VoiceOver in Safari can drop the list role from a list whose markers CSS has removed, but it keeps it for a list inside a `nav`, as here. A trail kept outside a `nav` needs `role="list"` on its `ol`.
- Single-page apps: when a route change swaps the page without loading it, the trail must be rebuilt and `aria-current` moved with it; see [single-page app route changes that are never announced](/learn/route-changes).

Keep the current crumb visible in a Windows contrast theme. Forced colors give every link the same colour, so a crumb told apart only by its grey looks like the others; one that also has no underline, or is set in bold, still stands apart.

## Verify the fix

1. In the code or the browser's inspector, confirm the trail is a `nav` with its own name and an ordered list, that the last crumb has `aria-current="page"` or hidden text, or that the trail ends at the section above, that the current-page look comes from the same condition, and that the separators have no text.
2. Open the screen reader's landmarks list and confirm the trail is listed by its name, apart from the site's main menu.
3. Read the trail with the screen reader and confirm the current page is announced as current, or that the trail ends at the section above and the page's heading follows, and that no separator is read.
4. Go to another page, deeper in the site and back up through the trail, and confirm both the look and the announcement have moved to the new page.
5. Turn on a Windows contrast theme and confirm the current crumb still looks different from the links before it.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the shared breadcrumb component changes.

## Limits

This is a starting pattern, not a guarantee for every site. Screen readers word `aria-current` and landmarks differently, and how they read punctuation depends on their settings; test with the ones your visitors use. A trail only helps when it follows the site's real structure: one that changes with how someone arrived, or skips a level, leaves people as lost as none. The page links at the end of a long list have failures of their own; see [pagination links that don't say which page or where they go](/learn/pagination). The site's main menu marks its current page with the same attribute; see [navigation that marks the current page only by its look](/learn/current-page).

---

Get this guide in your coding agent: MCP https://easeweb.dev/mcp (setup at https://easeweb.dev/services/developers)
