Skip to main content

Pagination Component

Modern aiab-pagination with accessibility, multiple variants, and responsive design

Overview

The Pagination component provides accessible navigation through large sets of content. It supports multiple styles, sizes, and advanced features like page jumping, page size selection, and information display. The component is fully responsive and includes keyboard navigation and screen reader support.

🎨 Multiple Styles

Default, pills, rounded, borderless, outlined, and compact aiab-pagination variants.

📏 Flexible Sizing

Compact, default, and large aiab-pagination sizes for different interface densities.

♿ Full Accessibility

ARIA labels, keyboard navigation, focus management, and screen reader support.

📱 Responsive Design

Adaptive layouts that work on all screen sizes with smart content hiding.

🔧 Advanced Features

Page jumping, page size selection, info display, and loading states.

⚡ Performance Optimized

Efficient rendering and smooth interactions with minimal DOM manipulation.

Basic Pagination

Default Pagination

Standard Pagination Navigation
<nav aria-label="Pagination Navigation" role="navigation">
  <ul class="aiab-pagination">
    <li class="pagination__item pagination__item--nav">
      <a href="#" class="pagination__link pagination__link--disabled" aria-label="Go to previous page">
        <div class="pagination__nav">
          <svg class="pagination__nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
          </svg>
          <span class="pagination__nav-text">Previous</span>
        </div>
      </a>
    </li>
    <li class="pagination__item pagination__item--current">
      <span class="pagination__current" aria-label="Page 1, current page" aria-current="page">1</span>
    </li>
    <li class="pagination__item">
      <a href="#" class="pagination__link" aria-label="Go to page 2">2</a>
    </li>
    <li class="pagination__item">
      <a href="#" class="pagination__link" aria-label="Go to page 3">3</a>
    </li>
    <!-- More pages... -->
    <li class="pagination__item">
      <span class="pagination__ellipsis" aria-hidden="true">…</span>
    </li>
    <li class="pagination__item">
      <a href="#" class="pagination__link" aria-label="Go to page 10">10</a>
    </li>
    <li class="pagination__item pagination__item--nav">
      <a href="#" class="pagination__link" aria-label="Go to next page">
        <div class="pagination__nav">
          <span class="pagination__nav-text">Next</span>
          <svg class="pagination__nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
          </svg>
        </div>
      </a>
    </li>
  </ul>
</nav>

Simple Pagination

Simple Previous/Next Pagination
<nav aria-label="Simple Pagination" role="navigation">
  <ul class="aiab-pagination aiab-pagination--simple">
    <li class="pagination__item">
      <a href="#" class="pagination__link" aria-label="Go to previous page">
        <div class="pagination__nav">
          <svg class="pagination__nav-icon">...</svg>
          <span class="pagination__nav-text">Previous</span>
        </div>
      </a>
    </li>
    <li class="pagination__item">
      <a href="#" class="pagination__link" aria-label="Go to next page">
        <div class="pagination__nav">
          <span class="pagination__nav-text">Next</span>
          <svg class="pagination__nav-icon">...</svg>
        </div>
      </a>
    </li>
  </ul>
</nav>

Pagination Variants

Pills Pagination

Rounded Pagination

Borderless Pagination

Outlined Pagination

<!-- Pills Pagination -->
<ul class="aiab-pagination aiab-pagination--pills">...</ul>

<!-- Rounded Pagination -->
<ul class="aiab-pagination aiab-pagination--rounded">...</ul>

<!-- Borderless Pagination -->
<ul class="aiab-pagination aiab-pagination--borderless">...</ul>

<!-- Outlined Pagination -->
<ul class="aiab-pagination aiab-pagination--outlined">...</ul>

Pagination Sizes

Compact Pagination

Default Pagination

Large Pagination

<!-- Compact Pagination -->
<ul class="aiab-pagination aiab-pagination--compact">...</ul>

<!-- Default Pagination -->
<ul class="aiab-pagination">...</ul>

<!-- Large Pagination -->
<ul class="aiab-pagination aiab-pagination--large">...</ul>

Advanced Pagination

Complete Pagination with Controls

Full-Featured Pagination Component
Showing 1 to 10 of 97 results
<div class="aiab-pagination-container">
  <div class="aiab-pagination-container__left">
    <div class="pagination__info">
      Showing <span class="pagination__info-highlight">1</span> to
      <span class="pagination__info-highlight">10</span> of
      <span class="pagination__info-highlight">97</span> results
    </div>
  </div>

  <div class="aiab-pagination-container__center">
    <nav aria-label="Pagination" role="navigation">
      <ul class="aiab-pagination">
        <!-- Pagination items... -->
      </ul>
    </nav>
  </div>

  <div class="aiab-pagination-container__right">
    <div class="pagination__page-size">
      <label for="page-size">Show:</label>
      <select id="page-size" class="pagination__page-size-select">
        <option value="10">10</option>
        <option value="25">25</option>
        <option value="50">50</option>
      </select>
    </div>

    <div class="pagination__jump">
      <label for="jump-page">Go to:</label>
      <input type="number" id="jump-page" class="pagination__jump-input" min="1" max="10">
      <button class="pagination__jump-button">Go</button>
    </div>
  </div>
