tutos

Facebook Placeholders

Thursday 2nd August, 2018 —

Content Placeholders ?

A while ago, the great and (not always) glorious Facebook introduced content placeholders that appear to users while the page content is still being loaded, yielding a fantastic user experience. It's not always possible to load content as fast as we would want to, but at least, we can make the wait time more pleasant with this solution. There are already some interesting resources about this question :

  • A Cloud Cannon article that deconstructs the facebook placeholder process and comes up with a CSS only solution.
  • A vanilla js library named Placeloadjs that does the trick for you. You can create shapes (with some limitations though) that will behave like the placeholders seen on Facebook.

Still not convinced ? Let's build our own

Eventhough the two already existing solutions that i've mentionned above are great and work quite well, they have some limitations that didn't fit in, regarding my particular needs.

1

  • I liked the Cloud Cannon article because it offers a pure CSS solution involving 0 javascript. However, i found that the chosen approach is totally different from what i would have done and brings in some limitations.
  • The placeholder starts with an animated gradient box that is the base for the component.
  • Then, some parts of the gradient box are masked by adding a bunch of little divs that give this impression of a fake schematic layout. Look at the image below, the original author included borders so you can see all the divs that are involved in the process :
  • I find this approach really limitating because it forces you to create shapes only by hiding some parts of the original gradient box. Not gonna work for me.
  • The second solution is javascript based. Placeloadjs is quite awesome, but i do not want to add an additional library for this in my projects. Plus the plugin has some serious CSS limitations that won't let you create the exact shapes you want.

So it is obvious that i won't use javascript for this. Sticking to pure CSS seems the only viable solution, but with a different approach.

What we'll be building

In this article we will be building a custom content placeholder that leverages the best practices from the Cloud Cannon article, but with much more flexibility.

Tutorial Code

You can find all the code used for this tutorial in the following Pen.

See the Pen Content Placeholder by Driss Chelouati (@cssninjaStudio) on CodePen.

Html Markup

Let's start with the easy part : the HTML markup. In this example, we are going to create a post placeholder. This placeholder will have three parts :

  • A header with a rounded avatar and some content
  • A main central image that serves as a post image
  • A footer section with tiny bits of content.
<div class="container">
  <div class="placeload">
    <div class="header">
      <div class="img loads"></div>
      <div class="header-content">
        <div class="content-shape loads"></div>
        <div class="content-shape loads"></div>
      </div>
    </div>
    <div class="image-placeholder loads"></div>
    <div class="placeholder-footer">
      <div class="footer-block">
        <div class="content-shape loads"></div>
        <div class="content-shape loads"></div>
      </div>
    </div>
  </div>
</div>

The main difference with the Cloud Cannon approach is the ability to build any kind of layout and element, rather than just filling the gaps with random divs to obtain the desired shape. We first create a .container element that will hold our placeholder element, for demo purpose. Then we create our .placeload element with the 3 blocks we discussed above : header, post image and post footer. Inside those wrapping elements, we have an .img element, wich represents the user avatar, along with 2 .content-shape elements, wich represent the text content. Then we also add the .image-placeholder and the .placeholder-footer, wich holds 2 additional .content-shape elements. Also notice the .loads class, wich adds all the magic to the element. We'll talk about it in the next paragraph, when we will discuss the styles. That's all for the markup, we don't need anything else.

SCSS

Let's now deal with the styles. First of all, we have to declare some color variables and to add some demo styles for the body and the container elements.

$background: #f6f7f8;
$grey-nuance-lighter: #eeeeee;
$grey-nuance-darker: #dddddd;
$fade-grey: #e8e8e8;

body {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  background: $fade-grey;
  position: relative;
  overflow: hidden;
}

.container {
  width: 800px;
  margin: 0 auto;
  min-height: 100vh;
  position: relative;
}

Nothing fancy, here. We just set some defaults for the body and some basic styles for our .container. Then let's setup our main placeholder styles.

