Flux 2.0

Component

Pagination

Range text and page links for a long list that is served one page at a time.

Plain-text source, for agents and clipboard use: /components/source/pagination.txt

When to use it

Splits a long, server-side result into pages and says which part is showing: 26–50 of 312. The page links are real links, so a page can be bookmarked, opened in a new tab and reached with the back button. Previous and Next stay in place and disable at the ends rather than disappearing, and a long run of pages collapses around the current one with gaps.

Not this
Not for a list short enough to show in full. Not for a feed read top to bottom; load more on request instead. Not for client-side tables of a few hundred rows, which can scroll.

Examples

Below a table

The pagination sits after the table wrapper, which spaces it automatically. Pages near the current one are shown, with the first and last.

Bookings, page 2 of 13
Reference Departure Pax
KB-104953Golden Circle, 21 Sep6
KB-104954South Coast, 22 Sep2
KB-104957Snæfellsnes, 22 Sep11
html
<div class="flux-table-wrap">
  <table class="flux-table">
    <caption class="flux-visually-hidden">Bookings, page 2 of 13</caption>
    <thead>
      <tr>
        <th scope="col">Reference</th>
        <th scope="col">Departure</th>
        <th scope="col" data-numeric>Pax</th>
      </tr>
    </thead>
    <tbody>
      <tr><th scope="row">KB-104953</th><td>Golden Circle, 21 Sep</td><td data-numeric>6</td></tr>
      <tr><th scope="row">KB-104954</th><td>South Coast, 22 Sep</td><td data-numeric>2</td></tr>
      <tr><th scope="row">KB-104957</th><td>Snæfellsnes, 22 Sep</td><td data-numeric>11</td></tr>
    </tbody>
  </table>
</div>
<nav class="flux-pagination" aria-label="Pagination">
  <p class="flux-pagination__range">26–50 of 312</p>
  <ul class="flux-pagination__list">
    <li><a class="flux-pagination__link" href="#"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 4l-4 4 4 4" /></svg>Previous</a></li>
    <li><a class="flux-pagination__link flux-pagination__page" href="#" aria-label="Page 1">1</a></li>
    <li><a class="flux-pagination__link flux-pagination__page" href="#" aria-label="Page 2" aria-current="page">2</a></li>
    <li><a class="flux-pagination__link flux-pagination__page" href="#" aria-label="Page 3">3</a></li>
    <li><span class="flux-pagination__gap" aria-hidden="true">…</span></li>
    <li><a class="flux-pagination__link flux-pagination__page" href="#" aria-label="Page 13">13</a></li>
    <li><a class="flux-pagination__link" href="#">Next<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4" /></svg></a></li>
  </ul>
</nav>

Previous and next only

For results served by cursor, where the total is unknown or expensive to count. On the first page, Previous is disabled rather than removed, so Next does not move.

html
<nav class="flux-pagination" aria-label="Pagination">
  <p class="flux-pagination__range">1–25</p>
  <ul class="flux-pagination__list">
    <li><span class="flux-pagination__link" aria-disabled="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 4l-4 4 4 4" /></svg>Previous</span></li>
    <li><a class="flux-pagination__link" href="#">Next<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4l4 4-4 4" /></svg></a></li>
  </ul>
</nav>

Anatomy

PartDescription
NavigationA nav with aria-label="Pagination" and the flux-pagination class.
RangeWhich records are showing and how many there are, in text-secondary with tabular figures.
Previous and NextLinks with an arrow and a word. At an end they become disabled spans.
PageA link per page in mono. The current page is selected and carries aria-current="page".
GapAn ellipsis standing for the pages left out.

API

NameKindDescription
flux-paginationclassOn the nav. Give it aria-label="Pagination".
flux-pagination__rangeclassThe range text.
flux-pagination__listclassOn the ul of links.
flux-pagination__linkclassOn every link, and on the span that replaces a disabled Previous or Next.
flux-pagination__pageclassAdds mono figures to a page number link. Label it "Page n".
flux-pagination__gapclassThe ellipsis for omitted pages, with aria-hidden.
aria-current="page"attributeOn the current page’s link.
aria-disabled="true"attributeOn the span for Previous or Next at an end.

States

StateTreatment
Defaulttext-primary on no fill.
Hoversurface-hover fill.
Currentsurface-selected fill, text-brand, mono bold.
Disabledtext-disabled, not focusable, not a link.

Keyboard

KeysAction
TabMoves through the links. Disabled ends and gaps are skipped.
EnterOpens the page.

Accessibility

  • Label the nav "Pagination", and each number "Page n", so a list of links reads as pages.
  • The current page carries aria-current="page", so it is not conveyed by fill alone.
  • Update the table caption with the page number, so the change is clear after the page loads.
  • Move focus to the top of the results after a page change that does not reload the page.

Tokens

The tier-3 and tier-2 tokens this component binds to. Change the token, not the component.

  • --flux-pagination-gap | -item-size | -item-text | -item-hover
  • --flux-pagination-current-bg | -current-text
  • --flux-pagination-range

Do and don’t

Do

  • Show the first page, the last page and the pages either side of the current one.
  • Keep the page in the URL so the back button returns to it.

Don’t

  • Do not hide Previous or Next at an end: disable it.
  • Do not use buttons for pages when a URL can identify each one.
  • Do not shrink row height to fit more rows on a page.