Pagination
FreePage-level navigation for traversing multi-page data sets. Displays numbered page buttons, previous/next arrows, and optional ellipsis for truncated ranges. Always communicates the current page and total pages to assistive technology.
6 variants
Variants
| Variant | Description | When to use |
|---|---|---|
| Default | Numbered page buttons with previous/next arrows and ellipsis truncation. | Use for paginated lists, tables, and search results with known page counts. |
| Compact | Simplified pagination showing only previous/next buttons and "Page X of Y" text. | Use in space-constrained contexts like mobile views or card footers. |
| With Input |
Usage Guidelines
Wrap in <nav aria-label="Pagination"> for landmark identification.
Use pagination for fewer than 2 pages — hide it when all results fit on one page.
Mark the current page with aria-current="page".
Remove the current page indicator — users need to know where they are.
Disable previous/next buttons at the bounds rather than hiding them.
Auto-paginate without user action — that pattern is infinite scroll.
Show total page count or result count for orientation.
Use pagination for sequential content like articles — use previous/next links instead.
Code
<nav class="lex-pagination" aria-label="Pagination">
<button class="lex-pagination__btn lex-pagination__btn--prev"
aria-label="Previous page" disabled>
<svg aria-hidden="true"><!-- chevron left --></svg>
</button>
<ol class="lex-pagination__list">
<li><button class="lex-pagination__page" aria-current="page">1</button></li>
<li><button class="lex-pagination__page">2</button></li>
<li><button class="lex-pagination__page">3</button></li>
<li><span class="lex-pagination__ellipsis" aria-hidden="true">…</span></li>
<li><button class="lex-pagination__page">12</button></li>
</ol>
<button class="lex-pagination__btn lex-pagination__btn--next"
aria-label="Next page">
<svg aria-hidden="true"><!-- chevron right --></svg>
</button>
</nav>.lex-pagination {
display: flex;
align-items: center;
gap: var(--lex-space-4);
}
.lex-pagination__list {
display: flex;
align-items: center;
gap: var(--lex-space-2);
list-style: none;
padding: 0;
margin: 0;
}
.lex-pagination__page {
min-width: 36px;
height: 36px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: var(--lex-radius-md);
border: none;
background: transparent;
color: var(--lex-text-muted);
font-size: var(--lex-font-size-sm);
cursor: pointer;
transition: background 150ms ease, color 150ms ease;
}
.lex-pagination__page:hover {
background: var(--lex-bg-surface-hover);
color: var(--lex-text-default);
}
.lex-pagination__page[aria-current="page"] {
background: var(--lex-bg-brand);
color: var(--lex-text-on-brand);
font-weight: 600;
}
.lex-pagination__btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}// Using Lexicon CSS classes with React
export function ResultsPagination() {
const [page, setPage] = useState(1);
return (
<Pagination
currentPage={page}
totalPages={12}
onPageChange={setPage}
/>
);
}Design Tokens
| Token | Value (dark) | Value (light) | CSS property |
|---|---|---|---|
| --lex-pagination-btn-size | — | — | size |
| --lex-pagination-btn-bg | — | — | bg |
| --lex-pagination-btn-active-bg | — | — | bg |
| --lex-pagination-gap | — | — | gap |
Accessibility
- Wrap in <nav aria-label="Pagination"> as a distinct navigation landmark.
- Current page uses aria-current="page" for screen reader identification.
- Previous/next buttons use descriptive aria-labels, not just arrow icons.
- Ellipsis elements have aria-hidden="true" since they carry no semantic meaning.
- Disabled buttons at bounds use the disabled attribute rather than aria-disabled for simplicity.