.container {
  .placeload {
    background: #fff;
    max-width: 500px;
    margin: 0 auto;
    width: 100%;
    padding: 20px;
    border: 1px solid $fade-grey;
    border-radius: 3px;
    position: relative;
    margin-top: 40%;
    transform: translateY(-50%);
    .header {
      display: flex;
      justify-content: flex-start;
      align-items: center;
      .img {
        width: 50px;
        min-width: 50px;
        height: 50px;
        border-radius: 50%;
      }
      .header-content {
        margin-left: 20px;
        width: 100%;
        .content-shape {
          height: 8px;
          margin-bottom: 8px;
          &:first-child {
            width: 80%;
          }
          &:nth-child(2) {
            width: 60%;
          }
        }
      }
    }
    .image-placeholder {
      width: 100%;
      margin-top: 20px;
      height: 250px;
    }
    .placeholder-footer {
      position: relative;
      margin-top: 20px;
      width: 100%;
      display: flex;
      justify-content: space-between;
      align-items: center;
      .footer-block {
        width: 100%;
        height: 100%;
        min-height: 20px;
        .content-shape {
          height: 8px;
          margin-bottom: 8px;
          &:first-child {
            width: 32%;
          }
          &:nth-child(2) {
            width: 24%;
          }
        }
      }
    }
  }
}

Our .placeload element is styled as we would do for a normal element, using basic CSS properties and a flexbox layout. You can also notice that we don't define any background colors for elements that will be an active part of the placeholder component (e.g animated).

Then, let's deal with the animation. We have to define CSS keyframes for our animation.

@-webkit-keyframes placeload {
  0% {
    background-position: -468px 0;
  }
  100% {
    background-position: 468px 0;
  }
}

@keyframes placeload {
  0% {
    background-position: -468px 0;
  }
  100% {
    background-position: 468px 0;
  }
}

We are simply playing with the CSS background-position property to achieve this progress bar effect that you can see in the demo. We will then have to attach it to every element that has to be animated as a placeholder. In order to do that, we are going to create a separate class that will handle everything related to the animation. We will then be able to add this class to any element of the placeholder that we wish to see animated. This way, we remove all restrictions raised by the Cloud Cannon article. Let's create a loads class that will do this:

.loads {
  -webkit-animation-duration: 1s;
  animation-duration: 1s;
  -webkit-animation-fill-mode: forwards;
  animation-fill-mode: forwards;
  -webkit-animation-iteration-count: infinite;
  animation-iteration-count: infinite;
  -webkit-animation-name: placeload;
  animation-name: placeload;
  -webkit-animation-timing-function: linear;
  animation-timing-function: linear;
  background: $background;
  background: $grey-nuance-lighter;
  background: -webkit-gradient(
    linear,
    left top,
    right top,
    color-stop(8%, $grey-nuance-lighter),
    color-stop(18%, $grey-nuance-darker),
    color-stop(33%, $grey-nuance-lighter)
  );
  background: -webkit-linear-gradient(
    left,
    #eeeeee 8%,
    #dddddd 18%,
    #eeeeee 33%
  );
  background: linear-gradient(
    to right,
    $grey-nuance-lighter 8%,
    $grey-nuance-darker 18%,
    $grey-nuance-lighter 33%
  );
  -webkit-background-size: 800px 104px;
  background-size: 1200px 104px;
  position: relative;
}

In this class, we are setting up 2 things :

  • First, the animation defaults, including animation duration, iteration count, fill mode and timing function.
  • Then, we setup a background gradient that will be applied to any element that has the .loads class. The gradient is quite elaborated, leveraging some properties like color-stop and complex background definitions. Animating this gradient with the previously defined keyframes creates our progress bar like animation.

Once the code is in place, everything should work as expected and you should see a magnificent animated facebook flavored content placeholder, without a single line of javascript. You can then use javascript to show or hide the placeholder while the page is still loading the content from the server!

Conclusion

in this article we saw how to make a totally custom content placeholder but most important, we created some basic features that you can reuse to create an infinity of layouts and shapes, while keeping the same behaviour, something that we could not achieve with the 2 solutions we first explored.

If you want to share your thoughts or ask a question, give it a shot in the comments section. I hope you enjoyed this little session as much as i did writing it and to see you again soon on Css Ninja.

Back to Blog

Continue reading

Server Cache Invalidation in Nuxt and Nitro

Top 10 Vue Components Libraries

How to build a filterable list with Nuxt and Tailwind