Creating Loader with box-shadow

Antonija Dimoska
Codeart
Published in
9 min readDec 10, 2021

You know when your internet is so slow or the page loads slower, you click on something and wait for an eternity before you know something is happening?

Most of you will think it’s frozen or it’s not working and will leave the site.

Because of that reason, to keep as many visitors as possible, most developers use loaders or spinners. They tell the users that something is happening, it takes some time, but the site is working, which is a good user experience.

Simple spinners ahead:

Lately, the loaders and spinners have become more creative, so people don’t lose interest while they wait for something to happen.

This post is meant as a short tutorial about an interesting way to make creative spinners with CSS, without using actual GIFs, where you have all the control.

Now, the following loaders, may look simple, but if you know how they work, you can create more complicated and more creative loaders by using one or two DOM elements.

The possibilities are infinite!

So let’s get started.

There are two loaders that I would like to present to you today, one was created by mistake while trying to make the second one. I loved it, so I kept it.

The method used in creating both of them, will help you to create many others, with different shapes, colors, animations.

Let’s break them down.

Blue Loader

All the magic happens on one DOM element, one div that has the class loader.

<div class=”loader”></div>

On that class, the style added, namely:

.loader {
position: relative;
width: 100%;
height: 100vh;
}

is meant to cover the whole screen. If you need to cover only a section, adjust it accordingly. The one thing that must be present, is declaring the position to be relative, because the pseudo elements attached to this class will be absolute, relative to the parent.

This following part is meant for the overlay of the loader. If it isn’t there, the loader will appear on top of the content, and it might be unnoticeable.

.loader::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: #1D1E22;
z-index: 999;
}

Now we have reached the part where the magic happens, but first let’s explain the CSS properties that are used.

Box-shadow is a property that attaches shadows to an element.

Here is an example of the shorthand value:

box-shadow: 5px 15px 0 10px rgba(0,0,0,.4);

The first value represents offset-x, meaning how much will the shadow be moved along the X axis from the center of the element on which it is applied.

The second value represents offset-y, meaning how much will the shadow be moved along the Y axis from the center of the element on which it is applied.

The third value represents blur-radius, meaning how much the shadow will be blurred. If it’s 0, there will be no blur on the edges.

The forth value represents spread-radius, meaning how much the shadow will be spread outside the borders of the element to which it is applied.

The fifth value is the background-color of the shadow.

Knowing these, there is no limit to what you can do using only shadows.

So after we set the overlay, now we need to set the pseudo element where the shadows will be.

.loader::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
z-index: 99999;
width: 16px;
height: 16px;
border-radius: 50px;
animation: loader 1.1s infinite ease;
}

We position the element to the middle of the screen. Give it width and height 16px and make it round by adding border-radius.

If you look at it at the moment, there will be nothing on the screen. If you add background-color: red, you will get this:

The shadows will be applied to this element. Because it is round, they will be round too. We don’t need the red background, so if you are testing, remove it.

Next, we add the animation.

animation: loader 1.1s infinite ease;

loader is the name of the animation.
1.1s is the animation duration time.
infinite tells us how many times the animation will repeat itself before it stops, in this case it will never stop.
ease is the transition easing of the animation.

And we reached the part where the animation happens.

