The nice thing to do when your website isn’t fully loaded is to play a little animation.
It lets the user know: “We’re working on it.”
“We know if it takes more than 2 seconds, you’ll leave forever.”
“My liege, we graciously offer you ~three blinking dots~.”
Animations act like a mantra. Like staring into a campfire. It hits your brain jingling keys hit a baby. Something primitive is opened inside, and we're transported to a place outside of time. And while we're there, no one notices the load…
Tutorial
In this blog, I try my hand at recreating various loaders I’ve seen on the web. In doing so, I attempt to make them as simple as possible, so they can easily be imported into your project or you can use the ideas to create your own.
Preliminary Junk
I set up a file structure that is an HTML file, index.html
and a CSS file, index.css
. The HTML looks like this:
<!DOCTYPE html>
<html lang="en">
<head>
<title>loaders</title>
<link rel="stylesheet" href="./index.css">
</head>
<body>
<div id="example-1" class="loader"></div>
<div id="example-2" class="loader">
<div id="bar-1" class="bar"></div>
<div id="bar-2" class="bar"></div>
<div id="bar-3" class="bar"></div>
</div>
<div id="example-3" class="loader"></div>
<div id="example-4" class="loader">
<div id="ball-container-1" class="ball-container">
<div id="ball-1" class="ball"></div>
</div>
<div id="ball-container-2" class="ball-container">
<div id="ball-2" class="ball"></div>
</div>
<div id="ball-container-3" class="ball-container">
<div id="ball-3" class="ball"></div>
</div>
</div>
</body>
</html>
I’ll explain each example in more detail when it’s relevant.
I set up some CSS variables for colors (from Coolors.co), flexbox
, and margin
s for the general layout of the demo.
:root {
--main-bg-color: #1A535C;
--loader-bg-color: #FF6B6B;
--loader-acc-color: #4ECDC4;
}
body{
display: flex;
align-items: center;
justify-content: space-around;
background-color: var(--main-bg-color);
}
.loader{
margin-top: 5em;
}
Example 1
Example 1 has the HTML of one div, set up like this:
<div id="example-1" class="loader"></div>
Design: I give it the same size width
and height
and a border-radius: 50%;
. By adding a border
, you can see that this creates a circle.
I style the border-top-color
with my accent color, var(--loader-acc-color)
. This overwrites the initial color in border
for just the top of the border.
Animation: I set up @keyframes example-one
so an element with this animation will rotate from 0 to 360 degrees.
I give the #example-1
element an animation
property. Using the shorthand, I set the duration to 2s
, iteration count to infinite
, and name as example-one
.
/* EXAMPLE 1 */
#example-1{
width: 3em;
height: 3em;
border-radius: 50%;
border: 0.75em solid var(--loader-bg-color);
border-top-color: var(--loader-acc-color); /* overrides top color to stand out */
animation: 2s infinite example-one;
}
@keyframes example-one{
from {transform: rotate(0deg)}
to {transform: rotate(360deg)}
}
Example 2
For Example 2, the HTML is a div container for three more divs. Each will be a “bar”.
<div id="example-2" class="loader">
<div id="bar-1" class="bar"></div>
<div id="bar-2" class="bar"></div>
<div id="bar-3" class="bar"></div>
</div>
Design: #example-2
is given some width
, height
and flexbox
properties to center the bars within.
Each bar is given a margin
, width
, and starting height
. I give them a background-color
and some fancy border
stuff for accent.
Animation: There are four parts to the example-two
animation, divided into 0%, 25%, 50%, and 100%.
At 0%, the height
is set to 2.5em
, the same as the initial height of each bar. From 0% to 25%, the height
grows to 5em
. From 25% to 50%, it shrinks back to 2.5em
where it will sustain until 100% when the animation restarts.
I give each bar an animation
property with a duration of 1.5s
, iteration count of infinite
, and connect it to @keyframes
by name, example-two
.
Finally, in order to stagger the play, I grab the individual bars by their IDs. bar-1
gets a delay of 0.25s
and bar-2
gets a delay of 0.5s
.
/* EXAMPLE 2 */
#example-2{
width: 5em;
height: 5em;
display: flex;
justify-content: center;
align-items: center;
}
.bar{
margin: 0.2em;
width: 0.75em;
height: 2.5em;
border: 0.1em solid var(--loader-bg-color);
border-left: 0.1em solid var(--loader-acc-color);
background-color: var(--loader-bg-color);
animation: 1.5s infinite example-two;
}
#bar-2{animation-delay: 0.25s}
#bar-3{animation-delay: 0.5s}
@keyframes example-two{
0% {height: 2.5em}
25% {height: 5em}
50% {height: 2.5em}
100% {height: 2.5em}
}
Example 3
Example 3 is one div.
<div id="example-3" class="loader"></div>
Design: I give #example-3
a width: 5em;
and height: 1em;
to make it a long rectangle. I give it a background-color
and some fancy border
stuff for an accent.
Animation: I use the transform
property again, but this time I flip the div from 0 to 180 degrees over its y axis using rotateY()
. Then I flip it to 360 degrees, back to its starting position.
/* EXAMPLE 3 */
#example-3{
width: 5em;
height: 1em;
border: 0.3em solid var(--loader-bg-color);
border-right: 0.3em solid var(--loader-acc-color);
background-color: var(--loader-bg-color);
animation: 3s infinite example-three;
}
@keyframes example-three{
from { transform: rotateY(0deg);}
50% { transform: rotateY(180deg);}
to { transform: rotateY(360deg);}
}
Example 4
Example 4, most complex loader, has HTML of a container div with three children divs. Each child is also a container div for a single div that will be shaped like a ball.
<div id="example-4" class="loader">
<div id="ball-container-1" class="ball-container">
<div id="ball-1" class="ball"></div>
</div>
<div id="ball-container-2" class="ball-container">
<div id="ball-2" class="ball"></div>
</div>
<div id="ball-container-3" class="ball-container">
<div id="ball-3" class="ball"></div>
</div>
</div>
Design: The outer most container, #example-4
contains flexbox
properties to center the loader within.
Each .ball-container
gets the same width
as height
to make it a square and a margin-right
to put some space in between.
Then, .ball-container
gets flexbox
properties to center the “ball” inside. This is important because as the ball changes sizes, I want it to remain centered.
Each .ball
gets an initial width
and height
of 0. A border-radius
of 50% turns them into circles, and a background-color
makes them visible.
Animation: The animation follows the same logic as example-2
except I am manipulating each ball’s height
and width
.
From 0% to 20%, they grow from 0
x 0
to 1.5em
x 1.5em
. I keep them at this size from 20% to 40%. From 40% to 90% they shrink down to 0
x 0
, and remain there from 90% to 100%.
I set each ball to have an animation property with a duration of 1.2s
, iteration count of infinite
, and name example-four
.
Finally, I grab each ball by their individual ID so I can add an animation-delay
to #ball-2
and #ball-3
. This staggers the animation.
/* EXAMPLE 4 */
#example-4{
display: flex;
align-items: center;
justify-content:center;
}
.ball-container{
width: 1.5em;
height: 1.5em;
margin-right: 0.8em;
<span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span>
<span class="nl">align-items</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="nl">justify-content</span><span class="p">:</span><span class="nb">center</span><span class="p">;</span>
}
.ball {
width: 0;
height: 0;
border-radius: 50%;
background-color: var(--loader-bg-color);
<span class="nl">animation</span><span class="p">:</span> <span class="m">1.2s</span> <span class="n">infinite</span> <span class="n">example-four</span><span class="p">;</span>
}
#ball-2{animation-delay: 0.1s;}
#ball-3{animation-delay: 0.2s;}
@keyframes example-four{
0% {
width: 0;
height: 0;
}
20% {
width: 1.5em;
height: 1.5em;
}
40%{
width: 1.5em;
height: 1.5em;
}
90%{
width: 0;
height: 0;
}
100%{
width: 0;
height: 0;
}
}
Conclusion
Thanks for reading the blog. I hope you found some of it useful.
At minimum, I hope one of my loaders transported you to that timeless place where — just for a moment — you felt the balance of the universe, tasted a slice of nirvana, inner peace. Best, Jason.
Top comments (1)
The simplest way to get a loader in your website/app is to use
<progress></progress>
. No js, no svg animation and very efficient.