DEV Community


Posted on • Originally published at


Beautiful CSS pagination and how it's done

Image description


For HTML we have a scroller element with pages inside.

Each page element has a class named 'page', and 'active', if that page is currently selected.

<div class="scroller">
    <div class="page">1</div>
    <div class="page">2</div>
    <div class="page active">3</div>
    <div class="page">4</div>
    <div class="page">5</div>
Enter fullscreen mode Exit fullscreen mode


For javascript, we'll select all page elements on load.

Then we'll loop through them and add a click event listener.

On click, we'll remove active class from element that has it and add it to an element that was clicked.

window.onload = () => {
    let pages = document.querySelectorAll('.page')

    for (let i = 0; i < pages.length; i++) {
        pages[i].onclick = () => {
Enter fullscreen mode Exit fullscreen mode

Now we'll create previous mentioned functions.

For setActiveClass we'll simply just all a class 'active' to class list.

And for removeActiveClass, we'll just select the element that has 'active' class and remove it.

const setActiveClass = (el) => el.classList.add('active')

const removeActiveClass = () => {
    let activeEl = document.querySelector('.active')
Enter fullscreen mode Exit fullscreen mode

Simple, huh?


For CSS, first we'll style the scroller element.

We'll just align page numbers inside, using flexbox and add some gap between them.

.scroller {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 10px;
Enter fullscreen mode Exit fullscreen mode

Now the pages.

We'll set the width and height to 30px with 100% border radius. This will appear as circle.

Using flexbox, we'll center the number inside.

Of course, we'll set transition to 0.5 seconds so that the hover effect (which we'll add later) is smooth.

Lastly, we'll set cursor to pointer and text to white and bold.

.page {
    width: 30px;
    height: 30px;
    border-radius: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    transition: .5s;
    cursor: pointer;
    color: #fff;
    font-weight: 500;
Enter fullscreen mode Exit fullscreen mode

On page hover, we'll just increase it's size using transform scale and add transition so the transform is smooth.

.page:hover {
    transform: scale(1.2);
    transition: .2s;
Enter fullscreen mode Exit fullscreen mode

Now we'll set background color to all pages.

Each will have different color. From yellow to brown.

.page:first-child {
    background-color: rgb(255, 225, 0);
.page:nth-child(2) {
    background-color: rgb(255, 200, 0);
.page:nth-child(3) {
    background-color: rgb(255, 157, 0);
.page:nth-child(4) {
    background-color: rgb(255, 123, 0);
.page:nth-child(5) {
    background-color: rgb(211, 106, 0);
Enter fullscreen mode Exit fullscreen mode

For active page, we'll increase the element's size by 50% using transform property, so that the active page is different.

We'll also change it's background color, to red.

.active {
    transform: scale(1.5);
    background-color: rgb(245, 57, 0) !important;
Enter fullscreen mode Exit fullscreen mode

On hover over active page, we'll overwrite the previous transform property from scale 1.2 to scale 1.5 and set the default cursor, so that it doesn't look clickable.

.active:hover {
    transform: scale(1.5);
    cursor: default;
Enter fullscreen mode Exit fullscreen mode

And that is it.

Video tutorial:

You can find the whole code with video tutorial here.

Thank you for reading ❤️

Top comments (2)

naubit profile image
Al - Naubit

It looks really cool, great job!

lukeshiru profile image
Luke Shiru

A few things to improve:

  • Use a (with an href to the corresponding page) instead of div for the .page elements (ideally with a aria-current="page" for the active page).
  • The main container should be a nav instead of a div (ideally with a aria-label="pagination").
  • Instead of running that script on load, you can just have this as a deferred script.
const activate = element => {
    element.setAttribute("aria-current", "page");

const deactivateActive = () => {
    const active = document.querySelector("");

document.querySelectorAll(".page").forEach(page => {
    page.addEventListener("click", ({ currentTarget }) => {
Enter fullscreen mode Exit fullscreen mode


Need a better mental model for async/await?

Check out this classic DEV post on the subject.

⭐️🎀 JavaScript Visualized: Promises & Async/Await

async await