3 Pure CSS4 with JSPublished 2026-10-05Free · no sign-up
Seven ways to move through results: numbered pages with ellipses, a simple previous and next with Page 4 of 12, a Load more button, a jump-to-page field, pill-style pages, a data table footer with rows per page and infinite scroll that stops at a known end.
Previous and next links around page numbers, with an ellipsis for skipped ranges. It is a nav with an aria-label, the current page is marked with aria-current="page", and the arrows have full names such as Previous page.
The minimal version for mobile and blogs: Previous, Page 4 of 12 and Next. On the first or last page the unavailable link becomes plain text with aria-disabled rather than a link that goes nowhere.
Instead of pages, a Load more button appends the next batch and says how many are showing out of the total. Focus moves to the first new item so keyboard users continue from where the new content starts, and the button disappears when everything is loaded.
View code
<div class="pg3"><ul><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul><p aria-live="polite">Showing 3 of 9</p><button>Load more</button></div>
var ul = document.querySelector('.pg3 ul'), p = document.querySelector('.pg3 p'), b = document.querySelector('.pg3 button'), n = 3, total = 9;
b.onclick = function () { var first; for (var i = 0; i < 3 && n < total; i++) { n++; var li = document.createElement('li'); li.textContent = 'Item ' + n; li.tabIndex = -1; ul.appendChild(li); if (!first) first = li; } p.textContent = 'Showing ' + n + ' of ' + total; if (first) first.focus(); if (n >= total) b.remove(); };
04. Jump to Page Field
PaginationInput
For long result sets, a small form lets people type a page number and go, between the previous and next buttons. The number input has min and max set to the real range and a visible label, and the form submits with Enter.
document.querySelector('.pg4').addEventListener('submit', function (e) { e.preventDefault(); });
05. Pill Pagination
PaginationStyle
Page links inside one rounded pill container, with the current page as a raised white pill. It is the same accessible markup as plain numbered pagination, so the style can change freely without touching the structure.
The footer of a data table: a rows-per-page select, the visible range such as 21-30 of 87, and previous and next buttons. The range text updates as you page, and the buttons are disabled at the ends, which a real disabled attribute communicates.
var w = document.querySelector('.pg6'), sel = w.querySelector('select'), p = w.querySelector('p'), bs = w.querySelectorAll('button'), total = 87, start = 0;
function draw() { var n = +sel.value, end = Math.min(start + n, total); p.textContent = (start + 1) + '-' + end + ' of ' + total; bs[0].disabled = start === 0; bs[1].disabled = end >= total; }
bs[0].onclick = function () { start = Math.max(0, start - +sel.value); draw(); };
bs[1].onclick = function () { start = start + +sel.value; draw(); };
sel.onchange = function () { start = 0; draw(); };
07. Infinite Scroll with a Sentinel
PaginationInfinite scroll
More items load automatically when a sentinel at the end of the list scrolls into view. A status line says what is happening, the list stops at a known end with a clear message, and a footer stays reachable because loading is capped.
var box = document.querySelector('.pg7'), ul = box.querySelector('ul'), st = box.querySelector('.st'), n = 0, max = 30, busy = false;
function more() { if (busy || n >= max) return; busy = true; st.textContent = 'Loading…'; setTimeout(function () { for (var i = 0; i < 6 && n < max; i++) { n++; var li = document.createElement('li'); li.textContent = 'Post ' + n; ul.appendChild(li); } busy = false; st.textContent = n >= max ? 'You have reached the end. 30 posts.' : 'Showing ' + n + ' posts'; }, 400); }
new IntersectionObserver(function (es) { if (es[0].isIntersecting) more(); }, { root: box, rootMargin: '80px' }).observe(box.querySelector('.sent'));
About these CSS Pagination
Pagination is navigation
Wrap page links in <nav aria-label="Pagination">, mark the current page with aria-current="page" and give arrow links real names such as Previous page.
Pages are links when they have URLs
If each page has its own URL, use links, so people can open them in new tabs and search engines can follow them. Use buttons only when the page changes without a new URL.
Load more moves focus
After loading more items, move focus to the first new one and announce the new count. Otherwise keyboard users are left on a button far from the new content.
Infinite scroll needs an end
Endless feeds hide the footer and make it hard to return to an item. Cap automatic loading, say when the end is reached, or offer a Load more button instead.
Frequently asked questions
Which pattern is best for SEO?
Numbered pages with real URLs are the most crawlable.
How many page numbers should I show?
First, last, the current page and one or two either side, with ellipses for the gaps.
Is infinite scroll accessible?
It can be, with a status message and a known end, but Load more is often simpler.