CSS — Pagination
The markup
Pagination is another list of links:
<nav aria-label="Pagination">
<ul class="pagination">
<li><a href="?page=1" aria-label="Previous">‹</a></li>
<li><a href="?page=1">1</a></li>
<li><a href="?page=2" class="active" aria-current="page">2</a></li>
<li><a href="?page=3">3</a></li>
<li><span class="ellipsis">…</span></li>
<li><a href="?page=12">12</a></li>
<li><a href="?page=3" aria-label="Next">›</a></li>
</ul>
</nav>
Two accessibility signals: aria-current="page" marks where the user is; aria-label explains arrow-only buttons.
The styling
.pagination {
display: flex;
gap: 4px;
list-style: none;
padding: 0;
justify-content: center;
}
.pagination a,
.pagination span {
display: flex; /* perfect centering */
align-items: center;
justify-content: center;
min-width: 40px; /* touch-target friendly */
height: 40px;
padding: 0 8px;
border-radius: 8px;
text-decoration: none;
color: #334155;
transition: background .15s;
}
.pagination a:hover { background: #f0fdfa; color: #0d9488; }
.pagination .active {
background: #0d9488;
color: white;
pointer-events: none; /* current page isn't clickable */
}
.pagination .disabled {
opacity: .4;
pointer-events: none;
}
.ellipsis {
color: #94a3b8;
}
(Row of 40px rounded squares; teal pill on the current page; dimmed edges at boundaries.)
Edge states — prev/next disabling
On page 1, Previous shouldn't pretend to work:
<li><span class="disabled" aria-disabled="true">‹</span></li>
Swap <a> for a non-interactive span (or omit the link) when there's nowhere to go.
Smart number truncation
Ten pages fit; a hundred don't. Show a window around the current page:
function pageList(current, total) {
const pages = new Set([1, total, current - 1, current, current + 1]);
// always keep 2 near the edges
if (current <= 3) [2, 3, 4].forEach(p => p < total && pages.add(p));
if (current >= total - 2) [total-1, total-2, total-3].forEach(p => p > 1 && pages.add(p));
return [...pages].sort((a,b) => a-b);
}
// pageList(6, 40) → [1, 5, 6, 7, 40] → render "… 5 6 7 …"
Insert .ellipsis spans wherever numbers jump by more than one.
Variants
Load-more button (often better UX)
.load-more {
display: block;
margin: 24px auto;
padding: 12px 32px;
}
Infinite scroll and load-more beat numbered pagination for feeds; keep numbered pagination for search results where users want to jump/judge depth.
Compact prev/next only
.pager {
display: flex;
justify-content: space-between;
}
Blog-post style: "← Older | Newer →". Zero math needed.
Server-side note
Links (?page=2) stay crawlable and work without JS — pagination is one place plain links genuinely beat click handlers.
Mini Practice
- Base pagination with all four state styles.
- Disable prev on page 1, next on last page.
- Implement pageList(); render windowed numbers with ellipses.
- Add keyboard focus-visible rings matching hover.
- Build the blog-style prev/next pager variant.
Next: multiple columns →
Related Topics
Frequently Asked Questions about Pagination
What is Pagination in CSS?
Pagination is a fundamental concept in CSS. This lesson explains it step by step with clear examples, making it easy for beginners to understand.
How do I learn Pagination?
Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Pagination.
Why is Pagination important in CSS?
Pagination is essential for CSS development. Understanding this concept will help you write better code and solve real-world problems more effectively.