paint-brush
How To Create And Use Skeleton Loader: Simple Example [Part 2]by@kevin-mehta
1,117 reads
1,117 reads

How To Create And Use Skeleton Loader: Simple Example [Part 2]

by Kevin MehtaDecember 11th, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

How To Create And Use Skeleton Loader: Simple Example [Part 2] The first post of the series is focused on providing an overview of the concept, purpose, usage and points to keep in mind. Skeleton loader is designed to make interfaces more expressive and intuitive. Part 2 of this series will dive into actual implementation of Skeleton loader step by step. We have to keep background for the placeholders where content has not been loaded yet. We also have to animate gradient from left to right, repeatedly with an ease.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - How To Create And Use Skeleton Loader: Simple Example [Part 2]
Kevin Mehta HackerNoon profile picture

In this post, we will dive into actual implementation of Skeleton Loader step by step.

The first post of the series is focused on providing an overview of
Skeleton Loader, its purpose, usage and points to keep in mind while
designing.
If you are still uncertain regarding concept, refer Skeleton Loader: An overview, purpose, usage and design

Steps Involved:

  • Design a Background
  • Design a Gradient
  • Animate Gradient
  • Display loader only for empty placeholders
  • Complete Implementation

Design a Background

We have to keep background for the placeholders where content has not
been loaded yet. These visual placeholders should be in light gray or
neutral colors.

<div>	🅐
    <span class="skeleton-loader-background"></span>	🅑
</div>
.skeleton-loader-background {
  width: 100%;
  height: 15px;
  display: block;
  background: lightgray;       🅒
}

A. Create a section for loading content.

B. Create

<span>
inside that section for adding hooks to a text. Assigned a class “skeleton-loader-background” for styling background.

C. Specify

background
with light-gray color.

Image: Skeleton Loader Background

Design a Gradient

<div>
	<span class="skeleton-loader-gradient"></span>		🅐
</div>
.skeleton-loader-gradient {
  width: 100%;
  height: 15px;
  display: block;
  background: linear-gradient(        🅑
      to right,
      rgba(255, 255, 255, 0),
      rgba(255, 255, 255, 0.5) 50%,
      rgba(255, 255, 255, 0) 80%
    ),
    lightgray;
  background-repeat: repeat-y;        🅒
  background-size: 50px 200px;        🅓
  background-position: 0 0;           🅔
}

A. Create a section, span for adding hooks and assign a class “skeleton-loader-gradient” for styling gradient.

B. Specify

linear-gradient
direction and pattern. Here, gradient will get applied from left to right. It consists of white color with 0% opacity (transparent) in the starting, 50% opacity in the middle and 80% opacity at the end. Also, background contains two values separated by commas. According to specifications, values gets stacked vertically. The first value remains on top and other goes down from there.

C. Repeat gradient vertically i.e. on Y-axis.

D. Specify size of the gradient i.e. width and height.

E. Set the position of the gradient to start i.e. 0 0.

Image: Skeleton Loader Gradient

Animate Gradient

Motion plays an important role in applications. It can help to make interfaces more expressive and intuitive. For Skeleton loader, we have to animate a gradient from left most end towards right.

When thinking of motion, we should aim for an appropriate duration. If it is slow it makes users feel they are waiting more than they actually are. If it is quite fast it gives bad perception.

We have to keep right balance of speed, direction and easing to give performant experience.

We have a light-gray background and gradient designed. Next, we have to animate gradient from left to right, repeatedly with an ease.

<div>
  <span class="skeleton-loader"></span>   🅐
</div>
.skeleton-loader {
  width: 100%;
  height: 15px;
  display: block;
  background: linear-gradient(	  🅑
      to right,
      rgba(255, 255, 255, 0),
      rgba(255, 255, 255, 0.5) 50%,
      rgba(255, 255, 255, 0) 80%
    ),
    lightgray;
  background-repeat: repeat-y;
  background-size: 50px 500px;
  background-position: 0 0;
  animation: shine 1s infinite;	  🅒
}
@keyframes shine {	🅓
  to {
    background-position: 100% 0, /* move highlight to right */ 0 0;
  }
}

A. Create a section, span for adding hooks and assign a class “skeleton-loader” for styling gradient.

B. Specify gradient and light-gray background for loader.

C. Specify

animation
name, duration and iteration count.

D. Using

keyframes
rule, specify how animation will gradually change from the current style to the new style at certain times. Here, it will change background position from left to right (0 to 100%).

Image: Skeleton Loader Animation

Show loader only for empty placeholders

