# Pagination

Pages for tables and lists: numbered keys in a tray with the current page latched down, seven slots so the row never changes width. Compact, it is a transport with a track counter.

Kit part · Free · https://tactileui.dev/parts/pagination

- Element: `<nav>`
- Needs: `kit.css` and `kit.mjs` ([how to install the kit](https://tactileui.dev/docs/installation#the-kit))
- Categories: Navigation, Paginations
- Finishes: Aluminium, Graphite, Oak, Walnut, Porcelain, Black, Daylight, Night, Paper, Phosphor, E-paper, AMOLED
- Volumes: Tactile, Quiet

## Variants

### Few pages · no gaps

```html
<nav class="tui-pagination" aria-label="Few pages" data-page="2" data-pages="5"><div class="tui-pagination__keys"><button class="tui-key tui-key--icon tui-pagination__step" type="button" data-page-step="-1" aria-label="Previous page" aria-disabled="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.8 3.8 5.6 8l4.2 4.2"/></svg></button><ol class="tui-pagination__pages"><li><button class="tui-key" type="button" data-page="1" aria-label="Page 1">1</button></li><li><button class="tui-key" type="button" data-page="2" aria-label="Page 2" aria-current="page">2</button></li><li><button class="tui-key" type="button" data-page="3" aria-label="Page 3">3</button></li><li><button class="tui-key" type="button" data-page="4" aria-label="Page 4">4</button></li><li><button class="tui-key" type="button" data-page="5" aria-label="Page 5">5</button></li></ol><button class="tui-key tui-key--icon tui-pagination__step" type="button" data-page-step="1" aria-label="Next page" aria-disabled="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.2 3.8 10.4 8l-4.2 4.2"/></svg></button></div></nav>
```

### Many pages · start

```html
<div style="zoom:.8"><nav class="tui-pagination" aria-label="Many pages" data-page="1" data-pages="40"><div class="tui-pagination__keys"><button class="tui-key tui-key--icon tui-pagination__step" type="button" data-page-step="-1" aria-label="Previous page" aria-disabled="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.8 3.8 5.6 8l4.2 4.2"/></svg></button><ol class="tui-pagination__pages"><li><button class="tui-key" type="button" data-page="1" aria-label="Page 1" aria-current="page">1</button></li><li><button class="tui-key" type="button" data-page="2" aria-label="Page 2">2</button></li><li><button class="tui-key" type="button" data-page="3" aria-label="Page 3">3</button></li><li><button class="tui-key" type="button" data-page="4" aria-label="Page 4">4</button></li><li><button class="tui-key" type="button" data-page="5" aria-label="Page 5">5</button></li><li class="tui-pagination__gap" aria-hidden="true">…</li><li><button class="tui-key" type="button" data-page="40" aria-label="Page 40">40</button></li></ol><button class="tui-key tui-key--icon tui-pagination__step" type="button" data-page-step="1" aria-label="Next page" aria-disabled="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.2 3.8 10.4 8l-4.2 4.2"/></svg></button></div></nav></div>
```

### Compact · transport and counter

```html
<nav class="tui-pagination tui-pagination--compact" aria-label="Takes, pages" data-page="7" data-pages="24"><div class="tui-pagination__keys"><button class="tui-key tui-key--icon tui-pagination__step" type="button" data-page-step="-1" aria-label="Previous page" aria-disabled="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.8 3.8 5.6 8l4.2 4.2"/></svg></button><span class="tui-lcd tui-pagination__lcd" data-ghost="88/88" aria-hidden="true">07/24</span><button class="tui-key tui-key--icon tui-pagination__step" type="button" data-page-step="1" aria-label="Next page" aria-disabled="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.2 3.8 10.4 8l-4.2 4.2"/></svg></button></div><span class="tui-pagination__said" aria-live="polite">Page 7 of 24</span></nav>
```

### Links · the server pages

```html
<nav class="tui-pagination" aria-label="Archive, pages"><div class="tui-pagination__keys"><a class="tui-key tui-key--icon" href="#variants" aria-label="Previous page"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.8 3.8 5.6 8l4.2 4.2"/></svg></a><ol class="tui-pagination__pages"><li><a class="tui-key" href="#variants" aria-label="Page 1">1</a></li><li><a class="tui-key" href="#variants" aria-label="Page 2" aria-current="page">2</a></li><li><a class="tui-key" href="#variants" aria-label="Page 3">3</a></li></ol><a class="tui-key tui-key--icon" href="#variants" aria-label="Next page"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.2 3.8 10.4 8l-4.2 4.2"/></svg></a></div></nav>
```

## Every state

- Rest
- Hover
- Current · at the end
- Focus

Hover, pressed and focus are forced with `.is-hover`, `.is-down` and `.is-focus`; checked and disabled are real.

## API

| Name | Kind | Values | What it does |
| - | - | - | - |
| .tui-pagination | class | on | The row: the keys, then the range and the rows-per-page, wrapping when narrow. |
| aria-label | attribute | on the nav | Name what is paged: “Circuits, pages”. |
| data-page · data-pages | attribute | numbers | The page you are on and how many there are. The part keeps data-page current. |
| data-count · data-size | attribute | numbers | Rows in all and rows per page, for the range and the size select. |
| .tui-pagination__keys | class | on | The tray: the two arrows and the numbered keys, 30px keys, 3px apart. |
| .tui-pagination__pages | class | on | Seven slots at most: first, last, the page and its neighbours, and “…” gaps. |
| data-page | attribute | on a | A number key. Links () are left to the server. |
| aria-current="page" | attribute | on the current key | Latched below the plate, its printing at full ink. |
| data-page-step · aria-disabled | attribute | "-1" · "1" | The arrows. At the ends they are aria-disabled, so focus is not lost. |
| .tui-pagination__range | class | on | Rewritten as “51–75 of 287”. |
| select[data-pagination-size] · .tui-pagination__size | attribute · class | in a label | Rows per page. Changing it keeps the first row you were reading on screen. |
| .tui-pagination--compact · .tui-pagination__lcd | class | with .tui-lcd | The transport: arrows round a counter, “03/12”, its ghost the same width. |
| .tui-pagination__said | class | aria-live | The words behind the counter, for screen readers: “Page 3 of 12”. |
| paginationItems · paginationKeysHTML · paginationRange | function | kit.mjs, pure | The slots, their markup, and the rows a page shows; for server rendering and tests. |
| setPaginationPage(nav, page) | function | kit.mjs | Go to a page from a script: clamps, redraws, fires tui-page. |
| tui-page | event | detail: page, pages, size | Fired on the nav after every change. |
| Tab · Enter · Space | keyboard | — | Every key is a real button. After a redraw, focus stays on the arrow or moves to the new page’s key. |

## Markup

```html
<nav class="tui-pagination" aria-label="Circuits, pages" data-page="3" data-pages="12" data-count="287" data-size="25">
  <div class="tui-pagination__keys">
    <button class="tui-key tui-key--icon tui-pagination__step" data-page-step="-1" aria-label="Previous page">…</button>
    <ol class="tui-pagination__pages">
      <li><button class="tui-key" data-page="1" aria-label="Page 1">1</button></li>
      <li><button class="tui-key" data-page="2" aria-label="Page 2">2</button></li>
      <li><button class="tui-key" data-page="3" aria-label="Page 3" aria-current="page">3</button></li>
      <li><button class="tui-key" data-page="4" aria-label="Page 4">4</button></li>
      <li><button class="tui-key" data-page="5" aria-label="Page 5">5</button></li>
      <li class="tui-pagination__gap" aria-hidden="true">…</li>
      <li><button class="tui-key" data-page="12" aria-label="Page 12">12</button></li>
    </ol>
    <button class="tui-key tui-key--icon tui-pagination__step" data-page-step="1" aria-label="Next page">…</button>
  </div>
  <p class="tui-pagination__range" aria-live="polite">51–75 of 287</p>
  <label class="tui-pagination__size"><span class="tui-label">Rows</span>
    <select class="tui-select" data-pagination-size><option>10</option><option selected>25</option><option>50</option></select>
  </label>
</nav>
```

```js
import { paginationKeysHTML } from '/kit/kit.mjs';

// Server-side, or first render: the same keys the part draws.
list.innerHTML = paginationKeysHTML(3, 12);

nav.addEventListener('tui-page', (e) => loadRows(e.detail.page, e.detail.size));
// Or go somewhere from a script:
import { setPaginationPage } from '/kit/kit.mjs';
setPaginationPage(nav, 1);
```

The demo on the page:

```html
<div style="display:grid;gap:28px;justify-items:center;width:min(100%,640px)">
  <nav class="tui-pagination" aria-label="Circuits, pages" data-page="3" data-pages="12" data-count="287" data-size="25"><div class="tui-pagination__keys"><button class="tui-key tui-key--icon tui-pagination__step" type="button" data-page-step="-1" aria-label="Previous page" aria-disabled="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.8 3.8 5.6 8l4.2 4.2"/></svg></button><ol class="tui-pagination__pages"><li><button class="tui-key" type="button" data-page="1" aria-label="Page 1">1</button></li><li><button class="tui-key" type="button" data-page="2" aria-label="Page 2">2</button></li><li><button class="tui-key" type="button" data-page="3" aria-label="Page 3" aria-current="page">3</button></li><li><button class="tui-key" type="button" data-page="4" aria-label="Page 4">4</button></li><li><button class="tui-key" type="button" data-page="5" aria-label="Page 5">5</button></li><li class="tui-pagination__gap" aria-hidden="true">…</li><li><button class="tui-key" type="button" data-page="12" aria-label="Page 12">12</button></li></ol><button class="tui-key tui-key--icon tui-pagination__step" type="button" data-page-step="1" aria-label="Next page" aria-disabled="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.2 3.8 10.4 8l-4.2 4.2"/></svg></button></div><p class="tui-pagination__range" aria-live="polite">51–75 of 287</p><label class="tui-pagination__size"><span class="tui-label">Rows</span><select class="tui-select" data-pagination-size><option>10</option><option selected>25</option><option>50</option><option>100</option></select></label></nav>
  <nav class="tui-pagination tui-pagination--compact" aria-label="Takes, pages" data-page="3" data-pages="12"><div class="tui-pagination__keys"><button class="tui-key tui-key--icon tui-pagination__step" type="button" data-page-step="-1" aria-label="Previous page" aria-disabled="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.8 3.8 5.6 8l4.2 4.2"/></svg></button><span class="tui-lcd tui-pagination__lcd" data-ghost="88/88" aria-hidden="true">03/12</span><button class="tui-key tui-key--icon tui-pagination__step" type="button" data-page-step="1" aria-label="Next page" aria-disabled="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.2 3.8 10.4 8l-4.2 4.2"/></svg></button></div><span class="tui-pagination__said" aria-live="polite">Page 3 of 12</span></nav>
</div>
```

## Usage notes

Page with the keys or the arrows: the row keeps its width. Change Rows and the range follows, staying on the rows you were reading.

## Accessibility

- A `<nav>` with its own name, a list of keys each named “Page 4”, and the current one marked `aria-current="page"`.
- At the first or last page the arrow is `aria-disabled`, not `disabled`, so the key you just pressed keeps focus.
- The gaps are `aria-hidden`; the range sits in a polite live region.
- The compact counter is a picture of the page number; the words “Page 3 of 12” are said beside it.

## How it’s built

The numbers come from one pure function (seven slots: the first page, the last, the current one and its neighbours, and a gap on each side that needs one), so the row is the same width on page 1 and page 40. `pagination.mjs` redraws the list from it on each press, delegated from the document, and keeps focus where your hand was. The keys are the kit’s at 30px in a tray; the current one takes the latched shadow. The compact counter is the kit’s LCD, with a ghost the same width as the digits.

## Rules

The row keeps its width while you page; the first and last pages are always one press away.

The full rules are in `pagination.rules.md` in the brain, which ships with the Kit.

## Related parts

- [Table](https://tactileui.dev/parts/table): Rows of data with printed column labels, figures right-aligned in columns, and status in lamps with words.
- [Selector](https://tactileui.dev/parts/selector): One mode of two to five: latching keys in a tray, one down at a time, each with a lamp that lights when chosen.
- [LCD](https://tactileui.dev/parts/lcd): A value that changes, on grey-green liquid crystal, with a ghost of the unlit segments behind it: clocks, battery, counters.
