Back

Creating a Text-Based Pagination Component

A tutorial on how to build a clean pagination component using text labels for better readability.

HTML

Let's create a pagination component that uses descriptive text labels instead of icons. This can improve accessibility and make the component more intuitive for users:

        <nav class="text-pagination" aria-label="Page navigation">
          <div class="page-controls">
            <button class="page-link prev" id="prev-page" aria-label="Go to previous page">Previous</button>
            
            <div class="page-numbers">
              <button class="page-link number">1</button>
              <button class="page-link number">2</button>
              <button class="page-link number active">3</button>
              <button class="page-link number">4</button>
              <button class="page-link number">5</button>
            </div>
            
            <button class="page-link next" id="next-page" aria-label="Go to next page">Next</button>
          </div>
          
          <div class="page-info" id="page-info">Page 3 of 5</div>
        </nav>
      
The key aspects of our HTML structure: - We use a semantic nav element with an appropriate aria-label - Text-based "Previous" and "Next" buttons instead of icon buttons - Numbered page buttons in the middle - An informative text showing the current page and total page count - Clear class names that describe the purpose of each element

JavaScript

Now let's implement the JavaScript to handle the pagination interaction:

        document.addEventListener('DOMContentLoaded', () => {
          const prevButton = document.getElementById('prev-page');
          const nextButton = document.getElementById('next-page');
          const pageInfo = document.getElementById('page-info');
          const pageButtons = document.querySelectorAll('.page-link.number');
          
          // Set initial page
          let currentPage = 3;
          const totalPages = pageButtons.length;
          
          // Update UI to reflect current page
          function updatePageUI() {
            // Update page buttons
            pageButtons.forEach((button, index) => {
              const pageNum = index + 1;
              if (pageNum === currentPage) {
                button.classList.add('active');
                button.setAttribute('aria-current', 'page');
                button.disabled = true;
              } else {
                button.classList.remove('active');
                button.removeAttribute('aria-current');
                button.disabled = false;
              }
            });
            
            // Update page info text
            pageInfo.textContent = `Page ${currentPage} of ${totalPages}`;
            
            // Enable/disable prev/next buttons
            prevButton.disabled = currentPage === 1;
            nextButton.disabled = currentPage === totalPages;
            
            // Add/remove disabled class for styling
            if (currentPage === 1) {
              prevButton.classList.add('disabled');
            } else {
              prevButton.classList.remove('disabled');
            }
            
            if (currentPage === totalPages) {
              nextButton.classList.add('disabled');
            } else {
              nextButton.classList.remove('disabled');
            }
          }
          
          // Set page and update UI
          function goToPage(pageNum) {
            currentPage = pageNum;
            updatePageUI();
            // In a real app, you would load the content for the new page here
            console.log(`Navigated to page ${currentPage}`);
          }
          
          // Add click handlers for prev/next buttons
          prevButton.addEventListener('click', () => {
            if (currentPage > 1) {
              goToPage(currentPage - 1);
            }
          });
          
          nextButton.addEventListener('click', () => {
            if (currentPage < totalPages) {
              goToPage(currentPage + 1);
            }
          });
          
          // Add click handlers for page number buttons
          pageButtons.forEach((button, index) => {
            button.addEventListener('click', () => {
              goToPage(index + 1);
            });
          });
          
          // Initialize the UI
          updatePageUI();
        });
      
Our JavaScript includes these key features: - A centralized function to update the UI state - ARIA attributes for better accessibility - Proper disabling of buttons when they're not usable - Event handlers for all interactive elements - Dynamic page information text

CSS

Let's style our text-based pagination with a clean, user-friendly design:

        /* Main container */
        .text-pagination {
          --primary: #3b82f6;
          --text: #374151;
          --border: #e5e7eb;
          --bg-hover: #f3f4f6;
          --disabled: #9ca3af;
          --active-bg: #3b82f6;
          --active-text: white;
          
          font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
          max-width: 600px;
          margin: 0 auto;
        }

        /* Controls container */
        .page-controls {
          display: flex;
          align-items: center;
          justify-content: space-between;
          margin-bottom: 8px;
        }

        /* Page numbers container */
        .page-numbers {
          display: flex;
          gap: 4px;
        }
      
Now let's style the pagination buttons:

        /* All pagination links */
        .page-link {
          background: transparent;
          border: 1px solid var(--border);
          padding: 8px 12px;
          border-radius: 4px;
          color: var(--text);
          font-size: 14px;
          font-weight: 500;
          cursor: pointer;
          transition: all 0.2s ease;
        }

        /* Prev/Next buttons */
        .page-link.prev,
        .page-link.next {
          padding: 8px 16px;
        }

        /* Number buttons */
        .page-link.number {
          min-width: 40px;
          text-align: center;
        }

        /* Hover effects */
        .page-link:hover:not(:disabled):not(.active) {
          background-color: var(--bg-hover);
          border-color: #d1d5db;
        }

        /* Active state */
        .page-link.active {
          background-color: var(--active-bg);
          color: var(--active-text);
          border-color: var(--active-bg);
        }

        /* Disabled state */
        .page-link:disabled,
        .page-link.disabled {
          opacity: 0.6;
          cursor: not-allowed;
        }
      
Let's style the page info text and add focus states for accessibility:

        /* Page info text */
        .page-info {
          text-align: center;
          font-size: 14px;
          color: var(--text);
          margin-top: 8px;
        }

        /* Focus styles for accessibility */
        .page-link:focus-visible {
          outline: 2px solid var(--primary);
          outline-offset: 2px;
        }
      