@keyframes loader {
0%,
100% {
box-shadow:
0 -2.6em 0 0 rgba(1, 160, 198, 1),
1.8em -1.8em 0 0 rgba(1, 160, 198, .2),
2.5em 0 0 0 rgba(1, 160, 198, .2),
1.75em 1.75em 0 0 rgba(1, 160, 198, .2),
0 2.5em 0 0 rgba(1, 160, 198, .2),
-1.8em 1.8em 0 0 rgba(1, 160, 198, .2),
-2.6em 0 0 0 rgba(1, 160, 198, .5),
-1.8em -1.8em 0 0 rgba(1, 160, 198, .7);
}
12.5% {
box-shadow:
0 -2.6em 0 0 rgba(1, 160, 198, .7),
1.8em -1.8em 0 0 rgba(1, 160, 198, 1),
2.5em 0 0 0 rgba(1, 160, 198, .2),
1.75em 1.75em 0 0 rgba(1, 160, 198, .2),
0 2.5em 0 0 rgba(1, 160, 198, .2),
-1.8em 1.8em 0 0 rgba(1, 160, 198, .2),
-2.6em 0 0 0 rgba(1, 160, 198, .2),
-1.8em -1.8em 0 0 rgba(1, 160, 198, .5);
}
25% {
box-shadow:
0 -2.6em 0 0 rgba(1, 160, 198, .5),
1.8em -1.8em 0 0 rgba(1, 160, 198, .7),
2.5em 0 0 0 rgba(1, 160, 198, 1),
1.75em 1.75em 0 0 rgba(1, 160, 198, .2),
0 2.5em 0 0 rgba(1, 160, 198, .2),
-1.8em 1.8em 0 0 rgba(1, 160, 198, .2),
-2.6em 0 0 0 rgba(1, 160, 198, .2),
-1.8em -1.8em 0 0 rgba(1, 160, 198, .2);
}
37.5% {
box-shadow:
0em -2.6em 0 0 rgba(1, 160, 198, .2),
1.8em -1.8em 0 0 rgba(1, 160, 198, .5),
2.5em 0em 0 0 rgba(1, 160, 198, .7),
1.75em 1.75em 0 0 rgba(1, 160, 198, 1),
0em 2.5em 0 0 rgba(1, 160, 198, .2),
-1.8em 1.8em 0 0 rgba(1, 160, 198, .2),
-2.6em 0em 0 0 rgba(1, 160, 198, .2),
-1.8em -1.8em 0 0 rgba(1, 160, 198, .2);
}
50% {
box-shadow:
0 -2.6em 0 0 rgba(1, 160, 198, .2),
1.8em -1.8em 0 0 rgba(1, 160, 198, .2),
2.5em 0em 0 0 rgba(1, 160, 198, .5),
1.75em 1.75em 0 0 rgba(1, 160, 198, .7),
0 2.5em 0 0 rgba(1, 160, 198, 1),
-1.8em 1.8em 0 0 rgba(1, 160, 198, .2),
-2.6em 0 0 0 rgba(1, 160, 198, .2),
-1.8em -1.8em 0 0 rgba(1, 160, 198, .2);
}
62.5% {
box-shadow:
0 -2.6em 0 0 rgba(1, 160, 198, .2),
1.8em -1.8em 0 0 rgba(1, 160, 198, .2),
2.5em 0em 0 0 rgba(1, 160, 198, .2),
1.75em 1.75em 0 0 rgba(1, 160, 198, .5),
0 2.5em 0 0 rgba(1, 160, 198, .7),
-1.8em 1.8em 0 0 rgba(1, 160, 198, 1),
-2.6em 0 0 0 rgba(1, 160, 198, .2),
-1.8em -1.8em 0 0 rgba(1, 160, 198, .2);
}
75% {
box-shadow:
0 -2.6em 0 0 rgba(1, 160, 198, .2),
1.8em -1.8em 0 0 rgba(1, 160, 198, .2),
2.5em 0 0 0 rgba(1, 160, 198, .2),
1.75em 1.75em 0 0 rgba(1, 160, 198, .2),
0 2.5em 0 0 rgba(1, 160, 198, .5),
-1.8em 1.8em 0 0 rgba(1, 160, 198, .7),
-2.6em 0 0 0 rgba(1, 160, 198, 1),
-1.8em -1.8em 0 0 rgba(1, 160, 198, .2);
}
87.5% {
box-shadow:
0 -2.6em 0 0 rgba(1, 160, 198, .2),
1.8em -1.8em 0 0 rgba(1, 160, 198, .2),
2.5em 0 0 0 rgba(1, 160, 198, .2),
1.75em 1.75em 0 0 rgba(1, 160, 198, .2),
0 2.5em 0 0 rgba(1, 160, 198, .2),
-1.8em 1.8em 0 0 rgba(1, 160, 198, .5),
-2.6em 0em 0 0 rgba(1, 160, 198, .7),
-1.8em -1.8em 0 0 rgba(1, 160, 198, 1);
}
}

Even if it looks confusing and complicated, it’s really not. The code snippet below, it’s the first one in the keyframe.

