Pagination

Numbered navigation strip. Flex row of .pagination__link children flanked by optional prev/next slots and an ellipsis slot for collapsed page ranges. The current page reads from the framework's primary colour combination; disabled prev/next reuse the global disabled-control treatment.

Live

Default (prev/next + numbered list + ellipsis)

<nav class="pagination" aria-label="Pagination">
  <a class="pagination__previous" href="?page=1" rel="prev">Previous</a>
  <ol class="pagination__list">
    <li><a class="pagination__link" href="?page=1">1</a></li>
    <li><a class="pagination__link" href="?page=2" aria-current="page">2</a></li>
    <li><a class="pagination__link" href="?page=3">3</a></li>
    <li><span class="pagination__ellipsis">&hellip;</span></li>
    <li><a class="pagination__link" href="?page=24">24</a></li>
  </ol>
  <a class="pagination__next" href="?page=3" rel="next">Next</a>
</nav>
@use '@adnap/krysalicss' with (
  $feature-list: ('base-reset', 'base-global', 'module-pagination')
);
Playground

.is-rounded + .is-disabled previous

<nav class="pagination is-rounded" aria-label="Pagination">
  <a class="pagination__previous is-disabled" aria-disabled="true">Previous</a>
  <ol class="pagination__list">
    <li><a class="pagination__link is-current" href="?page=1">1</a></li>
    <li><a class="pagination__link" href="?page=2">2</a></li>
    <li><a class="pagination__link" href="?page=3">3</a></li>
  </ol>
  <a class="pagination__next" href="?page=2" rel="next">Next</a>
</nav>
@use '@adnap/krysalicss' with (
  $feature-list: ('base-reset', 'base-global', 'module-pagination')
);
Playground

.is-centered.is-small

<nav class="pagination is-centered is-small" aria-label="Pagination">
  <ol class="pagination__list">
    <li><a class="pagination__link" href="?page=1">1</a></li>
    <li><a class="pagination__link" href="?page=2" aria-current="page">2</a></li>
    <li><a class="pagination__link" href="?page=3">3</a></li>
  </ol>
</nav>
@use '@adnap/krysalicss' with (
  $feature-list: ('base-reset', 'base-global', 'module-pagination')
);
Playground

Markup

The component is markup-flexible: the numbered list accepts either .pagination__list, <ol>, or <ul> — all three render identically. .pagination__previous and .pagination__next sit outside the list so the flex order (prev / list / next) is preserved while still letting the list wrap independently at narrow widths.

markup
<nav class="pagination" aria-label="Pagination">
  <a class="pagination__previous" href="?page=1" rel="prev">Previous</a>
  <ol class="pagination__list">
    <li><a class="pagination__link" href="?page=1">1</a></li>
    <li><a class="pagination__link" href="?page=2" aria-current="page">2</a></li>
    <li><a class="pagination__link" href="?page=3">3</a></li>
    <li><span class="pagination__ellipsis">&hellip;</span></li>
    <li><a class="pagination__link" href="?page=24">24</a></li>
  </ol>
  <a class="pagination__next" href="?page=3" rel="next">Next</a>
</nav>

States

Both the WAI-ARIA contract and a class-only fallback are emitted for every state, so static-site generators and accessibility-correct templates render identically.

StateARIA hookClass hookNotes
Current pagearia-current="page".is-currentPulls the primary combination from the framework's colour map. Hover affordance disabled (already painted).
Disabled prev/next[disabled] / aria-disabled="true".is-disabledRoutes through the global disabledControl() mixin. Hover affordance stripped.

Modifiers

ModifierEffect
.is-roundedPill links — radius bumps to $global-max-border-radius.
.is-centeredjustify-content: center on the outer flex row so prev/next align with the numbered links.
.is-rightjustify-content: flex-end.
.is-smallBumps font-size and link min-width to the small size tier.
.is-largeBumps font-size and link min-width to the large size tier.

Variables

