Discrete navigation through a bounded result set — where you are, in how many. One dense cell footprint at space.6 on a space.1 gap carries everything at the same font.size.copy-sm as SidebarNav rows — numbered pages as transparent ghost cells, icon-only chevron controls, and an inert ellipsis on the same grid.
Transient hover uses a light ghost tint, while the persistent current page uses selection.bg; a primary brand fill would misread navigation state as an action. The current page is marked by aria-current="page" and holds its fill against hover; arrows disable at the ends of the range — disabled, never hidden.
| Example | State | Driver | Roles | Usage |
|---|---|---|---|---|
| Rest | — | text.secondary | A reachable page — ghost until pointed at | |
| Hover | :hover | action.ghost.hover | A transient ghost tint, distinct from the held current state | |
| Focus | :focus-visible | focus.ring | The shared ring on the cell's radius.nested box | |
| Current | [aria-current] | selection.bgtext.primary | The page you are on — stays focusable, never disabled | |
| Disabled | :disabled | action.disabled.text | An arrow at the end of the range — present, so the row holds still |
A nav landmark labeled Pagination: first-page and previous-page controls, the page window, then next-page and last-page controls. Single chevrons step once; double chevrons jump to the range boundary. First and last pages always render, the window rides the current page, and the ellipsis stands for two or more hidden pages — never one — as inert aria-hidden text, not a control; the window math itself is layer 2.
The demo pages a real 10-page set — single chevrons step one page, double chevrons jump to the first or last page, and the window recomputes around aria-current. The window math here is demo chrome; layer 2 owns it.
A pagination states position; every cell keeps its place.
| Verdict | Rule |
|---|---|
| Do | Keep all four navigation controls icon-only with required aria-labels: double chevrons for first/last, single chevrons for previous/next. Disable boundary controls rather than hiding them, so the row holds still. |
| Do | Always render the first and last pages; an ellipsis replaces two or more hidden pages, never one — showing the page is shorter than eliding it. |
| Do | Keep aria-current="page" on the current cell and leave it focusable — never disable it. |
| Don’t | Make the ellipsis clickable — it is inert aria-hidden text on the cell grid; a jump-ahead control is layer 2. |
| Don’t | Paginate an unbounded feed — pagination is for bounded, ordered sets where position matters; a feed wants progressive loading. |