DEV Community

Cover image for Build a Simple Loader with CSS and HTML
Jason Melton
Jason Melton

Posted on

Build a Simple Loader with CSS and HTML

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…

loader demo gif

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.

The code on Github

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 margins 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 gif

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

example 2 gif

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 gif

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 gif

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;

    display: flex;
    align-items: center;
    justify-content:center;
}

.ball {
    width: 0;
    height: 0;
    border-radius: 50%;
    background-color: var(--loader-bg-color);

    animation: 1.2s infinite example-four;
}

#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.

Oldest comments (1)

Collapse
 
violet profile image
Elena

The simplest way to get a loader in your website/app is to use <progress></progress>. No js, no svg animation and very efficient.