The Facebook Loading Animation in CSS

Guest Author //

The following is a guest post by Fabrice Weinberg. I know of Fabrice through his excellent work on CodePen. He draws inspiration from seemingly anywhere and recreates things meticulously through CSS. He sent me this tutorial which walks through his techniques for recreating one of his most popular Pens, which I'm more than happy to publish! I learned a few tricks in here.

Hey everyone! CSS3 is just plain awesome. The development is fast and the possibilities are endless. Today I want to show you how to make an awesome animation using multiple background images and linear gradients in CSS. We will build the Facebook loading animation using just a single HTML element.

For simplicity, you will not see any vendor prefixes in the CSS. Before we dive into creating the Facebook loading animation I will explain the basic techniques.

background-image

We all know the background-image property. You give it a url() pointing to an external image file (or a data URI) to make that image appear as the background of any HTML element.

Now it's possible to use a linear-gradient or radial-gradient as a background image. This opens up a whole new world of backgrounds that can be generated using just code. I am not going to explain the syntax (you can get that from those past two links), but I will give you some tips that will help you create awesome patterns and nearly any shape.

The trick that comes in handy is the ability to add more then one background image at a time.

div {
  background-image: 
    background-image
    [, background-image]* /* optional additional backgrounds */
}

See MDN: background-image for more information. To see what others have created using these properties have a look at this Gallery.

How to draw with linear-gradient

Lets get you started with some basic linear-gradient to get to know its syntax.

background-image: linear-gradient(90deg, blue, red);

To see the syntax and all its possibilities so you should look at the MDN: linear-gradient.

This will result in this:

You see a nice gradient between blue and red. But to always draw a gradient would be quite boring. Woudn't it be awesome to generate patterns like this:

sharpcolor

So lets make it! Look at the pattern above. You see four different colors, each color needs 1/4th of the width. Expressed as percentage, we get 25% (100%/4). For such simple patterns I recommend you to use percentages so the pattern will dynamically adapt to the size of the element.

To translate the image pattern to linear-gradient we will start with the left-most color. Blue, in this case.

background-image: linear-gradient(90deg, blue 25%);

This will result in a blank background image. To get linear-gradient to work we have to add at least two colors. Let's add the next color, green:

background-image: linear-gradient(90deg, blue 25%, green 25%);

This first color value is special because "blue 25%" doesn't mean that the blue color starts at 25%. In this case "blue 25%" means that the color blue is spread to 25%.

What we get is this:

Exactly what we want, a sharp separation of blue and green. But as you may notice we have to write 25% for both the blue and the green color. During development this is quite annoying because you have to change both values to maintain the sharp separation.

While playing with linear-gradients I found a nice little way to make this a bit easier:

background-image: linear-gradient(90deg, blue 25%, green 0);

Now we just set the position of the color green to "0". What we achieve with this is that green will always start at the exact same position as the previous color, in this case blue. The result is the same as before but now you only have to change one value to change the size of the blue rectangle. Adding the remaining colors becomes quite easy because we know how to create a sharp color cut now.

background-image: linear-gradient(90deg, blue 25%, green 0, green 50%, red 0, red 75%, yellow 0);

You can see now why the first color value is special. For every other color we have to define it twice. Once for the start position and the other time to specify how far it will spread.

Result:

How can we make shapes?

To create some basic shapes with the background-image property we will start with this:

.squareandcircle{
  width: 40px;
  height: 30px; 
  background-image: linear-gradient(90deg, blue, blue),
                    radial-gradient(center, circle, green 10px, transparent 0);

  background-size: 15px 15px,
                   20px 20px;

  background-position: 0    0,
                       20px 10px;

  background-repeat:no-repeat;
}

squareandrect

We used multiple background images in combination with background-size and background-position to create a square and a circle.

So how does it work?

Starting with background-image

First we have to create the background images. For simplicity we use the most basic linear-gradient and the most basic radial-gradient.

Now further to the next property: background-size

The background-size property will set the size for each previously created background image in the same order as they were created.

So in our case the size of the linear-gradient is 15x15px. And for the radial-gradient it's 20x20px.

Position of each background: background-position

Know we have two images on of a square with a size of 15x15px and one of a circle which fits in a 20x20px square. But these images are on top of each other. To move them around we need background-position.

For background-position we have to define an offset for each background image relative to the upper left corner of the element.

