Jason Melton
Posted on September 7, 2020
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.
Posted on September 7, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.