How to Make a Pure CSS Loading Animation (in 5 Minutes)

✔︎ Last updated on September 29th, 2022

If you are wondering how to make a pure CSS loading animation, then you have come to the right place.

In this article, I will explain step by step how you can create the following loading animation with pure CSS (without touching a single line of JavaScript).

At the end of this article, you will also find the complete code in a Codepen block with which you can play around and experiment.

I have also written an ultimate beginner’s guide to CSS Animation. If you are new to CSS animation, I strongly suggest you check it out before reading this article.

Now, let’s begin with today’s tutorial.

Setting up the scene

I have only one element (pink square) which will rotate so here’s my entire HTML code for this project –

HTML
<div class="loader"></div>

Let’s come to the CSS part.

I have applied the following styles on the body element –

  • I like to place elements in the center of the page so I will set display: grid and place-items: center.
  • Furthermore, as I want to vertically center the loader on the page, I will have to set a height of 100vh. If you don’t set a height, the loader will touch the upper border of the page.

Our code looks like this –

CSS
body {	
/*  center the child elements on the page */
    display: grid;
    place-items: center;
    height: 100vh;
}

Now, it’s time to design our rotating loader.

I am applying some basic styling to it. Since it is a square, I have set equal height and width of 150px. I have also set the background-color to #ff0077.

Here’s our code –

CSS
.loader {
/*  set appearance of the square */
    width: 150px;
    height: 150px;
    background-color: #ff0077;
}

Our output so far looks like this –

Pure CSS loading animation

Now it’s time to animate this.

Animating the loader

I will use the animation shorthand property to set animation parameters on this loader.

Specifically,

  • I have to name the animation, I will call it load.
  • I have to set a duration, I will set it at 4s (4 seconds). The default animation-duration in CSS is 0 so you must set a non-zero duration for an animation to run.
  • The loading animations don’t run a definite number of times. They are essentially designed to run until the user’s wait is over, so we will set them to run for infinite times.

We can set all these things using the following line of code –

CSS
.loader {
    /* 	write code so far here */
	
    /* 	set animation properties */
    animation: load 4s infinite;
}

Now, all there remains to be done is to define our keyframes.

As we can see in the featured image above, our animation is executed in 4 steps. At every step, the box rotates by 90 degrees.

So we can define 4 keyframes at 25%, 50%, 75%, and 100%. At each keyframe, we will rotate the box along the Z-axis by 90 degrees more than the previous keyframe. So, at 25%, the rotation will be 90 degrees; at 50%, it will be of 180 degrees, and so on.

Now the question arises, why along the z-axis?

Because the square is rotating around an imaginary pivot point on the computer screen. An imaginary line perpendicular to the computer screen passing through the pivot point will be the z-axis. So we will use rotateZ() function.

This is our code –

CSS
/* define keyframes */
@keyframes load {
    25% {
	transform: rotateZ(90deg);
    }
    50% {
	transform: rotateZ(180deg);
    }
    75% {
	transform: rotateZ(270deg);
    }
    100% {
	transform: rotateZ(1turn);
    }
}

This is our output –

Pure CSS loading animation (box rotating on its center)

Uh-Oh! The box is rotating around its center. That’s not what we want.

We want it to rotate around its top-left corner. Fortunately, we have a property that lets us change its center of rotation – transform-origin property.

By default, the rotational axis of objects in CSS passes through their center. We have to transform that rotational axis from the center to the top left corner; transform-origin property lets us do exactly that.

We can use it like this –

CSS
.loader {
    /* write code so far here /*        

    /*  any of the following works */
	transform-origin: left top; //either this
        transform-origin: 0 0;   // or this.
}

This is our output –

CSS loading animation without javascript. Box rotating around its corner.

Much better!

There’s one more thing that I want to change here – timing-function.

By default, the ease timing is set on animations in CSS. But I do not want that here.

I want the animation to be snappy. I want it to look like the square is tied with a rubber band. The square tries to rotate but the rubber band keeps pulling. And then, suddenly, the band snaps due to stretching, and the square slams into place.

In my trials and errors on www.cubic-bezier.com, I found the values cubic-bezier(1,-0.01,.89,0) make the animation feel elastic.

We can put these values in the animation shorthand property itself, like this –

CSS
.loader {
    /* 	write code so far here */
    
    /* 	set animation properties and timing    function */
    animation: load 4s infinite cubic-bezier(1, -0.01, .89, 0);
}

And this is our final output –

<div class="loader"></div>
body {
/* 	center the child elements on the page */
	display: grid;
	place-items: center;
	height: 100vh;
}

.loader {
/* 	set appearance of the square */
	width: 150px;
	height: 150px;
	background-color: #ff0077;
	
/* 	set animation properties */
	animation: load 4s infinite cubic-bezier(1,-0.01,.89,0);
	transform-origin: left top;
}

/* define keyframes */
@keyframes load {
	25% {
		transform: rotatez(90deg);
	}
	50% {
		transform: rotateZ(180deg);
	}
	75% {
		transform: rotateZ(270deg);
	}
	100% {
		transform: rotateZ(1turn);
	}
}

I can think of one more way of improving this. We can play a pop sound whenever it completes a 90-degree turn, to give it an even more realistic effect but it will require some JavaScript code to play.

That brings us to the end of our tutorial.

I hope you liked this article. I would love to know if you have a different approach to the same problem.

Please drop a line below if you have any queries.

Leave a Comment