VariableDefaultNotes
$selector'.pagination'Root <nav>.
$list-selector'.pagination__list'Optional inner list class; <ol> / <ul> also matched.
$link-selector'.pagination__link'Numbered link.
$previous-selector'.pagination__previous'Previous-page anchor / button.
$next-selector'.pagination__next'Next-page anchor / button.
$ellipsis-selector'.pagination__ellipsis'Non-interactive gap glyph for collapsed page ranges.
$current-selector'.is-current'Class-only equivalent of aria-current="page".
$disabled-selector'.is-disabled'Class-only equivalent of [disabled] / aria-disabled="true".
$rounded-selector'.is-rounded'Pill-shape modifier.
$centered-selector'.is-centered'Center the outer flex row.
$right-selector'.is-right'Anchor the outer flex row to flex-end.
$small-selector'.is-small'Small size tier.
$large-selector'.is-large'Large size tier.
$gap$size-extra-smallGap between links and between rows on wrap.
$min-width2.5remPer-link minimum width AND height — keeps numeric pages square. See WCAG 2.5.5 note below.
$padding-y$size-extra-smallVertical padding.
$padding-x$size-extra-smallHorizontal padding.
$border-radius$global-border-radiusLink radius. Overridden to $global-max-border-radius under .is-rounded.
$border-mix35%Border tint relative to --kc-fg on numeric links and (when $nav-ghost: false) the nav slots. Clears WCAG 1.4.11 (3:1) on shipped themes.
$ellipsis-border-mix15%Border tint on .pagination__ellipsis. Lower than $border-mix so the ellipsis reads as a passive separator. Set to 0 for a borderless ellipsis.
$ellipsis-fg-mix50%Foreground tint on the ellipsis glyph. Mixed against --kc-fg.
$hover-mix8%Hover-lift opacity over currentColor — theme-agnostic.
$nav-ghostfalseWhen false (default), .pagination__previous and .pagination__next paint with the same chrome as the numeric tiles. Set to true to drop the border and fill at rest (useful when embedded in another bordered surface).
$nav-padding-x$size-normalHorizontal padding on the nav slots. Larger than $padding-x so the multi-character label has breathing room against the chrome.
$chevronstrueEmit decorative ‹ / › chevrons via ::before / ::after on the nav slots. CSS-only (two-border rotation), no font / SVG dependency.
$chevron-size0.5emWidth and height of the chevron square (em-relative so it tracks the row font-size).
$chevron-thickness0.125emStroke width.
$chevron-gap$size-extra-smallInline gap between the chevron and the label.
$current-combinationmap.get($color-combinations, primary)Bg/fg tuple for the active page. Falls back to $color-combination-default if a consumer's custom map omits primary.
$small-min-width2remMin-width under .is-small.
$small-font-size$size-smallFont-size under .is-small.
$large-min-width3remMin-width under .is-large.
$large-font-size$size-mediumFont-size under .is-large.

Override example

app.scss
@use '@adnap/krysalicss/module/pagination' with (
  $gap: 0.75rem,
  $min-width: 2.75rem,
  $rounded-selector: '.is-pill',
);

Tokens consumed

TokenUsed for
--kc-pagination-current-bg / --kc-pagination-current-fgActive-page surface. Rebindable per instance to retint the current page without recompiling.
--kc-fgMixed at $border-mix for link borders and at 50% for the ellipsis glyph.
--kc-focus-ringPainted on every link / prev / next on :focus-visible. Owned by helper/controls.

Accessibility

  • Wrap the strip in <nav aria-label="Pagination">.
  • Mark the active page with aria-current="page"; pair with .is-current for static-site portability.
  • Disable prev/next via the native [disabled] attribute on <button>, aria-disabled="true" on <a>, or .is-disabled. All three route through the same disabled-control treatment.
  • Per WCAG 2.5.5, the per-link min-width is 2.5rem rather than the AAA touch-target floor of 2.75rem: the row carries a gap between every link, which satisfies the spacing exception. Removing the gap warrants bumping $min-width to $global-min-touch-target.
  • Forced-colors mode pins borders to CanvasText and the active page to Highlight / HighlightText so the state still reads on Windows High Contrast.