Component Contract

Pagination

Navigation control for multi-page content, enabling CAAT Pension Plan members to move between pages of results, tables, and listings across the member experience.

Live Demo

Basic Pagination

Rendered

With Icons (Chevron Arrows)

Rendered

Sizing (Small, Default, Large)

Rendered

Small

Default

Large

With Results Text

Rendered

Showing 1–10 of 97 results  

States

Follows the Interaction States matrix. Page links implement default, hover, focus, active (current page), and disabled (boundary). The static is-* classes mirror the live pseudo-class states.

Rendered

Code sample

HTML

<!-- Basic pagination -->
<nav aria-label="Page navigation">
  <ul class="caat-pagination">
    <li class="caat-pagination__item caat-pagination__item--disabled">
      <a class="caat-pagination__link" href="#" tabindex="-1" aria-disabled="true">Previous</a>
    </li>
    <li class="caat-pagination__item caat-pagination__item--active">
      <a class="caat-pagination__link" href="#" aria-current="page">1</a>
    </li>
    <li class="caat-pagination__item">
      <a class="caat-pagination__link" href="#">2</a>
    </li>
    <li class="caat-pagination__item">
      <a class="caat-pagination__link" href="#">Next</a>
    </li>
  </ul>
</nav>

<!-- With chevron icons -->
<nav aria-label="Page navigation">
  <ul class="caat-pagination">
    <li class="caat-pagination__item">
      <a class="caat-pagination__link" href="#" aria-label="Previous page">
        <i class="bi bi-chevron-left" aria-hidden="true"></i>
      </a>
    </li>
    <li class="caat-pagination__item caat-pagination__item--active">
      <a class="caat-pagination__link" href="#" aria-current="page">1</a>
    </li>
    <li class="caat-pagination__item">
      <a class="caat-pagination__link" href="#" aria-label="Next page">
        <i class="bi bi-chevron-right" aria-hidden="true"></i>
      </a>
    </li>
  </ul>
</nav>

<!-- With results text -->
<div class="caat-pagination-wrapper">
  <span class="caat-pagination__results">Showing 1–10 of 97 results</span>
  <nav aria-label="Page navigation">
    <ul class="caat-pagination">…</ul>
  </nav>
</div>

1. Summary

Component namecaat/components/pagination
PurposeNavigation control for splitting long content across multiple pages. Used in member dashboards, contribution history tables, search results, and document listings to provide clear, accessible page navigation.
Status New Planned
VariantsBasic (text labels), Icon (chevron arrows), Small, Default, Large
OptionsResults summary text, ellipsis for truncated ranges, disabled state for first/last boundaries
AEM resource typecaat/components/pagination
AEM categoryCAAT Components – Navigation

2. Design Tokens

TokenRoleDefault Value
--caat-blue-900Active page background & primary text colour#003750
--caat-blue-700Hover border colour#0b5a80
--caat-blue-100Hover background#e7f4fb
--caat-greenAvailable for success/confirmation states#55a546
--caat-grey-200Default border & disabled text colour#dfe6ef
--caat-focusFocus ring outline colour#4d90fe
--caat-inkResults text & ellipsis colour#102637
--caat-radiusBorder radius for pagination links.75rem
--caat-font-primaryFont family'Libre Franklin', sans-serif

3. Authoring Fields

FieldTypeRequiredNotes
Total ItemsNumberYesTotal number of items to paginate. Drives page count calculation.
Items Per PageNumber (dropdown: 10, 20, 50)YesNumber of items displayed per page. Default: 10.
Current PageNumberYesThe currently active page (1-based). Default: 1.
Navigation StyleDropdown (text | icon)NoDefault: text. "text" renders "Previous/Next" labels; "icon" renders chevron arrows.
SizeDropdown (sm | default | lg)NoDefault: default. Controls touch-target sizing.
Show Results TextToggle (boolean)NoDefault: off. When enabled, shows "Showing X–Y of Z results" text.
Aria LabelText fieldNoCustom aria-label for the <nav> element. Default: "Page navigation".

4. Validation Rules

  • Total Items must be a positive integer greater than 0.
  • Items Per Page must be a positive integer; component will not render if set to 0.
  • Current Page must be between 1 and the calculated total page count.
  • Previous link is disabled when Current Page is 1; Next link is disabled when Current Page equals total page count.
  • If total pages ≤ 1, the pagination component should not render (nothing to paginate).
  • When total pages exceed 7, middle pages are truncated with an ellipsis ("…").

5. Content Guidance

GuidelineDetail
Use forContribution history tables, search results, document listings, member notifications — any content that spans multiple pages.
PlacementBelow the content being paginated, centred or right-aligned. Optionally include results summary text on the left.
Text labels"Previous" and "Next" are the default labels. Keep labels short and translatable.
Icon variantPreferred when space is limited (e.g., mobile layouts or dense data tables). Always include aria-label on icon-only links.
Results textFormat: "Showing X–Y of Z results". Use en-dash (–) not hyphen. Helps members orient themselves within large result sets.
Don'tDon't use pagination for fewer than two pages. Don't use for content that benefits from infinite scroll (e.g., news feeds). Don't mix text and icon styles within the same pagination instance.

