DEV Community

Yossi Abramov
Yossi Abramov

Posted on • Originally published at yossiabramov.com

CSS Skeleton Loading πŸ’€

Skeleton loading is a strategy/technique for improving user experience. In this post I want to share an example of how I would approach it without any UI libraries or fancy components.

Basically, skeleton loading is aimed at components or content areas that are being fetched from a backend or an API. We can use a loader for the entire page or even individual components, but this approach sometimes results in a flaky user experience. However, when applying skeleton loading we ensure that the basic structure of the page and its components are visible. Once our content is ready, we can remove the skeleton loaders and display the content.

Here is my skeleton loading example on codepen.io:

https://codepen.io/yossi_abramov/pen/jOqxOQp

A quick breakdown

For this example, I created a user card component that contains an avatar, name, email and contact button. The user card content is hard-coded for the sake of simplicity. In a real app or website you would probably fetch data and update the DOM with it.

<div class="user-card skeleton">
    <div class="user-cover">
      <img class="user-avatar" src="
        https://yossiabramov.com/images/avatar.jpeg" alt="user profile image" />
    </div>
    <div class="user-details">
      <div class="user-name hide-text">Yossi Abramov</div>
      <div class="user-email hide-text">josephabramov90@gmail.com</div>
    </div>
    <button class="contact-user hide-text">CONTACT</button>
</div>
Enter fullscreen mode Exit fullscreen mode

Notice that the .user-card element has a .skeleton class and every element that contains text has a .hide-text class.

Now, this example is a bit CSS heavy so let’s go over the most important lines:

/* Skeleton */

/* Static Skeleton */

.user-card.skeleton .user-cover {
  background: #e2e2e2;
}

.user-card.skeleton .user-cover .user-avatar {
  display: none;
}

.user-card.skeleton .user-cover::after {
  content: "";
  position: absolute;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  left: 0;
  right: 0;
  margin: auto;
  bottom: -25px;
  border: 1px solid #fff;
  z-index: 10;
  background: #e2e2e2;
}

/* Animated Skeleton */

.user-card.skeleton .hide-text {
  background: #e2e2e2;
  color: transparent;
  position: relative;
  overflow: hidden;
}

.user-card.skeleton .hide-text::before {
  content: "";
  position: absolute;
  left: 0%;
  top: 0;
  height: 100%;
  width: 50px;
  background: linear-gradient(to right, #e2e2e2 25%, #d5d5d5 50%, #e2e2e2 100%);
  animation-name: gradient-animation;
  animation-duration: 2s;
  animation-iteration-count: infinite;
  filter: blur(5px);
}

@keyframes gradient-animation {
  from {
    left: 0%;
  }
  to {
    left: 100%;
  }
}
Enter fullscreen mode Exit fullscreen mode

Basically, I have two states of skeleton loading: static and animated. The .user-cover and .user-avatar elements have a static skeleton – without any CSS transition or keyframe animation applied to them while all elements with the .hide-text class have a keyframe animation. The gradient-animation animation is applied to a ::before element that is positioned absolute to it’s relative .hide-text father. The animation is very simple but effective.

The JavaScript for this example only simulates a somewhat slow fetching of data. Once our data is fetched, we can remove our skeleton loaders.

const $el = document.querySelector(".user-card");

// Loading finished
setTimeout(() => {
  $el.classList.remove("skeleton");
  $el
    .querySelectorAll(".hide-text")
    .forEach((el) => el.classList.remove("hide-text"));
}, 3000);
Enter fullscreen mode Exit fullscreen mode

Hope you find this approach to skeleton loading simple and clear πŸ™ .

✍ For more posts:
https://yossiabramov.com/

Top comments (2)

Collapse
 
jpguisa profile image
Juan Pablo Guisasola

Amazing! Thanks for sharing

Collapse
 
yashraj021 profile image
Yash

I was looking for this from such a long time. Thank you brother!πŸ–€