Design system Pickers and overlays
Pagination
Numbered pages in a table or list footer. Previous and Next stay ordinary small ghost buttons; only the numbers wear the square face.
.mk-pagination.mk-page-btn.mk-page-gap
Overview
The page button is one numbered page in a table or list footer. Only the number wears this compact square face — Previous and Next stay ordinary small ghost buttons, because they are actions with words, not positions in a sequence.
Usage
- Navigate fixed pages of one collectionPlace pagination after a list or table when the complete result set is divided into stable numbered pages.
- Expose position and relative movementThe current page is read-only; numbered pages move to an absolute position, while Previous and Next move one page.
- Compress long page setsKeep the first and last page visible and replace omitted runs with a non-interactive ellipsis. Use a stepper or progress pattern for stages in a process.
Anatomy
- 1Relative moves. Previous and Next are small Quiet buttons with words. They move one slice and never masquerade as numbered square faces.
- 2Page face. One 32px square for one absolute position; the surrounding row supplies the 44px target floor. Rest, hover and focus change this same face.
- 3Current page. Exactly one face carries
aria-current="page", surface fill, camel border and stronger weight. It states position and is not pressable. - 4Ellipsis. A silent, non-interactive gap for omitted pages. First and last positions stay visible around it; the gap is never a jump button.
Variants and states
No sizes, no tiers — pagination is one job at one density.
States
Every state below is the real component wearing that state, not a picture of it.
| State | Specimen | What changes |
|---|---|---|
| Rest | A bare number in the muted ink on a transparent square. | |
| Hover | A translucent ink wash darkens the square one step on whatever ground the footer sits on — white table foot or grey widget alike. | |
| Keyboard focus | The standard 2px camel outline at 2px offset. | |
| Current | Surface fill, camel border, weight 600, and the cursor goes default — the current page is a fact, not an action. | |
| At the start | The edges never leave: the last page stays one press away. At the edge
Previous takes aria-disabled — reachable, refusing,
like every blocked action. |
|
| Mid-set | The current page keeps one neighbour on each side; the ellipsis stands for everything elided. On a narrow screen the same grammar keeps fewer neighbours — the square never shrinks below its 32px face or its 44px target. | |
| At the end | The mirror of the start: Next blocks, the first page stays pressable. |
Grounds
The white surface is the reference face above. When pagination sits directly on the page ground, its geometry and states do not change; this comparison is kept here as contextual evidence rather than competing with the overview.
- The gap is not a targetThe ellipsis is
aria-hiddensilence in the quiet ink — never a button. Jumping into the middle is what the footer's number field is for, where a list earns one.
Where the mobile form of a table shows every match, its pagination is deferred with the mobile pass — the component does not improvise a smaller self.
Do and don't
Numbers are squares; the relative moves keep their words and their ghost faces.
Chevron glyphs dressed as page numbers make the moves and the positions one undifferentiated row of squares.
Copy
- The number is the labelNothing is added to it — no "page", no punctuation.
- The moves speakPrevious and Next, full words, sentence case.
Accessibility
- State, not styleThe current page is
aria-current="page"; the visual face follows the attribute. - A nav landmarkThe footer's pagination sits in a
<nav aria-label="Pages">, so it is one stop, not seven. - Targets44px vertically through the expander; the squares sit close but never share a press.
When to reach for something else
- Moving, not jumpingPrevious and Next are ordinary ghost buttons — they carry words and act relatively.
- Sections, not pagesSteps of a flow are navigation, not pagination. The number here is a slice of one list, never a stage of a journey.
Markup
<nav aria-label="Pages"> <button class="mk-btn mk-btn-ghost mk-btn-s mk-btn-inline" type="button">Previous</button> <button class="mk-page-btn" type="button">1</button> <button class="mk-page-btn" type="button" aria-current="page">2</button> <button class="mk-page-btn" type="button">3</button> <button class="mk-btn mk-btn-ghost mk-btn-s mk-btn-inline" type="button">Next</button> </nav> <!-- a long set: the ellipsis is a silent seat, never a button --> <nav class="mk-pagination" aria-label="Pages"> <button class="mk-btn mk-btn-ghost mk-btn-s mk-btn-inline" type="button">Previous</button> <button class="mk-page-btn" type="button">1</button> <span class="mk-page-gap" aria-hidden="true">…</span> <button class="mk-page-btn" type="button">49</button> <button class="mk-page-btn" type="button" aria-current="page">50</button> <button class="mk-page-btn" type="button">51</button> <span class="mk-page-gap" aria-hidden="true">…</span> <button class="mk-page-btn" type="button">100</button> <button class="mk-btn mk-btn-ghost mk-btn-s mk-btn-inline" type="button">Next</button> </nav>