Grow your CSS skills. Land your dream job.

The Facebook Loading Animation in CSS

Published by 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!

Comments

  1. Warner
    Permalink to comment#

    Exactly one hour a go i Tweeted your ios6gallery example http://labs.weinberg.me/ios6gallery/ and now i am reading this.

    Love the article, keep em comming…

  2. Permalink to comment#

    Wow, I think the CSS version is smoother than the gif.

  3. Great article! I loved the step by step process you took explaining it to all of us. However I have to ask with all the CSS that is being used on a typical site. Do you feel like using a js script would be better than doing it in CSS?

    • Matt Forester
      Permalink to comment#

      No, some might argue the gif would be the better way. No coding and no math calcs by the browser. Seems like it’s all a matter of perspective.

  4. Neat and fun because you can but not something I’d do in a project.

    Any advantages over using an animated GIF that I’m missing?

    • Permalink to comment#

      One reason for doing it with CSS rather than an animated GIF might be to allow the colour to be set dynamically, e.g. based on the user’s selected theme. With GIFs you would need to create the GIF for each possible colour.

    • GIF was a bad example. How about an animated SVG? It can be styled as well.

  5. Permalink to comment#

    Wow. The facebook animation is great, but how you explain the use of gradients has made my day!

    I guess using the animated gif is always going to be the best option for backwards compatibility, but the CSS is great for customisability. I wonder which compiles to a smaller size? The css or the animated gif?

  6. Wowie! Awesome!!!

  7. Permalink to comment#

    Nice write up, it looks great!

  8. EstherH
    Permalink to comment#

    first of all, this is amazing!
    second, like previous commenters, i have a request – maybe you can do a post about what is more efficient (size-wise, processing speed, browser resources, whatnot) – animated gif’s, javascript, or pure css. I love these css brain exercises, but you have to make the right decision sometimes when creating a real site! (not talking about browser compatibility, let’s leave that out for now…)
    Thanks!

  9. kmh0909
    Permalink to comment#

    It’s an animation for kids

  10. Dan
    Permalink to comment#

    This is nice, but useless work. Stick to images in such a case.

    • Do you have some real data to back up a claim like that?

      Remember that:

      1. A GIF is another HTTP Request
      2. The CSS you would include in a larger stylesheet so no extra request
      3. The GIF is 4k (gzips to 1.5k)
      4. The CSS is 4k (gzips to 750 bytes)
    • How is this at all useless? This is bleeding edge stuff and this sort of progressive thinking is paving the way and inspiring developers around the globe to be more creative, think outside the box and improve the flexibility, scalability and ways that we achieve simple tasks such as this. Just because a tool like animated GIFs have been around for decades doesn’t mean you should just “stick to it” when there is an alternate method available. Both methods have their own set of pros/cons and both methods should definitely be considered when making these development decisions when creating our apps. The important take away from this is to fully understand the limitations and device/browser support of this newly introduced method.

    • cnwtx
      Permalink to comment#

      @Chris Coyier, I don’t want to sound like I’m taking sides, but a gif can be encoded as a data uri, putting it into the css. It would end up about the same size as a normal gif, and get rid of an extra http request. In my opinion both ways of doing it have merrit, one for customisibillity, and the other for backwards compatibillity.

      Very good and informative article!

  11. Awesome article Fabrice!

  12. This is one hell of a tutorial Fabrice. Awesome work as always. :)

  13. Louis
    Permalink to comment#

    Fab tutorial. The Codepen CSS only example doesn’t not work in the latest Firefox (16.0.2) on Windows 7 OS. Any other users with the same issue?

  14. Permalink to comment#

    Any of the above outside of the CSS3 spec? Or is it just FF?

  15. Permalink to comment#

    This is not working…
    Sorry

  16. Is there a reason why you didn’t just set a border on the boxes instead of linear gradients? Wouldn’t it be faster when running or does it have a problem with animation?

    • I used linear-gradients to use only a single HTML element.

    • @Fabrice: ah yes, I hadn’t noticed that bit. But is it still faster in the browser using gradients than multiple elements with a border? (I think that only an expert could answer that though)

    • Aaron
      Permalink to comment#

      Working in pure CSS rather than adding elements to the original markup is the standard that I think we’re trying to aspire to: a more semantic web.

      Of course, adding a “page loading” element can probably be considered superfluous from this perspective.

      Perhaps, you are correct that it would be easier to add several elements if we’re already going to add a non-semantic (irrelevant to content) element in the first place.

  17. Permalink to comment#

    On the css version, the rectangles fadeout completely while in the gif image they fadeout but doesnt desappear. have a closer look to the rectangles when fading out and compare them to the gif and you’ll understand what I’m saying.

    cool effect dude!!

  18. Iain Robinson
    Permalink to comment#

    I can’t see the Facebook animation in any of my browsers on the mac. That’s Safari, Firefox, Chrome and Opera, all up-to-date versions

  19. maxw3st
    Permalink to comment#

    The only difference between what you have here and what Mozilla instructs for

    CSS animation is that they define the animation timing in the relevant HTML element first. Then, define the animation steps in the @keyframes code block. Here that order is reversed.

    The demo here doesn’t work in Firefox (latest nightly), and it stops after a couple seconds in Webkit (Chrome-latest). Apparently the “infinite” looping instruction is being ignored as well. But, that may have to do with order as well.

    The Codepen also doesn’t work on Firefox, but does work in Chrome without stopping. That may be due to background code other than HTML or CSS that’s part of the Codepen app however.

  20. _John_
    Permalink to comment#

    Always amazing to see what you produce @Chris Coyier… How on earth do you have time to write this stuff out lol… You’re just that good!

  21. In the past there’s a couple of ways I would do this. One would be to use JQuery simply because that’s something quick to install but I appreciate that it’s actually quite heavy. Two would be to just use an animated GIF and actually the biggest problem I find with using a GIF is how inflexible it is. So what I use now is an animated SVG, the only problem with that is support for older browsers and some mobile devices. I think this CSS method does the trick so I’ll try it out on a couple of sites like http://www.reynoldsdigital.com and see how it goes. Love how light this method is. Great Tut.

Leave a Comment

Current day month ye@r *

*May or may not contain any actual "CSS" or "Tricks".