Accessible Breadcrumbs and Pagination
AI generated
A11Y
WCAG
Accessibility · Navigation
Accessible Breadcrumbs and Pagination
Why most breadcrumb and pagination components are half-heartedly marked up, and how to get them right

A breadcrumb bar built from nested div and span elements with separators looks correct to sighted users, but delivers no information whatsoever to a screen reader that it's a navigation aid with a clear position in the page hierarchy. This article covers how nav with aria-label, aria-current, a semantically correct list structure, and accessible pagination controls create real, usable navigation for screen reader users, with complete practical examples from Hyva.

12 min read Breadcrumbs Pagination

Both breadcrumbs and pagination controls are visually simple to build, a few links with separators or a row of number buttons, which is exactly why they frequently end up built with generic div and span elements instead of the semantic HTML elements meant for the job. For sighted users such a purely visual implementation usually works fine, but for screen reader users, all structural information indicating this is a navigation component in the first place is missing.

The problem is compounded by the fact that both components matter especially for orientation within a large Magento catalog with a deep category hierarchy and long product lists. Screen reader users in particular, who take in a page serially rather than at a glance, benefit disproportionately from a clearly marked position in the hierarchy and an unambiguous announcement of the current page number.

A breadcrumb bar semantically belongs inside its own nav element, since it represents a standalone navigation option within the page, independent of the main navigation. Since a page often contains several nav elements, say main navigation, breadcrumb, and footer navigation, a distinct aria-label like Breadcrumb is essential so screen reader users can tell the individual navigation regions apart while switching between landmarks.

Without that aria-label, most screen readers announce every nav element merely as navigation, with no discernible difference between the main menu and the breadcrumb, which effectively renders landmark navigation, one of the most important orientation aids for experienced screen reader users, useless.

3. Using aria-current page correctly

The last item in a breadcrumb bar typically represents the currently displayed page and should therefore no longer be a clickable link, but plain text carrying the attribute aria-current equal to page. That attribute tells assistive technology it represents the current position within a series of related items, and most screen readers additionally announce it acoustically as current alongside the text content.

A common mistake is keeping the last breadcrumb item as a link pointing to the very page currently displayed, which is functionally redundant and, without aria-current, also throws away the information that it represents the current position. The correct approach is a plain span or text element for the last segment, while all preceding segments remain regular, clickable links to the parent categories.

4. Semantically correct list structure for breadcrumbs

Within the nav element, the individual breadcrumb segments should be marked up as an ordered list with ol and li, not as a loose sequence of links with separators in between. A list additionally conveys the total number of items and the position of the currently focused item within the list to screen reader users, say item 3 of 4, information that's completely missing from a plain link sequence without list structure.

Visual separators such as slashes or arrows between breadcrumb items should be inserted exclusively via CSS pseudo-elements, never as real text content in the DOM, since a screen reader would otherwise read out the separator itself alongside every item, needlessly bloating the acoustic navigation experience.

5. Accessible pagination controls: the base structure

Similar to breadcrumbs, a pagination component also belongs inside its own nav element with a distinct aria-label like Pagination, since it represents a standalone, recurring navigation option within the page. The individual page numbers should likewise be marked up as a list, with the currently displayed page carrying the same aria-current page pattern used for the last breadcrumb item, combined with a clear visual highlight.

Every link within the pagination should also carry its own precise aria-label going beyond the plain number, say Go to page 4 instead of just 4, since an isolated number without context carries little meaning for screen reader users moving through the list of links.

6. Previous and next buttons: aria-label instead of plain arrow icons

The buttons for the previous and next page frequently consist of nothing but an arrow icon, which without an additional text alternative produces exactly the same problem as other icon-only buttons: a screen reader announces only button, with no information about direction. A distinct aria-label like Go to previous page or Go to next page fixes this problem reliably.

A disabled previous button on the first page, or a disabled next button on the last page, should additionally be marked as unavailable via the disabled attribute or aria-disabled true, rather than being left in the DOM functionless but still visually appearing active, since a button that looks active but does nothing leads screen reader users into repeated, unsuccessful activation attempts.

7. Page number announcements for screen readers

For pagination updated via JavaScript without a full page reload, say on a category page with client-side reloaded product lists, the page change should additionally be announced via an aria-live region, since a purely visual change of the active page number would otherwise go unnoticed by screen reader users. A polite aria-live region with text like Showing page 3 of 12 is already enough for this.

It's important to implement this announcement as a separate element permanently present in the DOM with aria-live polite, updating only the text content on every page change instead of re-inserting the whole element, since many screen readers don't reliably pick up a newly inserted aria-live region, while a text change within an existing element works reliably.

8. Practical example: the breadcrumb component in Hyva

In a Hyva theme, the breadcrumb component can be implemented as a standalone phtml template with a complete nav, ol, and aria-current structure, with no JavaScript needed, since breadcrumbs are usually rendered server-side from the category and product hierarchy. The separators between items are generated exclusively via a CSS class using content slash as a pseudo-element.

