Skip to main content

Pagination

Info:Navigation html svelte astro Success: coverage 61/61

A page navigator: previous/next controls around a windowed list of page numbers with ellipses.

Live demo

live · @xoji/astro

Pagination

Walking a collection

Button mode: click a page or an arrow and the control fires a page-change event. This demo listens for it, updates the current page, and the windowed range re-threads itself.

Page 1 of 12

The windowed range

Boundary pages stay pinned at each end and a sibling window follows the current page; the gaps collapse to an ellipsis. Same 20-page set, three positions.

page=1
page=10
page=20

Link mode

Give it an href template with {page} and every page is a real anchor: crawlable, works with zero JavaScript.

Tones

The current-page pill speaks the full tone vocabulary: the six semantic roles, the accent variants, and the twelve named hues. 21 in all, every one AA-cleared.

Accents

tone="accent"
tone="accent-2"
tone="accent-3"
tone="accent-4"
tone="neutral"

Statuses

tone="success"
tone="info"
tone="warn"
tone="danger"

Named hues

tone="red"
tone="orange"
tone="yellow"
tone="green"
tone="blue"
tone="purple"
tone="brown"
tone="pink"
tone="cyan"
tone="gray"
tone="white"
tone="black"

Sizes

Three steps, scaled against the type ramp.

Small
Medium (default)
Large

Pagination walks a reader through a paged collection. It is a <nav> landmark wrapping previous and next controls and an ordered list of page numbers; the current page is marked aria-current="page", and a sibling window around it keeps the control compact, collapsing the gaps to an ellipsis when there are more pages than fit.

The visible range is computed from page and total plus two knobs — siblings (links on each side of the current page) and boundaries (links pinned at each end). Give it an href template containing {page} and every page renders as a real link, so the control navigates with zero JavaScript and works on the static Astro path; omit the template and the pages render as buttons that emit a page-change event carrying the chosen page. A tone colors the current-page pill and three sizes scale the type.

When to use

How this component composes with the rest of the set.

Pair below a Table or a card grid to page through a long collection.
Use href for content routes (blog, search results) so the pages are crawlable links; use button mode with page-change for in-app state.
Drop siblings/boundaries to 0/1 for the most compact control, or raise siblings when there's room for a wider window.

Props

8 props, straight from the manifest.

PropTypeDefaultBindingsDescription
page number 1
html svelte astro
The current page, 1-based. Clamped into `[1, total]`.
total number 1
html svelte astro
The total number of pages.
siblings number 1
html svelte astro
How many page links to show on each side of the current page.
boundaries number 1
html svelte astro
How many page links to pin at each end of the range.
href string
html svelte astro
A URL template with a `{page}` placeholder. When set, every page is a real link (zero-JS navigation); when omitted, pages are buttons that emit `page-change`.
tone FullTone
accent neutral danger success warn info accent-2 accent-3 accent-4 red orange yellow green blue purple brown pink cyan gray white black
accent
html svelte astro
Color of the current-page pill, any of the 21 tones: the six semantic roles, `accent-2/3/4`, or the twelve named hues.
size Size
sm md lg
md
html svelte astro
Type scale of the control.
label string Pagination
html svelte astro
The accessible name of the `<nav>` landmark.

Appearance

Variants

accent

.xoji-pagination--accent

Current-page pill in the accent tone (the default).

neutral

.xoji-pagination--neutral

Current-page pill in neutral ink for a quieter control.

Sizes

sm

.xoji-pagination--sm

Compact.

md

default
.xoji-pagination

Default.

lg

.xoji-pagination--lg

Large.

States

hover

.xoji-pagination__page:hover

Pointer over a page or control; a soft wash fills behind it.

focus-visible

.xoji-pagination__page:focus-visible

Keyboard focus: a token-colored ring, plus a transparent outline promoted to a real one in forced-colors mode.

current

.xoji-pagination__page--current

The active page: a filled tone pill, no hover, aria-current.

disabled

.xoji-pagination__control[aria-disabled="true"]

A previous/next control at the end of the range: dimmed and non-interactive.

Anatomy

The named parts that make up the component, with their selectors.

pagination

.xoji-pagination

The <nav> landmark laying the controls and page list out in a wrapping row.

--font-sans --text-sm --leading-normal --fg-2 --space-1

page

.xoji-pagination__page

A page number — a link (href mode) or button — with a hover wash and a focus ring.

--fg-1 --bg-2 --radius-sm --space-0 --space-1

current

.xoji-pagination__page--current

The current page: a filled pill in the tone color, carrying aria-current.

--accent --accent-fg --weight-medium

control

.xoji-pagination__control

The previous / next arrows; dimmed and inert at the ends of the range.

--fg-1 --fg-3 --bg-2

ellipsis

.xoji-pagination__ellipsis

The collapsed-gap marker between distant pages, hidden from assistive tech.

--fg-3

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 61/61 consumed tokens produced default register: 276 tokens

Live coverage check against the xoji-default register (derive(xojiDefault, { anchors })coverComponent(manifest, register)). Every token this component consumes must be a key the algorithm produces.

--accent --accent-2 --accent-2-fg --accent-3 --accent-3-fg --accent-4 --accent-4-fg --accent-fg --bg-2 --black --black-fg --blue --blue-fg --border-normal --border-thick --border-thin --brown --brown-fg --cyan --cyan-fg --danger --danger-fg --duration-fast --ease-standard --fg-1 --fg-2 --fg-3 --font-sans --gray --gray-fg --green --green-fg --info --info-fg --leading-normal --neutral --neutral-fg --orange --orange-fg --pink --pink-fg --purple --purple-fg --radius-sm --red --red-fg --ring --space-0 --space-1 --success --success-fg --text-body --text-sm --text-xs --warn --warn-fg --weight-medium --white --white-fg --yellow --yellow-fg

Accessibility

Renders a <nav> landmark with an accessible name (aria-label, default "Pagination") so screen readers can find and announce the pager.
The current page carries aria-current="page" and is not actionable — it is the destination, not a target.
Each page and control has an explicit accessible name (Go to page N, Previous page, Next page), so the digit or arrow glyph is never read bare.
Previous/next at the ends of the range are marked aria-disabled="true" and made non-interactive rather than removed, so their position stays stable.
The ellipsis is decorative and aria-hidden, never announced between pages.
In button mode the pages are real <button>s — keyboard-operable and focusable for free; the chosen page rides a page-change event.

Code

Link and button modes

An href template makes each page a zero-JS link; without it the pages are buttons that emit page-change.

<!-- link mode: each page is a real anchor, zero JS needed -->
<xoji-pagination page="3" total="20" href="/blog?page={page}"></xoji-pagination>

<!-- button mode: listen for page-change -->
<xoji-pagination page="3" total="20" tone="neutral"></xoji-pagination>

<script>
	document.querySelector("xoji-pagination:not([href])")
		.addEventListener("page-change", (e) => console.log(e.detail.page));
</script>
<script lang="ts">
	import { Pagination } from "@xoji/svelte";
	let page = $state(3);
</script>

<Pagination {page} total={20} onpagechange={(e) => (page = e.detail.page)} />
---
import Pagination from "@xoji/astro/Pagination.astro";
const page = Number(Astro.url.searchParams.get("page") ?? 1);
---

<Pagination page={page} total={20} href="/blog?page={page}" />