Here the first image (the square) will be in the upper left corner and the circle will be 20px to the left and 20px down from that. Keep in mind that we move each image from the top left corner of the element.

background-repeat

The last thing unexplained bit is background-repeat which is pretty simple. We don't want our generated images to repeat in any direction so we set it once to no-repeat. If we only add one value to it this value will be used by all background images we create.

So how do we make animations with this?

It's quite easy. We create our shape with background images and then use the animation property to animate the whole thing by changing the background size and/or position.

Just a simple example before we do the Facebook loading animation. We start with some basic markup like this:

.square {
  width: 40px;
  height: 30px; 
  background-color: gray;
  background-image: linear-gradient(90deg, blue, blue);
  background-size: 15px 15px;
  background-position: 0 0;
  background-repeat: no-repeat;
}

Which looks like:

rect

Let's animate this in six steps, moving the square from the upper right corner than down to the bottom right corner and back to the bottom left corner.

Basic math tells us that we need 16.6% of time for each frame (100%/6 ~ 16.6%) in our keyframe animation. On every frame we modify the background position to create the illusion our square would move.

We will end up with quite a bit of CSS:

@keyframes move {
    16.6% {
      background-position: 0 0;
    }
    33.2% {  
      background-position: 12.5px 0;
    }
    49.8% {
      background-position: 25px 0;
    }
    66.4% {
      background-position: 25px 15px;        
    }
    83% {
      background-position: 12.5px 15px;      
    }
    99% { 
      background-position: 0 15px;               
    }
}

To see the animation we have to add the animation to our square element like this:

.square{
  width: 40px;
  height: 30px; 
  background-color: gray;
  background-image: linear-gradient(90deg, blue, blue);
  background-size: 15px 15px;
  background-position: 0 0;
  background-repeat: no-repeat;

  animation: move 1s steps(1, start) infinite;
}

The steps function is awesome. You can read more about it here.

See it in action:


The animation you see embedded here may be stopped, to see it in action visit the Pen.

Try to play with it to understand all the possibilities you have. In fact what we get here is an open canvas on which we can draw nearly every shape and animate it in any way.

The Facebook loading animation.

Now its time to make some more complicated animation using these techniques.

Take a look at the original gif of the Facebook loading animation.

orig-loading

In the GIF, we see vertical stripes going from left to right while shrinking and losing color.

How can we archive this in CSS? Let me first say this: I will not explain how to create every single on of these stripes. I will only explain to create the first one, from there on it shouldn't be to hard to understand the rest of it.

Let's zoom in a bit on the first stripe:

zoomedstripe

The first shape is 4px wide and 16px long. There is a 1px border around a 3x16px solid colored rectangle. For simplicity the border we will create will also have a solid color. To create such a shape we have to generate two background images using linear-gradient. One for the left border the solid inner rectangle and the right border and one for the upper and lower border. We start with the linear-gradient for the left border the solid inner rectangle and the right border.

#facebook {
  width: 16px;
  height: 16px;    

  background-image: linear-gradient(90deg, #8490c6 1px, #aeb5da 0, #aeb5da 3px, #8490c6 3px);  
  background-size: 4px 16px;
  background-position: 0 0;

  /* Just to make it a little bigger*/
  zoom: 5; 
}

The result:

cssstripe1

To finish this shape we than have to add another background image to create the upper and lower border:

