Long lists cut into honest, numbered pages.
| Prop / event | Type | Notes |
|---|---|---|
| page / total | number / number | Current page and page count, both 1-based |
| onChange | (page) => void | Fires with the requested page |
| variant | numbered · compact · simple · page-size | Numbered is the default |
| pageSize / onPageSizeChange | number / (n) => void | Rows-per-page select; resets to page 1 and re-derives the range |
| size | S · M · L | Item scale; M is the default |
| siblings | number | Pages kept either side of current before an ellipsis |
| ← / → | — | Prev / next; both disable at the ends |
Pagination moves through pages of one list. For peer views of one item use Tabs; for endless feeds prefer a Loader with infinite scroll.
Collapse long ranges. Keep the first, last and a window around current.
Don't print every page. A twenty-item row is a wall, not a control.
Disable Previous on page one. Dead-end controls tell the truth.
Never highlight two pages. Exactly one current page, always.
Numbered pagination sits below a data table, right or centre aligned.
The compact variant saves width in dense headers and mobile.
Two ghost buttons when the exact page number does not matter.
| Token | Value | Role |
|---|---|---|
| --pag-size | 34px | Item hit area (28 / 40 at S / L) |
| --pag-radius | 8px | Item corner |
| --pag-current | #FF7A00 @14% | Current page tint + #FF9D45 text |
| --pag-hover | #16171B | Hover fill on idle items |
| --pag-ellipsis | #565A62 | Range gap glyph |
| --pag-disabled | 0.32 opacity | Chevrons at the ends |
<nav class="o-pag" aria-label="Pagination"> <button class="o-pag__item" aria-label="Previous">‹</button> <button class="o-pag__item">1</button> <span class="o-pag__ellipsis">…</span> <button class="o-pag__item is-current" aria-current="page">4</button> <button class="o-pag__item">5</button> <span class="o-pag__ellipsis">…</span> <button class="o-pag__item">12</button> <button class="o-pag__item" aria-label="Next">›</button> </nav>