Finally, let's make our pagination responsive for mobile devices:

        /* Responsive adjustments */
        @media (max-width: 480px) {
          .page-controls {
            flex-direction: column;
            gap: 16px;
          }
          
          .page-numbers {
            order: 1;
          }
          
          .page-link.prev {
            order: 2;
            align-self: flex-start;
          }
          
          .page-link.next {
            order: 3;
            align-self: flex-end;
            margin-top: -44px; /* Align with prev button */
          }
        }
      
This text-based pagination component offers several advantages: - Improved clarity: Text labels like "Previous" and "Next" are more explicit than arrows - Better accessibility: Text labels provide clear meaning for screen readers - Informative context: The page information text helps users understand their location - User-friendly design: Clean layout with proper spacing and visual states - Mobile responsiveness: Adapts to different screen sizes with an optimized layout - Smooth interactions: Subtle transitions and clear hover/active states This pagination style is particularly well-suited for content-focused websites like blogs, articles, or documentation sites where clarity is more important than compact design.

Whole code

<nav class="text-pagination" aria-label="Page navigation">
  <div class="page-controls">
    <button class="page-link prev" id="prev-page" aria-label="Go to previous page">Previous</button>
    
    <div class="page-numbers">
      <button class="page-link number">1</button>
      <button class="page-link number">2</button>
      <button class="page-link number active">3</button>
      <button class="page-link number">4</button>
      <button class="page-link number">5</button>
    </div>
    
    <button class="page-link next" id="next-page" aria-label="Go to next page">Next</button>
  </div>
  
  <div class="page-info" id="page-info">Page 3 of 5</div>
</nav>

<script>
document.addEventListener('DOMContentLoaded', () => {
  const prevButton = document.getElementById('prev-page');
  const nextButton = document.getElementById('next-page');
  const pageInfo = document.getElementById('page-info');
  const pageButtons = document.querySelectorAll('.page-link.number');
  
  // Set initial page
  let currentPage = 3;
  const totalPages = pageButtons.length;
  
  // Update UI to reflect current page
  function updatePageUI() {
    // Update page buttons
    pageButtons.forEach((button, index) => {
      const pageNum = index + 1;
      if (pageNum === currentPage) {
        button.classList.add('active');
        button.setAttribute('aria-current', 'page');
        button.disabled = true;
      } else {
        button.classList.remove('active');
        button.removeAttribute('aria-current');
        button.disabled = false;
      }
    });
    
    // Update page info text
    pageInfo.textContent = `Page ${currentPage} of ${totalPages}`;
    
    // Enable/disable prev/next buttons
    prevButton.disabled = currentPage === 1;
    nextButton.disabled = currentPage === totalPages;
    
    // Add/remove disabled class for styling
    if (currentPage === 1) {
      prevButton.classList.add('disabled');
    } else {
      prevButton.classList.remove('disabled');
    }
    
    if (currentPage === totalPages) {
      nextButton.classList.add('disabled');
    } else {
      nextButton.classList.remove('disabled');
    }
  }
  
  // Set page and update UI
  function goToPage(pageNum) {
    currentPage = pageNum;
    updatePageUI();
    // In a real app, you would load the content for the new page here
    console.log(`Navigated to page ${currentPage}`);
  }
  
  // Add click handlers for prev/next buttons
  prevButton.addEventListener('click', () => {
    if (currentPage > 1) {
      goToPage(currentPage - 1);
    }
  });
  
  nextButton.addEventListener('click', () => {
    if (currentPage < totalPages) {
      goToPage(currentPage + 1);
    }
  });
  
  // Add click handlers for page number buttons
  pageButtons.forEach((button, index) => {
    button.addEventListener('click', () => {
      goToPage(index + 1);
    });
  });
  
  // Initialize the UI
  updatePageUI();
});
</script>

<style>
/* Main container */
.text-pagination {
  --primary: #3b82f6;
  --text: #374151;
  --border: #e5e7eb;
  --bg-hover: #f3f4f6;
  --disabled: #9ca3af;
  --active-bg: #3b82f6;
  --active-text: white;
  
  font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  max-width: 600px;
  margin: 0 auto;
}

/* Controls container */
.page-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}

/* Page numbers container */
.page-numbers {
  display: flex;
  gap: 4px;
}

/* All pagination links */
.page-link {
  background: transparent;
  border: 1px solid var(--border);
  padding: 8px 12px;
  border-radius: 4px;
  color: var(--text);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

/* Prev/Next buttons */
.page-link.prev,
.page-link.next {
  padding: 8px 16px;
}

/* Number buttons */
.page-link.number {
  min-width: 40px;
  text-align: center;
}

/* Hover effects */
.page-link:hover:not(:disabled):not(.active) {
  background-color: var(--bg-hover);
  border-color: #d1d5db;
}

/* Active state */
.page-link.active {
  background-color: var(--active-bg);
  color: var(--active-text);
  border-color: var(--active-bg);
}

/* Disabled state */
.page-link:disabled,
.page-link.disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Page info text */
.page-info {
  text-align: center;
  font-size: 14px;
  color: var(--text);
  margin-top: 8px;
}

/* Focus styles for accessibility */
.page-link:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* Responsive adjustments */
@media (max-width: 480px) {
  .page-controls {
    flex-direction: column;
    gap: 16px;
  }
  
  .page-numbers {
    order: 1;
  }
  
  .page-link.prev {
    order: 2;
    align-self: flex-start;
  }
  
  .page-link.next {
    order: 3;
    align-self: flex-end;
    margin-top: -44px; /* Align with prev button */
  }
}
</style>
      
Thank you for reading this article.

Comments

More paginations

Similar

See also