</div>

CSS Classes Reference

Base Classes

Class Description
.aiab-pagination Base aiab-pagination aiab-container
.pagination__item Individual aiab-pagination item
.pagination__link Pagination link element
.pagination__current Current page indicator
.pagination__ellipsis Ellipsis for truncated pages
.pagination__nav Navigation link aiab-container
.pagination__nav-icon Navigation arrow icon
.pagination__nav-text Navigation link text

Style Variants

Class Description
.aiab-pagination--simple Simple previous/next aiab-pagination
.aiab-pagination--pills Pill-shaped aiab-pagination buttons
.aiab-pagination--rounded Circular aiab-pagination buttons
.aiab-pagination--borderless Pagination without borders
.aiab-pagination--outlined Outlined aiab-pagination style

Size Modifiers

Class Description
.aiab-pagination--compact Compact aiab-pagination size
.aiab-pagination--large Large aiab-pagination size

State Classes

Class Description
.pagination__link--disabled Disabled aiab-pagination link
.aiab-pagination--loading Loading state aiab-pagination
.pagination__item--nav Navigation item modifier
.pagination__item--current Current page item modifier

Advanced Feature Classes

Class Description
.aiab-pagination-container Full aiab-pagination aiab-container with controls
.pagination__info Pagination information display
.pagination__info-highlight Highlighted information numbers
.pagination__page-size Page size selector aiab-container
.pagination__page-size-select Page size aiab-dropdown
.pagination__jump Page jump aiab-container
.pagination__jump-input Page jump input field
.pagination__jump-button Page jump button

JavaScript Integration

// Pagination component class
class Pagination {
  constructor(options = {}) {
    this.currentPage = options.currentPage || 1;
    this.totalPages = options.totalPages || 1;
    this.pageSize = options.pageSize || 10;
    this.totalItems = options.totalItems || 0;
    this.maxVisible = options.maxVisible || 5;
    this.showInfo = options.showInfo !== false;
    this.showJump = options.showJump || false;
    this.showPageSize = options.showPageSize || false;
    this.container = options.container;
    this.onChange = options.onChange || (() => {});

    this.init();
  }

  init() {
    if (this.container) {
      this.render();
      this.attachEvents();
    }
  }

  render() {
    const paginationHTML = this.generateHTML();
    this.container.innerHTML = paginationHTML;
  }

  generateHTML() {
    const pages = this.generatePages();
    const info = this.showInfo ? this.generateInfo() : '';
    const pageSize = this.showPageSize ? this.generatePageSize() : '';
    const jump = this.showJump ? this.generateJump() : '';

    return `
      <div class="aiab-pagination-container">
        <div class="aiab-pagination-container__left">
          ${info}
        </div>
        <div class="aiab-pagination-container__center">
          <nav aria-label="Pagination Navigation" role="navigation">
            <ul class="aiab-pagination">
              ${this.generatePrevButton()}
              ${pages.map(page => this.generatePageItem(page)).join('')}
              ${this.generateNextButton()}
            </ul>
          </nav>
        </div>
        <div class="aiab-pagination-container__right">
          ${pageSize}
          ${jump}
        </div>
      </div>
    `;
  }

  generatePages() {
    const pages = [];
    const half = Math.floor(this.maxVisible / 2);
    let start = Math.max(1, this.currentPage - half);
    let end = Math.min(this.totalPages, start + this.maxVisible - 1);

    // Adjust start if we're near the end
    if (end - start + 1 < this.maxVisible) {
      start = Math.max(1, end - this.maxVisible + 1);
    }

    // Add first page and ellipsis if needed
    if (start > 1) {
      pages.push(1);
      if (start > 2) {
        pages.push('ellipsis');
      }
    }

    // Add visible pages
    for (let i = start; i <= end; i++) {
      pages.push(i);
    }

    // Add ellipsis and last page if needed
    if (end < this.totalPages) {
      if (end < this.totalPages - 1) {
        pages.push('ellipsis');
      }
      pages.push(this.totalPages);
    }

    return pages;
  }

  generatePageItem(page) {
    if (page === 'ellipsis') {
      return `
        <li class="pagination__item">
          <span class="pagination__ellipsis" aria-hidden="true">…</span>
        </li>
      `;
    }

    if (page === this.currentPage) {
      return `
        <li class="pagination__item pagination__item--current">
          <span class="pagination__current" aria-label="Page ${page}, current page" aria-current="page">
            ${page}
          </span>
        </li>
      `;
    }

    return `
      <li class="pagination__item">
        <a href="#" class="pagination__link" data-page="${page}" aria-label="Go to page ${page}">
          ${page}
        </a>
      </li>
    `;
  }

