Build a Simple Loader With CSS and HTML

Jason Melton
The Startup
Published in
5 min readSep 5, 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…


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:

I’ll explain each example in more detail when it’s relevant.

I set up some CSS variables for colors (from, flexbox, and margins for the general layout of the demo.

Example 1

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

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 2

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

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 3

Example 3 is one 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 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.

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.


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.