ExtrasPagination

Page links from a nav with an ordered list, with a current page, disabled edges and no script.

On this page

Pagination is a nav with an ol of links. The current page is aria-current="page", and a disabled edge is a span with role="link" and aria-disabled="true". No class and no JavaScript are needed, because the server normally knows the current page.

Load css/extras/pagination.min.css, or the whole set from css/pico.extras.min.css. See the overview.

Basic pagination

The hook is aria-label="Pagination" on the nav (the match ignores case). Page numbers and ellipses, which are the first, last, current and neighboring pages, come from your app logic. An ellipsis is an li with aria-hidden="true".

Pagination
<nav aria-label="Pagination">
  <ol>
    <li><a href="?page=1" rel="prev">Previous</a></li>
    <li><a href="?page=1" aria-label="Page 1">1</a></li>
    <li><a href="?page=2" aria-label="Page 2" aria-current="page">2</a></li>
    <li><a href="?page=3" aria-label="Page 3">3</a></li>
    <li aria-hidden="true">&hellip;</li>
    <li><a href="?page=10" aria-label="Page 10">10</a></li>
    <li><a href="?page=3" rel="next">Next</a></li>
  </ol>
</nav>

With a results summary

Put the summary and the nav in one flex row, summary at the start and pages at the end. The row wraps on a narrow screen.

Pagination with a summary
<div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--pico-spacing)">
  <p style="margin: 0; color: var(--pico-muted-color)">Showing 11 to 20 of 96 invoices</p>
  <nav aria-label="Pagination" style="margin: 0">
    <ol>
      <li><a href="?page=1" rel="prev">Previous</a></li>
      <li><a href="?page=1" aria-label="Page 1">1</a></li>
      <li><a href="?page=2" aria-label="Page 2" aria-current="page">2</a></li>
      <li><a href="?page=3" aria-label="Page 3">3</a></li>
      <li><a href="?page=3" rel="next">Next</a></li>
    </ol>
  </nav>
</div>

Disabled edges

On the first page, the previous item is not a link, because there is nowhere to go. Use a span with role="link" and aria-disabled="true". It stays in the reading order, dimmed and unclickable.

First page
<nav aria-label="Team members pages" data-pagination>
  <ol>
    <li><span role="link" aria-disabled="true">Previous</span></li>
    <li><a href="?page=1" aria-label="Page 1" aria-current="page">1</a></li>
    <li><a href="?page=2" aria-label="Page 2">2</a></li>
    <li><a href="?page=3" aria-label="Page 3">3</a></li>
    <li aria-hidden="true">&hellip;</li>
    <li><a href="?page=10" aria-label="Page 10">10</a></li>
    <li><a href="?page=2" rel="next">Next</a></li>
  </ol>
</nav>

Another label

The default hook is aria-label="Pagination". When the label says something else, so that a page with several lists can tell them apart, add data-pagination to the nav. The first example above uses the label, this one uses the attribute.

Icon-only previous and next

Hide the arrow with aria-hidden="true" and name the link with aria-label. Arrows on a link with rel flip in right to left pages.

Icon pagination
<nav aria-label="Projects pages" data-pagination>
  <ol>
    <li>
      <a href="?page=1" rel="prev" aria-label="Previous page"><span aria-hidden="true">&larr;</span></a>
    </li>
    <li><a href="?page=1" aria-label="Page 1">1</a></li>
    <li><a href="?page=2" aria-label="Page 2" aria-current="page">2</a></li>
    <li><a href="?page=3" aria-label="Page 3">3</a></li>
    <li>
      <a href="?page=3" rel="next" aria-label="Next page"><span aria-hidden="true">&rarr;</span></a>
    </li>
  </ol>
</nav>

Size

Set --pico-pagination-item-size on the nav for a compact or large list.

Compact pagination
<nav aria-label="Compact pages" data-pagination style="--pico-pagination-item-size: 1.75rem">
  <ol>
    <li>
      <a href="?page=1" rel="prev" aria-label="Previous page"><span aria-hidden="true">&larr;</span></a>
    </li>
    <li><a href="?page=1" aria-label="Page 1">1</a></li>
    <li><a href="?page=2" aria-label="Page 2" aria-current="page">2</a></li>
    <li><a href="?page=3" aria-label="Page 3">3</a></li>
    <li>
      <a href="?page=3" rel="next" aria-label="Next page"><span aria-hidden="true">&rarr;</span></a>
    </li>
  </ol>
</nav>

Updating without navigation

If the list changes without a page load, move aria-current from script. The page numbers themselves still come from your app.

JavaScript
const nav = document.querySelector('nav[aria-label="Pagination"]');
function setCurrent(page) {
  nav.querySelectorAll('a[aria-current="page"]').forEach((a) => a.removeAttribute("aria-current"));
  const next = nav.querySelector('a[aria-label="Page ' + page + '"]');
  if (next) next.setAttribute("aria-current", "page");
}

Accessibility

  • The nav needs a label, and each list on a page needs its own.
  • Name number links with aria-label="Page 2", and mark the current one with aria-current="page".
  • Use rel="prev" and rel="next" on the edge links.
  • The current page keeps a border, so it still shows under forced colors.
  • Hide ellipses from assistive technology with aria-hidden="true".

Customization

--pico-pagination-item-size
2.25rem
Minimum width and height of each item.
--pico-pagination-gap
0.25rem
Space between items.