create a skeleton loading animation with CSS

How to create a skeleton loading animation with CSS

You can create a skeleton loading animation with CSS. Everyone knows that having a slow website is one of the worst things if you are a developer or browsing a lot. sometimes you just have a lot of data to load and it’s impossible to make your website any faster, which is why many large companies use loading animations.

It helps to make their site appear faster, and in this tutorial, I’ll show you exactly how to create a skeleton loading animation with CSS.

This will just give your site a lot more responsive experience to the users.
It will also appear to be quite professional, and best of all, it will be very simple to apply.

Create an HTML page, for example, the following code is a simple sample for you.

Skeleton loading animation demo 1

<div class="container">
  <div class="card">
    <div class="card-img skeleton"></div>
    <div class="card-body">
      <h2 class="card-title skeleton"> </h2>
        <p class="card-intro skeleton"></p>
    </div>
  </div>
  <div class="card">
    <div class="card-img">
      <img src="https://cdn.pixabay.com/photo/2017/10/09/19/29/eat-2834549__480.jpg" />
    </div>
    <div class="card-body">
      <h2 class="card-title">
        Card
      </h2>
      <p class="card-intro">
        Lorem ipsum, dolor sit amet consectetur adipisicing elit. Facilis quam odio quidem veritatis ...</p>
    </div>
  </div>
</div>
*, *:after, *:before {  
  box-sizing: border-box;  
 }  
 body {  
  font-family: "Inter", sans-serif;  
  background-color: #f2f5f7;  
 }  
 .card {  
  display: flex;  
  flex-direction: column;  
  flex-basis: 300px;  
  flex-shrink: 0;  
  flex-grow: 0;  
  max-width: 100%;  
  background-color: #FFF;  
  box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.15);  
  border-radius: 5px;  
  overflow: hidden;  
  margin: 1rem;  
 }  
 .card-img {  
  padding-bottom: 56.25%;  
  position: relative;  
 }  
 .card-img img {  
  position: absolute;  
  width: 100%;  
 }  
 .card-body {  
  padding: 1.5rem;  
 }  
 .card-title {  
  font-size: 1.25rem;  
  line-height: 1.33;  
  font-weight: 700;  
 }  
 .card-title.skeleton {  
  min-height: 28px;  
  border-radius: 4px;  
 }  
 .card-intro {  
  margin-top: 0.75rem;  
  line-height: 1.5;  
 }  
 .card-intro.skeleton {  
  min-height: 72px;  
  border-radius: 4px;  
 }  
 .skeleton {  
  background-color: #eee;  
  background-image: linear-gradient(90deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0));  
  background-size: 40px 100%;  
  background-repeat: no-repeat;  
  background-position: left -40px top 0;  
  -webkit-animation: shine 2s ease-in-out infinite;  
      animation: shine 1s ease infinite;  
 }  
 @-webkit-keyframes shine {  
  to {  
   background-position: right -40px top 0;  
  }  
 }  
 @keyframes shine {  
  to {  
   background-position: right  -40px top 0;  
  }  
 }  
 .container {  
  position: absolute;  
  top: 0;  
  left: 0;  
  right: 0;  
  bottom: 0;  
  width: 100%;  
  display: flex;  
  align-items: center;  
  justify-content: center;  
  flex-wrap: wrap;  
 }  

See the Pen skeleton loading in HTML and CSS by niat (@niat786) on CodePen.

Skeleton loading animation demo 2

<!-- create a skeleton loading animation with CSS -->
<body>
  <div class="grid"></div>
  
  <template id="card-template">
    <div class="card">
      <div class="header">
        <img class="header-img skeleton" src="https://source.unsplash.com/100x100/?nature" />
        <div class="title" data-title>
          <div class="skeleton skeleton-text"></div>
          <div class="skeleton skeleton-text"></div>
        </div>
      </div>
      <div data-body>
        <div class="skeleton skeleton-text"></div>
        <div class="skeleton skeleton-text"></div>
        <div class="skeleton skeleton-text"></div>
        <div class="skeleton skeleton-text"></div>
      </div>
    </div>
  </template>