6. Semantic HTML

HTL / Markup contract

<!-- Basic pagination -->
<nav aria-label="Page navigation">
  <ul class="caat-pagination">
    <li class="caat-pagination__item caat-pagination__item--disabled">
      <a class="caat-pagination__link" href="#" tabindex="-1"
         aria-disabled="true">Previous</a>
    </li>
    <li class="caat-pagination__item caat-pagination__item--active">
      <a class="caat-pagination__link" href="#"
         aria-current="page">1</a>
    </li>
    <li class="caat-pagination__item">
      <a class="caat-pagination__link" href="#">2</a>
    </li>
    <li class="caat-pagination__item">
      <a class="caat-pagination__link" href="#">Next</a>
    </li>
  </ul>
</nav>

<!-- With results text wrapper -->
<div class="caat-pagination-wrapper">
  <span class="caat-pagination__results">
    Showing 1–10 of 97 results
  </span>
  <nav aria-label="Page navigation">
    <ul class="caat-pagination">…</ul>
  </nav>
</div>

Key decisions

  • <nav> wraps the pagination with a descriptive aria-label for landmark navigation.
  • <ul>/<li> provides a list structure so screen readers announce the item count.
  • aria-current="page" on the active link tells assistive technology which page is selected.
  • aria-disabled="true" and tabindex="-1" on disabled Previous/Next links remove them from the tab order.
  • Icon-only links (bi-chevron-left/bi-chevron-right) require aria-label="Previous page"/aria-label="Next page".
  • Ellipsis uses aria-hidden="true" to avoid confusing screen reader output.

7. CSS Contract

SelectorPurpose
.caat-paginationBase container — flexbox list reset, gap, font-family.
.caat-pagination__itemList item wrapper.
.caat-pagination__linkClickable page link — min 44×44 touch target, border, border-radius, transitions.
.caat-pagination__item--active .caat-pagination__linkActive page — blue-900 background, white text, pointer-events none.
.caat-pagination__item--disabled .caat-pagination__linkDisabled state — greyed out, reduced opacity, pointer-events none.
.caat-pagination--smSmall size modifier — reduced min-width/height and font-size.
.caat-pagination--lgLarge size modifier — increased min-width/height and font-size.
.caat-pagination__ellipsisNon-interactive ellipsis indicator for truncated page ranges.
.caat-pagination__resultsResults summary text (e.g., "Showing 1–10 of 97 results").
.caat-pagination-wrapperFlex wrapper for results text + centred pagination layout.

Sample CSS

.caat-pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
  padding: 0;
  margin: 0;
  list-style: none;
  font-family: var(--caat-font-primary, 'Libre Franklin', sans-serif);
}

