---
title: "Pagination"
description: "Moves through the numbered pages of one list. Page numbers with the current page marked, previous and next, first and last where you ask for them, and a short form that says which page of how many."
url: "https://tanrei.chnotchy.com/components/pagination/"
last_updated: 2026-10-23T21:17:12.000Z
---

# Pagination

> Moves through the numbered pages of one list. Page numbers with the current page marked, previous and next, first and last where you ask for them, and a short form that says which page of how many.

## Overview

A pagination pages one list by number: search results, the posts of a blog, the rows of a table. The page before and the page after in a sequence of titled pages, such as the chapters of a guide, are [Previous and next links](https://tanrei.chnotchy.com/components/previous-next-links/): a title says where it leads, a page number does not.

### Usage

Give the current page and how many pages there are. The pagination works out which numbers to show. With `href`, every page is a link to its own address. Without it, the pages are buttons and you change what is shown when the page changes.

**Web**

```html
<!-- Links: each page has an address a person can bookmark, share or open in a tab. {page} is filled with its number. -->
<tanrei-pagination label="Search results pages" page="3" count="12" href="/results/?page={page}"
  previous-label="Previous page" next-label="Next page" page-label="Page {page}"></tanrei-pagination>

<!-- Buttons: the page changes in place. The element sets its own page, then fires change; cancel it to keep the page. -->
<tanrei-pagination id="pages" label="Pages" page="1" count="10"
  previous-label="Previous page" next-label="Next page" page-label="Page {page}"></tanrei-pagination>

<script type="module">
  import '@tanrei/web/components/pagination';
  document.querySelector('#pages').addEventListener('change', (e) => showPage(e.detail.page));
</script>
```

**React**

```tsx
import { Pagination } from '@tanrei/react';

<Pagination label="Pages" page={page} count={10}
  previousLabel="Previous page" nextLabel="Next page" pageLabel="Page {page}"
  onChange={(e) => setPage(e.detail.page)} />
```

SwiftUI, Compose, Flutter and React Native are planned: the names beside each platform at the top of this page are the ones the spec gives them, and nothing ships there yet.

The words are yours: Tanrei ships none. Give them on each element, or once for the whole product with `setWords()` (`previousPageLabel`, `nextPageLabel`, `firstPageLabel`, `lastPageLabel`, `pageLabel`, `pageCountText`).

## Specs

- **Which numbers are shown.** The first and the last page (`boundaries`, 1 by default), the current page and one page on each side of it (`siblings`, 1 by default). The pages left out between them are one ellipsis. Near an end, more pages are shown on the other side, so the row holds the same number of places on every page and keeps its width.
- **The current page** is drawn filled and is exposed as the current one. Activating it does nothing.
- **Previous and next** are icon-only. They are disabled on the first and on the last page, and keep their places. **First and last** are drawn on either side of them with `first-last`.
- **The short form** (`variant="compact"`) draws previous and next around your sentence, “Page 3 of 10”. Without a sentence it draws the two numbers with a slash, “3 / 10”, and a development build reports that the words are missing. The numbered form draws the short one by itself where it does not fit the width it is given.
- **Two sizes**: `medium` (40) and `small` (32), for a dense place such as the status line of a [Table](https://tanrei.chnotchy.com/components/table/).
- **Right to left**, the row is laid from the right and the arrows are mirrored. The numbers are drawn in the digits of the page’s language.

- Category: Navigation
- Level: base
- Listing: default
- Page: https://tanrei.chnotchy.com/components/pagination/
- Spec: https://github.com/chnotchy/tanrei/blob/main/specs/pagination.spec.json

### Availability

| Design system | Has it | Notes |
| --- | --- | --- |
| Tanrei (`tanrei`) | Yes | — |
| Cupertino (Apple HIG-inspired) (`cupertino`) | Yes | — |
| M3 Expressive-like (Material 3 Expressive-inspired) (`m3-expressive-like`) | Yes | — |
| M3-like (Material 3-inspired) (`m3-like`) | Yes | — |

### Platforms

- Web: the element `<tanrei-pagination>`, registered by importing `@tanrei/web` (or only its own module, `@tanrei/web/components/pagination`).
- React: `Pagination` from `@tanrei/react`.

| Platform | Name | Status |
| --- | --- | --- |
| Web | `tanrei-pagination` | alpha |
| SwiftUI | `TanreiPagination` | planned |
| Jetpack Compose | `TanreiPagination` | planned |
| Flutter | `TanreiPagination` | planned |
| React Native | `Pagination` | planned |

Names a platform gives in its own idiom:

| Spec | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| `label` (prop) | `label` | `label` | `contentDescription` | `semanticLabel` | `accessibilityLabel` |
| `firstLast` (prop) | `firstLast` | `showsFirstLast` | `showFirstLast` | `showFirstLast` | `showFirstLast` |
| `href` (prop) | `href` | `destination` | `hrefFor` | `hrefFor` | `hrefFor` |
| `change` (event) | `change` | `onChange` | `onPageChange` | `onPageChanged` | `onPageChange` |

Statuses: `planned` — In the inventory. Nothing shippable exists yet. `lagging` — An implementation of an earlier spec exists; it has not caught up with the current one. `alpha` — Implemented with tests. The API may still change. `beta` — Feature complete and at parity. Only fixes are expected. `stable` — Covered by semantic versioning. `deprecated` — Still shipped; scheduled for removal.

### Anatomy

- `root`: The navigation landmark: as wide as its layout gives it, holding one row of items at its start edge. It draws nothing of its own.
- `item`: One place of the row that can be pressed: a page, or one of the controls first, previous, next and last. As high as its size, at least as wide, and where hover, focus and press are drawn.
- `label`: A page's number, in the digits of the language of where the pagination is used.
- `icon`: The arrow of a control, from the system's icon set: first_page, chevron_left, chevron_right and last_page. Decorative; mirrored in a right-to-left layout by the icon helper.
- `ellipsis` (optional): The mark that stands for the pages left out, in a place as wide as an item. It is not a control and is hidden from assistive technology.
- `count` (optional): The short form's sentence, between previous and next: which page of how many ("Page 3 of 10").
- `focus-ring`: The indicator shown for keyboard focus, around an item.

### Props

| Prop | Type | Default | Web attribute | Description |
| --- | --- | --- | --- | --- |
| `label` | `string` | `""` | `label` | The accessible name of the landmark ("Pages", "Search results pages"), in the product's language. Tanrei ships no words of its own. It tells this landmark from the page's other navigation, so it is required. |
| `page` | `number` | `1` | `page` | The current page, counted from 1. Activating another page sets it (PGN-7). |
| `count` | `number` | `1` | `count` | How many pages there are. Below 1 the pagination draws nothing (PGN-3). |
| `siblings` | `number` | `1` | `siblings` | How many pages are shown on each side of the current page in the numbered form (PGN-2). |
| `boundaries` | `number` | `1` | `boundaries` | How many pages are always shown at the start and at the end of the numbered form (PGN-2). |
| `variant` | `enum`: `numbered`, `compact` | `"numbered"` | `variant` | numbered: the page numbers, with the controls on either side; it draws the compact form where its row does not fit (PGN-11). compact: the controls and the sentence that says which page of how many, never the numbers — for a narrow place, a table's status line, or a list whose pages are not reached by number. |
| `size` | `enum`: `small`, `medium` | `"medium"` | `size` | An item's height (32 / 40 under the default system). small is for a dense place, such as the status line of a compact table. The hit area always meets the minimum target. A design system may add sizes. |
| `firstLast` | `boolean` | `false` | `first-last` | Draws first and last on either side of previous and next, for a list long enough that its ends are worth a control of their own. |
| `href` | `string` | `""` | `href` | The address of a page, with the place {page}, which the pagination fills with the page's number ("/results/?page={page}"). Not empty, every page and every control is a link to its page's address (PGN-6). Empty, they are buttons, and the product changes what is shown when the page changes (PGN-7). |
| `previousLabel` | `string` | `""` | `previous-label` | The previous control's accessible name ("Previous page"). Empty reads the product's words. Required. |
| `nextLabel` | `string` | `""` | `next-label` | The next control's accessible name ("Next page"). Empty reads the product's words. Required. |
| `firstLabel` | `string` | `""` | `first-label` | The first control's accessible name ("First page"). Empty reads the product's words. Required while firstLast is true. |
| `lastLabel` | `string` | `""` | `last-label` | The last control's accessible name ("Last page"). Empty reads the product's words. Required while firstLast is true. |
| `pageLabel` | `string` | `""` | `page-label` | A page's accessible name, with the place {page}, which the pagination fills with the page's number as it draws it ("Page {page}", "{page}ページ"). Empty reads the product's words; where neither gives one, a page is named by its number alone. |
| `countText` | `string` | `""` | `count-text` | The short form's sentence, which the product writes whole in its own language with two places the pagination fills: {page} with the current page and {count} with the number of pages ("Page {page} of {count}", "{count}ページ中{page}ページ"). Empty reads the product's words; where neither gives one, the short form draws the two numbers with a slash between them ("3 / 10"), and a debug build reports it. |

### Events

| Event | Description |
| --- | --- |
| `change` | The user asked for another page, by a page or by first, previous, next or last; carries the page asked for. It is emitted before the page changes and before a link opens its address; a product that cancels it keeps the page as it was and opens nothing — its own router has taken the navigation (PGN-7). |

### States

`enabled`, `disabled`, `hovered`, `focused`, `pressed`, `selected`

### Accessibility

- Role: `navigation`
- Labeling: The landmark is named by label, which is required: a page has other navigation, and the name tells this one apart. Each page is named by pageLabel with its number ("Page 3"), which holds the number a person sees; the current page is exposed as the current one (aria-current="page"), so the name says nothing of it. The controls are icon-only and named by previousLabel, nextLabel, firstLabel and lastLabel. The arrows and the ellipsis are hidden from assistive technology.
- Minimum target size: yes

| Keys | Action |
| --- | --- |
| Tab / Shift+Tab | Moves focus through the enabled items in reading order: each page and each enabled control is a tab stop, as the links of a navigation region are. The current page is a stop too; a disabled control is not. |
| Enter | Activates the focused item: goes to its page. |
| Space | Activates the focused item where the items are buttons. Where they are links it scrolls the page, as on any link. |

- A pagination pages one list by number. The page before and after in a sequence of titled pages — the chapters of a guide, the articles of a series — is Previous and next links, which say by title where they lead.
- Links or buttons: a page with an address of its own (a result page a person can bookmark, share or open in a tab) is a link; a list whose page changes in place (the rows of a table, a dialog's list) is a button. href decides.
- The current page is marked by its fill and by aria-current, never by color alone: its fill differs from the rest in lightness, and forced colors draw it in the highlight colors (PGN-21).
- Disabled controls keep their places, so a person who pages through a list finds next where it was (PGN-5). They are out of the focus order: there is nothing to go to.
- The ellipsis is not a control. A person reaches a page it stands for by moving towards it, or by the product's own way to jump to a page; a pagination does not draw a field to type a page into.
- How many rows a page holds, and the range of rows shown ("1–10 of 248"), are the list's and not the pagination's: a table says them in its status line, where the pagination stands beside a Select for the rows per page.
- The short form is text: when the page changes, the product says what changed where the content changes (a heading, a status). The pagination announces nothing.
- Contrast in the default system: the numbers and arrows are on-surface, held to 4.5:1 (7:1 in the high-contrast schemes) by the token invariants, and the current page's number on-secondary-container on its fill. The focus ring is held to 3:1 against every surface.
- How a state looks is a value decision of the active design system (a state-layer mix in Tanrei, a dim or no hover at all elsewhere). The pagination reads the state's tokens and never computes the effect itself.
- A pagination belongs after the list it pages, and where the list is long, before it too; both name the same landmark by different labels.

### Behaviour

Requirements in EARS form, each with its stable id.

- **PGN-1** THE pagination SHALL be one navigation landmark named by label that holds its items as one list in reading order — first, previous, the pages and ellipses, next, last; in the compact form first, previous, the count sentence, next, last — and nothing else; WHILE label is empty THE pagination SHALL still render, and in debug builds report that the landmark has no accessible name.
- **PGN-2** THE numbered form SHALL show the pages 1 to boundaries, the pages count − boundaries + 1 to count, and the current page with siblings pages on each side of it, and SHALL draw an ellipsis in place of the pages left out between two of these runs; WHERE the ellipsis would stand for one page only THE numbered form SHALL draw that page instead. WHILE the current page is near an end THE numbered form SHALL show more pages on the other side of it, so that the row holds the same number of places — 2 × boundaries + 2 × siblings + 3 — on every page whenever count is at least that many, and the row keeps its width from page to page.
- **PGN-3** THE pagination SHALL read count as a whole number, and WHILE it is below 1 or cannot be read SHALL draw nothing, take no room and expose no landmark; it SHALL read page as a whole number held between 1 and count, and siblings and boundaries as whole numbers of 0 or more (boundaries 0 shows no end pages, so that an ellipsis stands at an end), and in debug builds report each value it had to change.
- **PGN-4** THE pagination SHALL draw the current page from the current tokens (comp.pagination.current.container.color, comp.pagination.current.label.color and so on) and every other page and every control from the item tokens, and SHALL expose the current page as the current one (on the web aria-current="page") while keeping it the same kind of element as the other pages: a link to its own address while href is given, a button otherwise. WHEN the current page is activated THE pagination SHALL emit no change; a current page that is a link still opens its own address, as any link does.
- **PGN-5** WHILE the current page is 1 THE pagination SHALL draw first and previous disabled, and WHILE it is count, next and last disabled: drawn from the disabled state's tokens, exposed as disabled, out of the focus order and not activatable, and in their places, so that the row keeps its width; a disabled control of a pagination with href SHALL have no address.
- **PGN-6** WHILE href is not empty THE pagination SHALL draw every page and every enabled control as the platform's own link to href with {page} filled with the page it leads to, written as a whole number in Western digits whatever the language, so that whatever the platform gives a link — on the web its context menu, opening in a new tab, copying its address, the address shown before it is followed — is the platform's; it SHALL expose the link role and WHERE the platform's links can say how a destination relates to the page (the web's rel) SHALL say prev for previous and next for next. IF href does not hold the place {page}, or names a script to run (the web's javascript: scheme), THE pagination SHALL draw buttons as if href were empty and in debug builds report it.
- **PGN-7** WHEN the user activates a page other than the current one, or an enabled control, by pointer, touch, Enter, Space on a button or assistive technology THE pagination SHALL emit change exactly once with the page it leads to — first 1, previous page − 1, next page + 1, last count — and then, unless the product cancelled it, SHALL make that page the current one and, while href is given, open the link's address where the person is; WHEN the product cancels change THE pagination SHALL keep the page it had and open nothing. A link SHALL NOT be activated by Space, which scrolls the page as it does on any link.
- **PGN-8** THE pagination SHALL draw previous, next, first and last as icons only — chevron_left, chevron_right, first_page and last_page of the system's icon set, through the platform's icon helper, in a box of comp.pagination.icon.size, in the icon color of their state, with sys.icon.family.unselected and sys.icon.fill.unselected, sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max — and SHALL name them by previousLabel, nextLabel, firstLabel and lastLabel, taking each from the product's words WHILE it is empty; WHILE a control's name is empty in both THE pagination SHALL still draw it and in debug builds report that it has no accessible name. It SHALL take no glyph from the product and SHALL draw no path of its own.
- **PGN-9** THE pagination SHALL draw a page's number in comp.pagination.label.typescale, in the digits the platform's number formatting gives for the language where it is used, without grouping separators (1205, not 1,205), and in figures of one width, and SHALL name the page by pageLabel with {page} filled by the number as it is drawn, taking pageLabel from the product's words WHILE it is empty and naming the page by its number alone WHILE neither gives one; the number drawn SHALL stay part of the name, so that a person who says what they see can activate the page.
- **PGN-10** THE pagination SHALL draw an ellipsis as the character … in comp.pagination.label.typescale and comp.pagination.ellipsis.color, centred in a place as wide as an item is high (comp.pagination.container.height.medium for medium, and so on), hidden from assistive technology, out of the focus order and with no state: the pages left out are said by the numbers on either side of it.
- **PGN-11** WHILE variant is compact, or variant is numbered and the width the layout gives the pagination is less than its numbered row needs, THE pagination SHALL draw the compact form: first and previous, then countText with {page} and {count} filled, comp.pagination.count.padding-inline on each side, in comp.pagination.count.typescale and comp.pagination.count.color, then next and last; WHILE countText is empty in both the prop and the product's words THE compact form SHALL draw the two numbers with a slash between them ("3 / 10") and in debug builds report it. It SHALL decide this by the width it is given, not by the window's, and the width it asks of a layout that sizes it by its content SHALL be the width of its numbered row, so that it draws the numbered form again where the room comes back. The sentence is text, not a control and not a live region: the product says what changed where the content changes.
- **PGN-12** THE pagination SHALL lay its items out in one row at the start edge of its box, comp.pagination.gap apart, each item the container height of its size high (comp.pagination.container.height.medium for medium, and so on) and at least as wide, a page's number centred in it with comp.pagination.container.padding-inline on each side where the number is wider than the square, and each control's arrow centred in its square; it SHALL NOT wrap the row.
- **PGN-13** WHILE an item is hovered, keyboard-focused, pressed or disabled THE pagination SHALL render its container, label, arrow and opacity from the component tokens of the highest-precedence active state of its part (item or current), with precedence disabled over pressed over focused over hovered; focus gained by pointer or touch SHALL NOT select the focused state.
- **PGN-14** WHEN an item receives keyboard focus THE pagination SHALL show the focus ring around that item without animation — the system's focus indicator, sys.focus.offset from the container, sys.focus.outer-width wide in the focus-ring color, with the inner stroke of sys.focus.inner-width where the system has one; focus gained by pointer or touch SHALL NOT show it.
- **PGN-15** WHEN an enabled item is pressed by pointer, touch, Enter or, on a button, Space THE pagination SHALL show the pressed state without delay and SHALL keep it visible for at least sys.motion.duration.press-hold, even when the press ends sooner; it SHALL NOT hold the change or the navigation back for press-hold.
- **PGN-16** WHILE pressed THE pagination SHALL answer as sys.behavior.pressed-feedback names: overlay applies the pressed tokens to the whole container at once; ripple grows the pressed state layer from the point of contact (from the center for keyboard activation); dim applies the pressed opacity to the whole item and draws no state layer; none draws no answer to the press. Under ripple and dim only the container color stays with the state under the press; label, icon, opacity and shape still come from the pressed tokens. Under none every part stays with the state under the press. The ripple grows over comp.pagination.motion.feedback-duration with comp.pagination.motion.state-easing, fades over comp.pagination.motion.state-duration with comp.pagination.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced.
- **PGN-17** THE pagination SHALL ease its item corners by sys.shape.corner-smoothing (0 is a quarter circle, 0.6 the iOS continuous corner; a platform with only the OS curve draws it for any amount above 0) and show its keyboard focus as sys.behavior.focus-style names (ring or glow); a platform that cannot draw a choice SHALL fall back to the first value and state that in its README.
- **PGN-18** THE pagination SHALL change an item between its states over comp.pagination.motion.state-duration with comp.pagination.motion.state-easing, and at once where motion is reduced; WHEN the current page changes THE pagination SHALL change the item that was current and the item that becomes current between their states as it changes any item's, and SHALL draw at once the items whose page numbers or places change.
- **PGN-19** THE pagination SHALL make each enabled item one stop of the focus order, in the order of PGN-1, as the links and buttons of a navigation region are, and SHALL take no arrow key: Tab and Shift+Tab move between items. WHEN the current page changes by an item the person activated THE pagination SHALL keep focus on an item: on the page now current where the person activated a page, and on the control they activated where it is still enabled, otherwise on the page now current — and in the compact form, which draws no pages, on the nearest enabled control (previous after next or last, next after previous or first).
- **PGN-20** WHERE the visual size of an item is below the minimum target THE pagination SHALL extend its hit area on both axes to sys.target.touch for touch input and sys.target.pointer for fine pointers, without changing its visual size and without overlapping the hit area of its neighbour beyond the gap between them; a platform that cannot tell the input type SHALL use sys.target.touch.
- **PGN-21** WHERE the platform draws with a person's own forced colors (the web's forced-colors mode, and a platform's equivalent where it has one) THE pagination SHALL draw the numbers, the arrows and the count sentence in the platform's text or link color, the current page in the platform's highlight colors, a disabled control in the platform's disabled text color, and the focus ring in the platform's highlight color.
- **PGN-22** WHILE the text size is enlarged THE pagination SHALL grow each item with its number, never below the container height of its size (comp.pagination.container.height.medium for medium, and so on), keeping comp.pagination.gap and the arrows at comp.pagination.icon.size, and SHALL draw the compact form where the grown row no longer fits (PGN-11).
- **PGN-23** THE pagination SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHERE that direction is right-to-left THE pagination SHALL lay its row from the right — first at the right edge, last at the left — keeping the reading and focus order of PGN-1, and SHALL draw the arrows mirrored where the system's icon set marks them as mirrored; THE pagination SHALL NOT mirror a glyph itself. WHEN the direction changes while the pagination is shown THE pagination SHALL draw itself for the new direction at once, keeping its focus.
- **PGN-24** THE pagination SHALL lay out the count sentence as text of its own direction, isolated from the text around it, and each number as a number that keeps its own order in a right-to-left layout, so that "3 / 10" reads 3 then 10 in either direction.
- **PGN-25** WHERE the platform has ARIA attributes (the web), WHEN the product sets an aria- attribute on the host, by the attribute or by the ARIA property that reflects it, THE pagination SHALL move it to the data-aria- attribute of the same name, so that the host is no second node with its name, description or state, and SHALL expose it on its navigation landmark (PGN-1) — an id reference by the elements it names in the host's tree or a tree around it, a description after its own. WHILE label is not empty it is the landmark's name, over a name given so; WHILE label is empty, a name given so is the landmark's name and SHALL NOT be reported as having none in debug builds. WHEN the product removes the aria- attribute or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaDescribedByElements…) to null, THE pagination SHALL take it off that element.

### Tokens

As written in the token documents (DTCG): an alias in braces names the token it takes its value from, which each design system resolves. On the web each token is a CSS custom property: `--tanrei` and the name after `tanrei`, joined by hyphens (`tanrei.comp.pagination.…` → `--tanrei-comp-pagination-…`).

#### `tanrei.comp.pagination`

| Token | Type | Value |
| --- | --- | --- |
| `comp.pagination.gap` | `dimension` | `{tanrei.sys.space.space50}` |
| `comp.pagination.container.height.small` | `dimension` | `32px` |
| `comp.pagination.container.height.medium` | `dimension` | `40px` |
| `comp.pagination.container.padding-inline` | `dimension` | `{tanrei.sys.space.space100}` |
| `comp.pagination.container.shape` | `dimension` | `{tanrei.sys.shape.corner.full}` |
| `comp.pagination.label.typescale` | `typography` | `{tanrei.sys.typescale.label}` |
| `comp.pagination.icon.size` | `dimension` | `{tanrei.sys.icon.small}` |
| `comp.pagination.ellipsis.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.pagination.count.typescale` | `typography` | `{tanrei.sys.typescale.body}` |
| `comp.pagination.count.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.pagination.count.padding-inline` | `dimension` | `{tanrei.sys.space.space100}` |
| `comp.pagination.item.container.color` | `color` | `{"colorSpace":"srgb","components":[0,0,0],"alpha":0,"hex":"#000000"}` |
| `comp.pagination.item.label.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.pagination.item.icon.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.pagination.item.state-layer.color` | `color` | `{tanrei.comp.pagination.item.label.color}` |
| `comp.pagination.current.container.color` | `color` | `{tanrei.sys.color.secondary-container}` |
| `comp.pagination.current.label.color` | `color` | `{tanrei.sys.color.on-secondary-container}` |
| `comp.pagination.current.icon.color` | `color` | `{tanrei.sys.color.on-secondary-container}` |
| `comp.pagination.current.state-layer.color` | `color` | `{tanrei.comp.pagination.current.label.color}` |
| `comp.pagination.motion.state-duration` | `duration` | `{tanrei.sys.motion.role.state.compact}` |
| `comp.pagination.motion.state-easing` | `cubicBezier` | `{tanrei.sys.motion.easing.state}` |
| `comp.pagination.motion.feedback-duration` | `duration` | `{tanrei.sys.motion.role.feedback}` |

Per-state tokens, valued by the design system's pattern rules (not written by hand):

- `comp.pagination.<variant>.<state>.<property>` for the variants `item`, `current`, the states `hovered`, `focused`, `pressed`, `disabled` and the properties `container.color`, `label.color`, `icon.color`, `state-layer.color`.
- `comp.pagination.<state>.<property>` for the states `hovered`, `focused`, `pressed`, `disabled` and the properties `opacity`, `container.shape`.

## Guidelines

### Do

- Put it after the list it pages, and before the list too where the list is long.
- Use links (`href`) when each page has an address of its own, and buttons when the page changes in place.
- Under a [Table](https://tanrei.chnotchy.com/components/table/), use the short form at size `small` in the status line, beside a [Select](https://tanrei.chnotchy.com/components/select/) for the rows per page where people choose it. The range of rows shown (“1–10 of 248”) is the table’s status text.
- When the page changes, say what changed where the content changes: a heading, or the table’s status text. The pagination announces nothing.

### Don’t

- Don’t use it for the page before and after in a sequence of titled pages. That is [Previous and next links](https://tanrei.chnotchy.com/components/previous-next-links/).
- Don’t show it for a list of one page.
- Don’t hide previous and next on the first and last page. They are disabled and keep their places.

## Accessibility

### What the component handles

- One navigation landmark around one list. Each page and each enabled control is a link or a button and a stop in the focus order.
- The current page is exposed with `aria-current="page"`, and drawn filled, not only in another color.
- Disabled controls are out of the focus order. The arrows and the ellipsis are hidden from assistive technology.
- A page’s name holds the number a person sees, so a person who says what they see can activate it.

### What you need to do

- Name the landmark with `label`, in your product’s language. A page has other navigation, and the name tells this one apart.
- Give the controls their names (`previous-label`, `next-label`, and `first-label` and `last-label` with `first-last`) and a page its name with `page-label`. Tanrei ships no words of its own.
- Say what changed when you replace the list. The pagination keeps focus on one of its items: the control that was activated, or the page now current where that control is disabled.
