Pagination
Pagination — links to the pages of a paged result.
Examples
<nav class="pagination" aria-label="Pagination">
<ol class="pagination-list">
<li>
<a class="btn" href="#page-3" rel="prev">
<svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg>
Previous
</a>
</li>
<li><a class="btn btn-quiet pagination-page" href="#page-1">1</a></li>
<li aria-hidden="true" class="pagination-ellipsis">…</li>
<li><a class="btn btn-quiet pagination-page" href="#page-3">3</a></li>
<li><a class="btn btn-quiet pagination-page" href="#page-4" aria-current="page">4</a></li>
<li><a class="btn btn-quiet pagination-page" href="#page-5">5</a></li>
<li>
<a class="btn" href="#page-5" rel="next">
Next
<svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg>
</a>
</li>
</ol>
</nav>Every control is a .btn, so the touch floor, the focus ring and the disabled treatment are the button’s and cannot drift.
.pagination-page is only there to make a number square — a .btn is sized by its label, so "1" and "10" would differ.
<nav class="pagination" aria-label="Pagination">
<ol class="pagination-list">
<li>
<button class="btn" type="button" disabled>
<svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg>
Previous
</button>
</li>
<li><a class="btn btn-quiet pagination-page" href="#page-1" aria-current="page">1</a></li>
<li><a class="btn btn-quiet pagination-page" href="#page-2">2</a></li>
<li>
<a class="btn" href="#page-2" rel="next">
Next
<svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg>
</a>
</li>
</ol>
</nav>The dead end is a <button disabled>, not an <a aria-disabled="true">. Without JavaScript the second one still navigates: it announces a state it does not have.
<nav class="pagination" aria-label="Search results pages">
<ol class="pagination-list">
<li>
<a class="btn btn-icon" href="#page-3" rel="prev" aria-label="Previous page">
<svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg>
</a>
</li>
<li><a class="btn btn-quiet pagination-page" href="#page-1">1</a></li>
<li aria-hidden="true" class="pagination-ellipsis">…</li>
<li><a class="btn btn-quiet pagination-page" href="#page-3">3</a></li>
<li><a class="btn btn-quiet pagination-page" href="#page-4" aria-current="page">4</a></li>
<li><a class="btn btn-quiet pagination-page" href="#page-5">5</a></li>
<li aria-hidden="true" class="pagination-ellipsis">…</li>
<li><a class="btn btn-quiet pagination-page" href="#page-24">24</a></li>
<li>
<a class="btn btn-icon" href="#page-5" rel="next" aria-label="Next page">
<svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg>
</a>
</li>
</ol>
</nav>No new class: dropping the words is .btn-icon, which the button already ships, and the whole variant is two aria-labels.
Those labels are not optional and nothing detects their absence — an icon-only control with no name announces as "link", and a pagination bar of two anonymous links and eight numbers is unusable by anyone listening. The name goes on the control, never on the <svg>: an aria-label on a decorative graphic is ignored, and one on a <use> is ignored twice over. Chevrons alone are the right choice on a narrow layout and the wrong one on a wide search-results page, where "Previous" and "Next" are both clearer and larger targets.
Tokens 7
Level 2, declared on .pagination itself. Set any of them on that selector to restyle this component without touching the skin.
| Token | Default |
|---|---|
| --pagination-cell-size | 2.5rem |
| --pagination-current-bg | var(--color-primary) |
| --pagination-current-color | var(--color-on-primary) |
| --pagination-current-font-weight | var(--font-weight-semibold) |
| --pagination-ellipsis-color | var(--color-on-surface-muted) |
| --pagination-ellipsis-font-size | var(--font-size-sm) |
| --pagination-gap | var(--space-1) |
Variants and states
Variants
None. It is one shape, and the page does the rest.
Inside it
.pagination-page.pagination-ellipsis.pagination-list
State it reads
None.
Before you ship it
What you have to do 1 requirements
- Name the nav:
aria-label="Pagination". A page that also has a primary nav otherwise announces two identical landmarks. Mark the current page witharia-current="page"on its anchor, and mark exactly one. It is the accessible signal as well as the visual one, and this file reads it straight from the platform — there is no.is-currentclass to keep in sync, deliberately. Keep the ellipsisaria-hidden="true". It is a typographic hint that some numbers were left out; announced, it is a stray "…" between two links. Give the previous/next controls a name that survives out of context if you replace their text with an icon: the label goes on the control (aria-label="Previous page"), never on the<svg>, which isaria-hidden.rel="prev"andrel="next"are worth adding and are not styled here.
src/css/components/pagination.css · npx mostlycss add pagination