Build a Simple Loader with CSS and HTML
Tags: css, html, webdev, tutorial
Published: 2020-09-07 02:21:17

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](https://dev-to-uploads.s3.amazonaws.com/i/08vgyqmc4a2eowget9hg.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](https://github.com/cooljasonmelton/loaders)

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

```html
<!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](https://coolors.co/1a535c-4ecdc4-f7fff7-ff6b6b-ffe66d)), `flexbox`, and `margin`s for the general layout of the demo.

```css
: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](https://dev-to-uploads.s3.amazonaws.com/i/lj9kerefndzv1ybm0v66.gif)

Example 1 has the HTML of one div, set up like this:

```html
    <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`.

```css
/* 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](https://dev-to-uploads.s3.amazonaws.com/i/goe8sz0xmc2fuoqdt6ec.gif)

For Example 2, the HTML is a div container for three more divs. Each will be a “bar”.

```html
    <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`.

```css
/* 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](https://dev-to-uploads.s3.amazonaws.com/i/t3ambf4oc0cfxjfihyym.gif)

Example 3 is one div.

```html
    <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.

```css
/* 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](https://dev-to-uploads.s3.amazonaws.com/i/29bboopn1aeyf4lihgrb.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.

```html
    <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.

```css
/* 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.