</body>

We only have one card with a title, a description, and an actual image. This is something we can start with for our skeleton loading, and the first thing I want to do with skeleton loading is get our image skeleton loaded, which is really simple.
We’ll add a skeleton class to our image, and the nice thing is that almost always when working with images, you’ll probably set a specific width and height to that image somewhere in your CSS, which is perfect because it’s already defining the boundaries for where our skeleton loading animation will go.

Now we will add some JavaScript code here.

// create a skeleton loading animation with CSS
  const grid = document.querySelector('.grid')
  const cardTemplate = document.getElementById('card-template')
  for (let i = 0; i < 10; i++) {
    grid.append(cardTemplate.content.cloneNode(true))
  }
  fetch("https://jsonplaceholder.typicode.com/posts")
    .then(res => res.json())
    .then(posts => {
      grid.innerHTML = ''
      posts.forEach(post => {
        const div = cardTemplate.content.cloneNode(true)
        div.querySelector('[data-title]').textContent = post.title
        div.querySelector('[data-body]').textContent = post.body
        grid.append(div)
      })
  })

Finally, we will add some CSS code to it which will handle the styling of the page.

/*. create a skeleton loading animation with CSS */
.skeleton {
  opacity: .7;
  animation: skeleton-loading 1s linear infinite alternate;
}
.skeleton-text {
  width: 100%;
  height: .5rem;
  margin-bottom: .25rem;
  border-radius: .125rem;
}
.skeleton-text:last-child {
  margin-bottom: 0;
  width: 80%;
}
@keyframes skeleton-loading {
  0% {
    background-color: hsl(200, 20%, 70%);
  }
  100% {
    background-color: hsl(200, 20%, 95%);
  }
}
.grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  padding: 1rem;
}
.card {
  background-color: white;
  box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;
  padding: 16px;
  border-radius: 4px;
}
.header {
  margin-bottom: 1rem;
  display: flex;
  align-items: center;
}
.header-img {
  width: 50px;
  height: 50px;
  object-fit: cover;
  border-radius: 100%;
  margin-right: 1rem;
  flex-shrink: 0;
}
.title {
  font-weight: bold;
  font-size: 1.25rem;
  text-transform: capitalize;
  word-wrap: none;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  flex-grow: 1;
}
/*. */

How does this skeleton loading in CSS work?

create a skeleton loading animation with CSS

If you look at the example, you’ll notice that this skeleton loading animation has the circle where our images are, and all we’re doing is taking a grey-colored background and pulling it in and out with a CSS animation.

It’s actually really simple to do, so I’m just going to give us some room at the top here where we can select that skeleton class. This skeleton class will give us that grey background with the animation.

We’re going to call this animation skeleton loading, and it’ll be a one-second animation that follows a straight path. This means it’s only going to get worse.

This means it will just transition linearly between the two values we provide, looping indefinitely, thus we’ll use the term endless here.

which simply means that the animation’s transition will go from the beginning to the end. Then from the end all the way back to the beginning.

so it won’t jump from the end to the beginning, and then we can define some keyframes for that animation.

So we’ll just call it animation here, and at zero percent, we want to start with a greyish background color.

In our case, we’re going to use HSL with a value of 200 to give it a little bit of a blue hue, 20 for saturation to make it look greyish, and 70 for lightness because we want it to be a fairly light grey color.

Then we’ll just copy that down for our 100 animations. Change the lightness to 95 instead of 70 because that’s what we want.

How to create a beautiful scroll indicator design in CSS?

If we save that and come back over here, we’ll just make sure that we change this image here to a div so that we can see what it looks like without the image inside of it, so we’ll just give it a div with these exact same classes and save it, you can see that it doesn’t really look like anything’s going on right now.