The following excerpt shows the complete, semantically correct structure for a three-level breadcrumb bar going from the homepage through a category to the currently displayed product.


<nav aria-label="Breadcrumb">
  <ol class="flex flex-wrap items-center gap-2 text-sm">
    <li>
      <a href="/" class="hover:underline">Home</a>
    </li>
    <li class="before:content-['/'] before:mx-2 before:text-gray-400">
      <a href="/safety-shoes" class="hover:underline">Safety Shoes</a>
    </li>
    <li class="before:content-['/'] before:mx-2 before:text-gray-400">
      <span aria-current="page" class="text-gray-600">
        Safety Shoe S3, Size 43
      </span>
    </li>
  </ol>
</nav>

9. Practical example: the pagination component in Hyva

For pagination on a Hyva category page, the accessible implementation combines nav with aria-label, an ol list for the page numbers, aria-current page for the active page, and precisely labeled previous and next buttons with a correctly set disabled state at the edges of the page range.

If pagination is additionally updated client-side via Alpine.js, a hidden aria-live region supplements the announcement of the page change, as shown in the full excerpt below. The closing table compares the most important attributes for breadcrumbs and pagination at a glance.


<nav aria-label="Pagination" x-data="{ page: 3, total: 12 }">
  <div class="sr-only" role="status" aria-live="polite" x-text="'Showing page ' + page + ' of ' + total"></div>

  <ol class="flex items-center gap-1">
    <li>
      <button
        type="button"
        aria-label="Go to previous page"
        :disabled="page === 1"
        :aria-disabled="page === 1"
        class="px-3 py-2 disabled:opacity-40"
      >
        <svg class="w-4 h-4" aria-hidden="true"><!-- left arrow --></svg>
      </button>
    </li>
    <li>
      <a href="?p=3" aria-current="page" aria-label="Go to page 3" class="px-3 py-2 font-bold">3</a>
    </li>
    <li>
      <a href="?p=4" aria-label="Go to page 4" class="px-3 py-2">4</a>
    </li>
    <li>
      <button
        type="button"
        aria-label="Go to next page"
        :disabled="page === total"
        :aria-disabled="page === total"
        class="px-3 py-2 disabled:opacity-40"
      >
        <svg class="w-4 h-4" aria-hidden="true"><!-- right arrow --></svg>
      </button>
    </li>
  </ol>
</nav>
Attribute Used for Purpose Common mistake
nav aria-label Breadcrumb and pagination Distinguishing multiple nav elements on the page Missing aria-label, all nav elements sound the same
aria-current page Last breadcrumb item, active page number Marking the current position within a series Current item stays a clickable link
ol and li Breadcrumb segments, page numbers Conveying total count and position within the list Loose links with separator characters instead of a real list
aria-live polite Client-side updated pagination Making page changes audible for screen readers Element re-inserted instead of text updated

Mironsoft

WCAG audits, accessible Magento shops, and training

Not sure whether the shop is actually accessible?

We audit existing Magento shops against WCAG 2.2, fix concrete barriers in the Hyvä frontend, and train teams so accessibility stays anchored in the development process for good.

WCAG Audit

Systematically review the shop against WCAG 2.2 AA, with a prioritized issue list.

Fixing Barriers

Concrete implementation: keyboard operability, screen reader support, contrast, forms.

Team Training

Raise developer and editor awareness for accessible implementation day to day.

10. Summary

Accessible Breadcrumbs and Pagination: The Essentials at a Glance

Base rule

Both components belong inside their own nav element with a distinct aria-label.

Current position

aria-current page marks the last breadcrumb item and the active page number.

Structure

ol and li instead of loose links convey the total count and position within the list.

Dynamic updates

An aria-live polite region reliably announces client-side page changes.

11. FAQ: Accessible Breadcrumbs and Pagination: The Essentials at a Glance

1Why does a breadcrumb bar need its own aria-label?
Because a page can contain several nav elements, and screen readers otherwise can't tell them apart.
2Should the last breadcrumb item be a link?
No, it should be plain text with aria-current page, since it represents the currently displayed page.
3Why should breadcrumbs be marked up as ol instead of loose links?
A list additionally conveys the total count and the current item's position within the series.
4How should visual separators between breadcrumb items be implemented?
Exclusively via CSS pseudo-elements, never as real text content in the DOM.
5What should a pagination link's aria-label contain?
More than the plain number, say Go to page 4, so the link stays understandable without context.
6How should previous and next buttons be labeled?
With a distinct aria-label like Go to previous page instead of just an arrow icon.
7What happens to the previous button on the first page?
It should be marked unavailable via disabled or aria-disabled true, not just visually.
8When does pagination need an aria-live region?
When the page change happens via JavaScript without a full page reload.
9Why should the aria-live region stay permanently in the DOM?
Newly inserted aria-live regions aren't reliably picked up by many screen readers.
10Does a server-rendered Hyva breadcrumb need extra JavaScript?
No, the full semantic structure can be built purely server-side in the phtml template.