#facebook {
  width: 16px;
  height: 16px;    
  background-image:
            linear-gradient(0deg, #8490c6 1px, transparent 0, transparent 15px, #8490c6 15px),
            linear-gradient(90deg, #8490c6 1px, #aeb5da 0, #aeb5da 3px, #8490c6 3px);      

  background-size: 4px 16px,
                   4px 16px;

  background-position: 0 0,
                       0 0;

  background-repeat: no-repeat;

  /* Just to make it a little bigger*/
  zoom:5;
}

It's not necessary to repeat the values of background-size and background-position because they are the same, but for further development its better to write them.

And now we have this:

cssstripe2

We need to make six of these stripes in slightly less color and size. How to make these should be clear by now.

#loader {
    zoom: 1; /* Increase this for a bigger symbol*/
    width: 16px;
    height: 16px;

    background: linear-gradient(0deg, #f4f5fa 1px, transparent 0, transparent 8px, #f4f5fa 8px),   /* 6  */
                linear-gradient(90deg, #f4f5fa 1px, #f6f9fb 0, #f6f9fb 3px, #f4f5fa 3px),

                linear-gradient(0deg, #ececf5 1px, transparent 0, transparent 8px, #ececf5 8px),   /* 5  */
                linear-gradient(90deg, #ececf5 1px, #f2f3f9 0, #f2f3f9 3px, #ececf5 3px),

                linear-gradient(0deg, #e7eaf4 1px, transparent 0, transparent 8px, #e7eaf4 8px),   /* 4  */
                linear-gradient(90deg, #e7eaf4 1px, #eef1f8 0, #eef1f8 3px, #e7eaf4 3px),

                linear-gradient(0deg, #b9bedd 1px, transparent 0, transparent 10px, #b9bedd 10px), /* 3  */
                linear-gradient(90deg, #b9bedd 1px, #d0d5e8 0, #d0d5e8 3px, #b9bedd 3px),

                linear-gradient(0deg, #9fa6d2 1px, transparent 0, transparent 15px, #9fa6d2 15px), /* 2  */
                linear-gradient(90deg, #9fa6d2 1px, #c0c5e1 0, #c0c5e1 3px, #9fa6d2 3px),

                linear-gradient(0deg, #8490c6 1px, transparent 0, transparent 15px, #8490c6 15px), /* 1  */
                 linear-gradient(90deg, #8490c6 1px, #aeb5da 0, #aeb5da 3px, #8490c6 3px); 
  
    background-repeat: no-repeat;

    background-size: 
               4px 9px,   /* 6 */
               4px 9px,

               4px 9px,   /* 5 */
               4px 9px,

               4px 9px,   /* 4 */
               4px 9px,

               4px 11px,  /* 3 */
               4px 11px,

               4px 16px,  /* 2 */
               4px 16px,

               4px 16px,  /* 1 */
               4px 16px;

  background-position-x: -4px; /* Hide All */
  background-position-y: 3px, 3px, 3px, 3px, 3px, 3px, 2px, 2px, 0, 0, 0, 0;
}

All six stripes are hidden because they are moved -4px on the x-axis.

Let's think of the original gif again. It contains eight steps moving each stripe further and further to the left. So what we need to make is an animation containing eight steps while each step needs 12.5% of the time (100%/8). On every step, each stripe will move 6px to the right. If the position of the stripe on the x-axis is greater than 16px, it is off the canvas and we can place it at -4px to hide it.

You may have noticed the use of background-position-y. This saves us quite a lot of code because we only have to move the stripes on the x-axis we never have to change the y-coordinates, therefore we only have to write the coordinates for the position on the x-axis in our keyframe animation.

@keyframes wait {
  12.5% {
    background-position-x: -4px,-4px,   -4px, -4px,  -4px,-4px,
                           -4px,-4px,   -4px, -4px,   0,   0;
  }
  25% {
    background-position-x: -4px, -4px,  -4px, -4px,  -4px,-4px,
                           -4px, -4px,   0,    0,     6px, 6px;
  }
  37.5% {
    background-position-x: -4px, -4px,  -4px, -4px,  -4px, -4px,
                            0,    0,     6px,  6px,  12px, 12px;
  }
  50%{
    background-position-x: -4px, -4px,  -4px, -4px,   0,    0,
                            6px,  6px,  12px, 12px,  -4px, -4px;
  }
  62.5% {
    background-position-x: -4px, -4px,   0,    0,     6px,  6px,
                           12px, 12px,  -4px, -4px,  -4px, -4px;
  }
  75% {
    background-position-x:  0,    0,     6px,  6px,  12px, 12px,
                           -4px, -4px,  -4px, -4px,  -4px, -4px;
  }
  87.5%{
    background-position-x:  6px,  6px,  12px, 12px,  -4px, -4px,
                           -4px, -4px,  -4px, -4px,  -4px, -4px;
  }
  100% {
    background-position-x: 12px, 12px,  -4px, -4px,  -4px, -4px,
                           -4px, -4px,  -4px, -4px,  -4px, -4px;
  }
}

Because every stripe is made of two background images we have to change twelve background positions (two for every stripe) on every step.

And finally we can add the animation property to our element:

animation: wait .80s steps(1, start) infinite;

Here is a live demo on Codepen:

Again, the animation you see embedded here may be stopped, to see it in action visit the Pen.

Thanks for reading. I hope you enjoyed it. Thank you!