Pagination

A row of page chips for moving through a long list or paginated view.

Basic

Wrap a list of .pagination__button in .pagination. Mark the current page with data-state="active" and aria-current="page". The active chip paints --st-highlight while hover paints the lighter --st-accent.

<nav aria-label="Page navigation">
  <ul class="pagination">
    <li><a class="pagination__button" href="#">Previous</a></li>
    <li><a class="pagination__button" href="#">1</a></li>
    <li><a class="pagination__button" href="#" data-state="active" aria-current="page">2</a></li>
    <li><a class="pagination__button" href="#">3</a></li>
    <li><a class="pagination__button" href="#">Next</a></li>
  </ul>
</nav>

With icons

Drop an <svg> or <i> inside the chip. Icons sit at 1em so they scale with the chip's font size and stay on the label baseline.

<nav aria-label="Page navigation">
  <ul class="pagination">
    <li>
      <a class="pagination__button" href="#" aria-label="Previous">
        <i data-lucide="chevron-left"></i>
        <span>Previous</span>
      </a>
    </li>
    <li><a class="pagination__button" href="#">1</a></li>
    <li><a class="pagination__button" href="#" data-state="active" aria-current="page">2</a></li>
    <li><a class="pagination__button" href="#">3</a></li>
    <li>
      <a class="pagination__button" href="#" aria-label="Next">
        <span>Next</span>
        <i data-lucide="chevron-right"></i>
      </a>
    </li>
  </ul>
</nav>

Disabled

Set aria-disabled="true" on the chip (or use <button disabled>) to gray it out and turn off pointer events. Drop the href too so screen readers don't announce it as a link. Use on the leading or trailing chip when the user is at the start or end of the range.

<nav aria-label="Page navigation">
  <ul class="pagination">
    <li>
      <span class="pagination__button" aria-label="Previous" aria-disabled="true">
        <i data-lucide="chevron-left"></i>
        <span>Previous</span>
      </span>
    </li>
    <li><a class="pagination__button" href="#" data-state="active" aria-current="page">1</a></li>
    <li><a class="pagination__button" href="#">2</a></li>
    <li><a class="pagination__button" href="#">3</a></li>
    <li>
      <a class="pagination__button" href="#" aria-label="Next">
        <span>Next</span>
        <i data-lucide="chevron-right"></i>
      </a>
    </li>
  </ul>
</nav>

Ellipsis

Drop .pagination__ellipsis in for a truncation marker between page ranges. Takes the same chip footprint so the row rhythm holds, no hover, no focus.

<nav aria-label="Page navigation">
  <ul class="pagination">
    <li>
      <a class="pagination__button" href="#" aria-label="Previous">
        <i data-lucide="chevron-left"></i>
      </a>
    </li>
    <li><a class="pagination__button" href="#">1</a></li>
    <li><a class="pagination__button" href="#">2</a></li>
    <li><a class="pagination__button" href="#" data-state="active" aria-current="page">3</a></li>
    <li><a class="pagination__button" href="#">4</a></li>
    <li><a class="pagination__button" href="#">5</a></li>
    <li><span class="pagination__ellipsis" aria-hidden="true">…</span></li>
    <li><a class="pagination__button" href="#">12</a></li>
    <li>
      <a class="pagination__button" href="#" aria-label="Next">
        <i data-lucide="chevron-right"></i>
      </a>
    </li>
  </ul>
</nav>

Sizes

Add .pagination--sm or .pagination--lg for compact or large chips. Heights match the button scale at 28 / 36 / 44 px under default density.

<div class="d-flex flex-column gap-4 w-100">
  <nav aria-label="Small page navigation">
    <ul class="pagination pagination--sm">
      <li><a class="pagination__button" href="#">Previous</a></li>
      <li><a class="pagination__button" href="#">1</a></li>
      <li><a class="pagination__button" href="#" data-state="active" aria-current="page">2</a></li>
      <li><a class="pagination__button" href="#">3</a></li>
      <li><a class="pagination__button" href="#">Next</a></li>
    </ul>
  </nav>
  <nav aria-label="Default page navigation">
    <ul class="pagination">
      <li><a class="pagination__button" href="#">Previous</a></li>
      <li><a class="pagination__button" href="#">1</a></li>
      <li><a class="pagination__button" href="#" data-state="active" aria-current="page">2</a></li>
      <li><a class="pagination__button" href="#">3</a></li>
      <li><a class="pagination__button" href="#">Next</a></li>
    </ul>
  </nav>
  <nav aria-label="Large page navigation">
    <ul class="pagination pagination--lg">
      <li><a class="pagination__button" href="#">Previous</a></li>
      <li><a class="pagination__button" href="#">1</a></li>
      <li><a class="pagination__button" href="#" data-state="active" aria-current="page">2</a></li>
      <li><a class="pagination__button" href="#">3</a></li>
      <li><a class="pagination__button" href="#">Next</a></li>
    </ul>
  </nav>
</div>

Alignment

Add .pagination--center or .pagination--end to flip the row's justify-content. Default is start.

<div class="d-flex flex-column gap-4 w-100">
  <nav aria-label="Centered page navigation">
    <ul class="pagination pagination--center">
      <li><a class="pagination__button" href="#">Previous</a></li>
      <li><a class="pagination__button" href="#">1</a></li>
      <li><a class="pagination__button" href="#" data-state="active" aria-current="page">2</a></li>
      <li><a class="pagination__button" href="#">3</a></li>
      <li><a class="pagination__button" href="#">Next</a></li>
    </ul>
  </nav>
  <nav aria-label="End-aligned page navigation">
    <ul class="pagination pagination--end">
      <li><a class="pagination__button" href="#">Previous</a></li>
      <li><a class="pagination__button" href="#">1</a></li>
      <li><a class="pagination__button" href="#" data-state="active" aria-current="page">2</a></li>
      <li><a class="pagination__button" href="#">3</a></li>
      <li><a class="pagination__button" href="#">Next</a></li>
    </ul>
  </nav>
</div>

Customization

Sixteen variables retune .pagination without touching component CSS. Override on .pagination itself, on a parent scope, or on :root. The cascade scopes the change. Size modifiers --sm / --lg retune the chip height, padding, and font size in their own scope, so there are no per-size vars to maintain.

Variable Default Use
--pagination-gap calc(0.25rem * var(--st-density)) Space between chips
--pagination-margin-bottom 0 Trailing space below the row
--pagination-font-size 0.875rem Chip text size
--pagination-button-height calc(2.25rem * var(--st-density)) Chip height (also doubles as min-width so single-glyph chips land as squares)
--pagination-button-padding-x calc(0.75rem * var(--st-density)) Chip horizontal padding (longer labels grow from this)
--pagination-button-inner-gap 0.375rem Space between icon and label inside a chip
--pagination-button-radius var(--st-radius) Chip corner radius (set 9999px for pill chips)
--pagination-button-bg transparent Chip rest background
--pagination-button-color var(--st-foreground) Chip rest text color
--pagination-button-border-color transparent Chip border color (set var(--st-border) for outlined chips)
--pagination-button-hover-bg var(--st-accent) Hover background (interactive chips only)
--pagination-button-hover-color var(--st-accent-foreground) Hover text color
--pagination-button-active-bg var(--st-highlight) Current page background (data-state="active" / aria-current="page")
--pagination-button-active-color var(--st-highlight-foreground) Current page text color
--pagination-button-disabled-color var(--st-muted-foreground) Disabled / ellipsis text color
--pagination-button-ring var(--st-ring) Focus outline color