  generatePrevButton() {
    const disabled = this.currentPage === 1 ? 'pagination__link--disabled' : '';
    const href = this.currentPage === 1 ? '#' : `#page-${this.currentPage - 1}`;

    return `
      <li class="pagination__item pagination__item--nav">
        <a href="${href}" class="pagination__link ${disabled}" data-page="${this.currentPage - 1}" aria-label="Go to previous page">
          <div class="pagination__nav">
            <svg class="pagination__nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
            </svg>
            <span class="pagination__nav-text">Previous</span>
          </div>
        </a>
      </li>
    `;
  }

  generateNextButton() {
    const disabled = this.currentPage === this.totalPages ? 'pagination__link--disabled' : '';
    const href = this.currentPage === this.totalPages ? '#' : `#page-${this.currentPage + 1}`;

    return `
      <li class="pagination__item pagination__item--nav">
        <a href="${href}" class="pagination__link ${disabled}" data-page="${this.currentPage + 1}" aria-label="Go to next page">
          <div class="pagination__nav">
            <span class="pagination__nav-text">Next</span>
            <svg class="pagination__nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
            </svg>
          </div>
        </a>
      </li>
    `;
  }

  generateInfo() {
    const start = ((this.currentPage - 1) * this.pageSize) + 1;
    const end = Math.min(this.currentPage * this.pageSize, this.totalItems);

    return `
      <div class="pagination__info">
        Showing <span class="pagination__info-highlight">${start}</span> to
        <span class="pagination__info-highlight">${end}</span> of
        <span class="pagination__info-highlight">${this.totalItems}</span> results
      </div>
    `;
  }

  generatePageSize() {
    const options = [10, 25, 50, 100];
    const optionsHTML = options.map(size =>
      `<option value="${size}" ${size === this.pageSize ? 'selected' : ''}>${size}</option>`
    ).join('');

    return `
      <div class="pagination__page-size">
        <label for="page-size-select">Show:</label>
        <select id="page-size-select" class="pagination__page-size-select">
          ${optionsHTML}
        </select>
      </div>
    `;
  }

  generateJump() {
    return `
      <div class="pagination__jump">
        <label for="jump-page-input">Go to:</label>
        <input type="number" id="jump-page-input" class="pagination__jump-input"
               min="1" max="${this.totalPages}" placeholder="${this.currentPage}">
        <button class="pagination__jump-button">Go</button>
      </div>
    `;
  }

  attachEvents() {
    this.container.addEventListener('click', (e) => {
      e.preventDefault();

      const link = e.target.closest('.pagination__link:not(.pagination__link--disabled)');
      const jumpButton = e.target.closest('.pagination__jump-button');

      if (link) {
        const page = parseInt(link.dataset.page);
        if (page >= 1 && page <= this.totalPages) {
          this.goToPage(page);
        }
      } else if (jumpButton) {
        const input = this.container.querySelector('.pagination__jump-input');
        const page = parseInt(input.value);
        if (page >= 1 && page <= this.totalPages) {
          this.goToPage(page);
        }
      }
    });

    this.container.addEventListener('change', (e) => {
      if (e.target.classList.contains('pagination__page-size-select')) {
        const newPageSize = parseInt(e.target.value);
        this.updatePageSize(newPageSize);
      }
    });

    // Keyboard navigation
    this.container.addEventListener('keydown', (e) => {
      if (e.target.classList.contains('pagination__jump-input') && e.key === 'Enter') {
        const page = parseInt(e.target.value);
        if (page >= 1 && page <= this.totalPages) {
          this.goToPage(page);
        }
      }
    });
  }

  goToPage(page) {
    if (page !== this.currentPage && page >= 1 && page <= this.totalPages) {
      this.currentPage = page;
      this.render();
      this.attachEvents();
      this.onChange({
        currentPage: this.currentPage,
        pageSize: this.pageSize
      });
    }
  }

  updatePageSize(newPageSize) {
    this.pageSize = newPageSize;
    this.totalPages = Math.ceil(this.totalItems / this.pageSize);
    this.currentPage = 1; // Reset to first page
    this.render();
    this.attachEvents();
    this.onChange({
      currentPage: this.currentPage,
      pageSize: this.pageSize
    });
  }

  // Public methods
  update(options) {
    Object.assign(this, options);
    this.totalPages = Math.ceil(this.totalItems / this.pageSize);
    this.render();
    this.attachEvents();
  }
}

// Usage example
const pagination = new Pagination({
  aiab-container: document.getElementById('aiab-pagination-container'),
  currentPage: 1,
  totalItems: 97,
  pageSize: 10,
  maxVisible: 5,
  showInfo: true,
  showJump: true,
  showPageSize: true,
  onChange: (data) => {
    console.log('Page changed:', data);
    // Fetch new data here
  }
});

Best Practices

Accessibility Guidelines

UX Considerations

Performance Tips

Implementation Notes