.caat-pagination__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0.375rem 0.75rem;
  color: var(--caat-blue-900, #003366);
  background-color: #fff;
  border: 1px solid var(--caat-grey-200, #e9ecef);
  border-radius: var(--caat-radius, 0.375rem);
  text-decoration: none;
  transition: background-color 0.15s, border-color 0.15s;
}

.caat-pagination__link:hover {
  background-color: var(--caat-blue-100, #cfe2ff);
  border-color: var(--caat-blue-700, #004a99);
}

.caat-pagination__link:focus-visible {
  outline: 3px solid var(--caat-focus, #4d90fe);
  outline-offset: 2px;
}

.caat-pagination__item--active .caat-pagination__link {
  background-color: var(--caat-blue-900, #003366);
  border-color: var(--caat-blue-900, #003366);
  color: #fff;
  pointer-events: none;
}

.caat-pagination__item--disabled .caat-pagination__link {
  color: var(--caat-grey-200, #e9ecef);
  background-color: #f8f9fa;
  pointer-events: none;
  opacity: 0.65;
}

8. Accessibility

RequirementImplementation
LandmarkWrap in <nav> with a descriptive aria-label (e.g., "Page navigation" or "Search results pagination").
Current pageActive page link uses aria-current="page" so screen readers announce "current page".
Disabled linksUse aria-disabled="true" and tabindex="-1" to remove from tab order. CSS sets pointer-events: none.
Icon-only linksChevron-only Previous/Next links must include aria-label="Previous page" / aria-label="Next page".
Touch targetsMinimum 44×44px touch target on all interactive elements (WCAG 2.5.8 Target Size).
Colour contrastActive page (white on blue-900) meets WCAG 2.1 AA 4.5:1. Hover state meets 3:1 non-text contrast.
Focus visible3px solid focus ring using --caat-focus token with 2px offset. Visible on all interactive links.
KeyboardAll page links reachable via Tab. Enter/Space activates the link. Disabled links are skipped.
Reduced motionHover/focus transitions respect prefers-reduced-motion: reduce.

9. SEO

  • Use rel="prev" and rel="next" on Previous/Next <a> links to signal paginated series to search engines.
  • Each page's URL should have a distinct, crawlable href (e.g., ?page=2) — avoid JavaScript-only navigation.
  • Pagination links are server-side rendered as standard <a> elements, ensuring crawlability.
  • Include a canonical URL on each paginated page to prevent duplicate content issues.
  • The <nav> landmark allows bots to identify the pagination region and skip to main content.

10. Analytics

Analytics are captured through the shared data-analytics-component attribute on the component root (emitted automatically) and reported to the central data layer. Event names and payloads follow the organisation's standard analytics schema — defined centrally with the analytics team, not invented per component — so no bespoke tracking is specified here.

11. AEM Notes

  • Resource type: caat/components/pagination
  • Component group: CAAT Components – Navigation
  • Proxy: Create as a proxy component under /apps/caat/components/pagination.
  • Policies: Configure default items per page and size variant per template via content policies.
  • Sling Model: com.caat.core.models.PaginationModel — exposes totalItems, itemsPerPage, currentPage, totalPages, pages[], hasPrevious, hasNext, showResults.
  • HTL: pagination.html — uses data-sly-list to iterate page items, data-sly-attribute for active/disabled states.
  • Client library: caat.components.pagination (categories: caat.site).
  • Integration: Typically consumed by list/search components rather than placed independently. The parent component passes total count and current page to the pagination model.

12. Dialog Model (YAML)

Simplified model — shows field intent and Granite UI resource types, not the literal cq:dialog node tree (e.g. select options are authored as child items nodes).

title: Pagination
description: Multi-page navigation for lists, tables, and search results.
fields:
  - name: totalItems
    label: Total Items
    type: numberfield
    required: true
    min: 1
    helpText: Total number of items to paginate.

  - name: itemsPerPage
    label: Items Per Page
    type: select
    required: true
    options:
      - value: 10
        label: "10"
      - value: 20
        label: "20"
      - value: 50
        label: "50"
    defaultValue: 10
    helpText: Number of items shown per page.

  - name: navStyle
    label: Navigation Style
    type: select
    required: false
    options:
      - value: text
        label: Text (Previous / Next)
      - value: icon
        label: Icon (Chevron arrows)
    defaultValue: text
    helpText: Controls whether Previous/Next use text labels or chevron icons.

  - name: size
    label: Size
    type: select
    required: false
    options:
      - value: sm
        label: Small
      - value: default
        label: Default
      - value: lg
        label: Large
    defaultValue: default
    helpText: Controls the pagination touch-target and font size.

  - name: showResults
    label: Show Results Summary
    type: checkbox
    required: false
    defaultValue: false
    helpText: Displays "Showing X–Y of Z results" alongside the pagination.

  - name: ariaLabel
    label: Aria Label
    type: textfield
    required: false
    maxLength: 80
    defaultValue: Page navigation
    helpText: Custom aria-label for the nav element. Important for pages with multiple pagination instances.

13. QA Acceptance Checklist

  • Basic pagination renders Previous, page numbers 1–5, and Next correctly.
  • Previous link is disabled on page 1; Next link is disabled on the last page.
  • Active page shows blue-900 background with white text and aria-current="page".
  • Icon variant renders chevron-left and chevron-right with appropriate aria-label.
  • Small, Default, and Large sizes render with correct min-width/min-height touch targets.
  • Results text ("Showing X–Y of Z results") displays correctly when enabled.
  • Ellipsis renders for truncated page ranges (total pages > 7) with aria-hidden="true".
  • Focus ring (3px solid, --caat-focus) is visible on all interactive links.
  • Keyboard navigation: Tab reaches all enabled links; disabled links are skipped.
  • Hover state applies blue-100 background on non-active, non-disabled links.
  • Pagination does not render when total pages ≤ 1.
  • Responsive: pagination centres and simplifies at mobile breakpoint (< 576px).
  • Analytics events (pagination-click, pagination-view) fire with correct data attributes.
  • Cross-browser tested: Chrome, Firefox, Safari, Edge.

14. Definition of Done

AreaDone means
Design Figma component matches this contract. All size variants (sm, default, lg) documented. Icon and text navigation styles provided. Results summary layout included. Responsive behaviour defined.
Development AEM proxy component created. HTL template emits semantic HTML per Section 6. CSS uses design tokens exclusively. All states (active, disabled, hover, focus) implemented. Touch targets meet 44px minimum. Unit tests pass.
QA All items in the QA Acceptance Checklist (Section 13) pass. Manual accessibility audit completed. Screen-reader testing (NVDA, VoiceOver) confirms aria-current and disabled state announcements. Cross-browser tested.
Launch Component deployed to AEM production. Integrated with list/search parent components. Content authors trained. Documentation published. Analytics dashboards confirmed receiving events.