box-shadow:
0 -2.6em 0 0 rgba(1, 160, 198, 1),
1.8em -1.8em 0 0 rgba(1, 160, 198, .2),
2.5em 0 0 0 rgba(1, 160, 198, .2),
1.75em 1.75em 0 0 rgba(1, 160, 198, .2),
0 2.5em 0 0 rgba(1, 160, 198, .2),
-1.8em 1.8em 0 0 rgba(1, 160, 198, .2),
-2.6em 0 0 0 rgba(1, 160, 198, .5),
-1.8em -1.8em 0 0 rgba(1, 160, 198, .7);

What do all these lines mean?

They are all the shadows applied to one element. Without animation this is how they look:

Like we said earlier, the first two numbers are offset-x and offset-y.

For better understanding, it’s how much the shadows are distanced from the center. You can see that visually here:

We don’t want blurry edges or spreading outside the borders of the element, so the third and forth value are set to 0.

The background needs to be in rgba format, so we can play with the opacity of the color. Actually that’s the only thing changing in this animation.

If you look closely at the code, you can see that everywhere throughout the keyframes animation, the values are the same. The only thing that’s changing is the color.

Here’s the final result:

Simple right?

On to the second one:

Colorful Loader

This one came to be by complete mistake. I was playing with the box-shadow values and suddenly it started doing things that I wasn’t planning it to.

For this one, instead of playing with the opacity of the color, I play with the offset-x and offset-y value. The spread and blur values are once again 0.

The setup is the same for the loader class and the ::before pseudo element.

The ::after pseudo element is this one:

.loader::after {
content: "";
width: 10px;
height: 10px;
border-radius: 100%;
z-index: 99999;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(0deg) scale(0, 0);
background-color: #0366b2;
box-shadow:
31px 45px 0 5px #3385ff,
13px 20px 0 2px #ff9967,
-31px -45px 0 5px #3385ff,
-13px -20px 0 2px #ff9967;
animation: loader 3s infinite, round 4s infinite;
}

The difference is, here we have box-shadow and background-color applied directly to the element, and we call two animations at the same time.

Also, we have added more values for the transform property. Not only does it center the element relatively to the parent, it sets the rotate value, default is 0 degrees, and it scales it 0 by both, x and y axis.

The animation called loader will last 3s and will repeat infinitely.
The animation called round will last 4s and will repeat infinitely.

Let’s see what these animations actually do.

This is the animation that is responsible for rotating and scaling the circles.

@keyframes loader {
0% {
transform: translate(-50%, -50%) rotate(0deg) scale(1, 1);
}
70% {
transform: translate(-50%, -50%) rotate(360deg) scale(.7, .7);
}
100% {
transform: translate(-50%, -50%) rotate(360deg) scale(1, 1);
}
}

When the animation starts, the element is centered, it’s not rotated and it is scaled to it’s normal size. This lasts for the first 70% of those 3s duration time we set.

Then, it stays centered, but it’s rotated a full circle (360deg) and it is scaled a bit smaller than it’s normal size.

At the end, it is centered, still rotated but it returns to the original size.

The other animation is responsible for moving the circles around.

@keyframes round {
0%,
100%{
box-shadow:
31px 45px 0 5px #3385ff,
13px 20px 0 2px #ff9967,
-31px -45px 0 5px #3385ff,
-13px -20px 0 2px #ff9967;
}
25% {
box-shadow:
-13px -20px 0 2px #ff9967,
31px 45px 0 5px #3385ff,
13px 20px 0 2px #ff9967,
-31px -45px 0 5px #3385ff;
}
50% {
box-shadow:
-31px -45px 0 5px #3385ff,
-13px -20px 0 2px #ff9967,
31px 45px 0 5px #3385ff,
13px 20px 0 2px #ff9967;
}
75% {
box-shadow:
-13px -20px 0 2px #ff9967,
31px 45px 0 5px #3385ff,
13px 20px 0 2px #ff9967,
-31px -45px 0 5px #3385ff;
}
}

As you can see, there are four shadows added, the shadows have different colors and positioning, and in each stage of the animation they are moved around.

The final result is this:

Pretty simple, right?

So by manipulating the box-shadow values, and adding animations with transform, you can get very interesting results for your page, and your users will be entertained while waiting for something to happen.

Win win, situation right?

Tip: Don’t forget to run the CSS code through the autoprefixer so the animation works on all the browsers.

If you have any questions, feel free to comment below.

Happy coding and experimenting and let your creativeness shine!

***

Starting a new project? Contact us if you a looking for a digital partner that will enable your business to flourish.

--

--