.skeleton-loader:empty {      🅐
  width: 100%;
  height: 15px;
  display: block;
  background: linear-gradient(
      to right,
      rgba(255, 255, 255, 0),
      rgba(255, 255, 255, 0.5) 50%,
      rgba(255, 255, 255, 0) 80%
    ),
    lightgray;
  background-repeat: repeat-y;
  background-size: 50px 500px;
  background-position: 0 0;
  animation: shine 1s infinite;
}

A. The

:empty
selector matches every element that has no child elements or text nodes. Here, it will display skeleton loader only when content has not been loaded.

Complete Implementation

<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" type="text/css" href="loader.css" />
    <link rel="stylesheet" type="text/css" href="tile.css" />
    <script src="script.js"></script>
  </head>
  <body>
    <div class="prod--wrapper">
      <div class="prod--col prod--img">
        <img id="productImage" class="prod--img-graphic skeleton-loader" />
      </div>
      <div class="prod--col prod--details">
        <div class="prod--row prod--name">
          <span id="productName" class="prod--name-text skeleton-loader"></span>
        </div>
        <div class="prod--row prod--description">
          <span
            id="productId"
            class="prod--description-text skeleton-loader"
            ></span>
        </div>
      </div>
    </div>
  </body>
</html>
.skeleton-loader:empty {
  width: 100%;
  height: 15px;
  display: block;
  background: linear-gradient(
      to right,
      rgba(255, 255, 255, 0),
      rgba(255, 255, 255, 0.5) 50%,
      rgba(255, 255, 255, 0) 80%
    ),
    lightgray;
  background-repeat: repeat-y;
  background-size: 50px 500px;
  background-position: 0 0;
  animation: shine 1s infinite;
}

@keyframes shine {
  to {
    background-position: 100% 0;
  }
}
.prod--wrapper {
  display: flex;
  width: 95%;
  margin: 32px 0;
  border: 1px solid #b6b6b6;
  border-radius: 6px;
  padding: 22px 10px;
  font-family: "Calibri", "Arial";
}

.prod--wrapper .prod--row {
  display: flex;
  flex-direction: row;
}

.prod--wrapper .prod--col {
  display: flex;
  flex-direction: column;
}

.prod--wrapper .prod--img {
  width: 20%;
  margin: 0 15px;
}

.prod--wrapper .prod--img .prod--img-graphic {
  max-height: 100%;
  height: 100%;
  vertical-align: top;
  max-width: 100%;
}

.prod--wrapper .prod--details {
  width: 90%;
  margin-left: 17px;
}

.prod--wrapper .prod--details .prod--name {
  margin-bottom: 3px;
  width: 85%;
  display: block;
  max-width: 100%;
}

.prod--wrapper .prod--details .prod--name .prod--name-para {
  margin: 0 auto;
}

.prod--wrapper .prod--details .prod--name .prod--name-text {
  font-weight: bold;
  font-size: 16px;
  line-height: 23px;
  color: #002877;
  height: 40px;
}

.prod--wrapper .prod--details .prod--description {
  margin-bottom: 13px;
}

.prod--wrapper .prod--details .prod--description .prod--description-text {
  font-size: 13px;
  line-height: 18px;
  color: #666666;
}
document.addEventListener("DOMContentLoaded", function() {
  var eleProductImage = document.getElementById("productImage");
  var eleProductName = document.getElementById("productName");
  var eleProductId = document.getElementById("productId");

  function getProductDetails() {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "https://fakestoreapi.com/products/1", true);
    xhr.onload = function() {
      var res = JSON.parse(xhr.responseText);
      eleProductImage.src = res.image;
      eleProductName.innerHTML = res.title;
      eleProductId.innerHTML = res.description;
    };
    xhr.send();
  }

  getProductDetails();
});

Here, we have designed product tile containing image, product name and product description. We are fetching content through live API using JavaScript.

A.

DOMContentLoaded
will get fired on load of HTML DOM. We will perform JavaScript operations on receipt of this event.

B. Access elements through

HTML DOM API
.

C. Fetch data from live API. Here, we are using dummy API for fetching product details (https://fakestoreapi.com/products/1).

D. Populate HTML elements with appropriate content.

Image: Skeleton Loader Visual

You can also check my video depicting the designing of the Skeleton Loader as per the steps mentioned above.

Bottom Line

While working with animations, it looks demanding at first sight. However, implementing them shouldn’t be that tough compared to the experience it provides.

Also, you can reuse the CSS anywhere in your code.

For working example, head over to repository.

If you enjoyed reading this post, do appreciate and give a follow.

Thank you for your time.