Skeleton Loaders are used very commonly in Modern Websites/Apps to indicate loading of data instead of using the traditional loaders, spinners, etc. which are boring and can lead to Poor User Experience 😵💫
I created this tutorial to share my knowledge of how to create a Perfect Skeleton Screen that looks like the exact replica of the original element 😉
We are going to convert the Food Blog Card to it's own Skeleton loader as shown in the GIF below 🖼️
There are 3 Steps to creating a perfect Skeleton Screen 🤘
Step 1:
Make sure you already have implemented the HTML and CSS for the Original Element. In our case, I have included the code for the Food Blog Card below.
HTML Code ⬇️
<!DOCTYPE html>
<html lang="en">
<head>
<link
href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600;700;900&display=swap"
rel="stylesheet"
/>
</head>
<body>
<div class="container">
<div class="card">
<div class="img-cont">
<img
class="img"
src="https://images.unsplash.com/photo-1594398028856-f253a046f417?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1189&q=80"
alt="Food image"
/>
</div>
<div class="user-info">
<div class="user-avatar-cont">
<img
src="https://i.ibb.co/JzNYHV9/image-1.jpg"
alt="User Image"
class="user-avatar-img"
/>
</div>
<div class="user-details">
<div class="user-name"><span>Natalia Rodrigues</span></div>
<div class="user-profession"><span>Food Blogger</span></div>
</div>
</div>
</div>
</div>
</body>
</html>
CSS Code ⬇️
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 62.5%;
}
body {
font-family: 'Source Sans Pro', sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #eee;
}
.card {
background: #fff;
position: relative;
padding: 2rem;
border-radius: 5px;
box-shadow: 0 10px 100px rgba(0, 0, 0, 0.1);
width: 45rem;
overflow: hidden;
}
.img-cont {
height: 30rem;
margin-bottom: 2rem;
border-radius: 5px;
overflow: hidden;
}
.img {
width: 100%;
height: 100%;
object-fit: cover;
}
.user-info {
display: flex;
align-items: center;
}
.user-avatar-cont {
width: 6rem;
height: 6rem;
margin-right: 2rem;
border-radius: 50rem;
overflow: hidden;
}
.user-avatar-img {
width: 100%;
height: 100%;
object-fit: cover;
}
.user-details {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.user-name {
font-size: 2.2rem;
margin-bottom: 5px;
color: #444;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 600;
}
.user-profession {
font-size: 1.3rem;
color: #999;
border-radius: 2px;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 600;
}
Result ⬇️
Step 2:
Now we have to convert the card into its own Skeleton Version. To do that, I will add an extra class called card--skeleton on the HTML div with the class called card as shown below so we can target the elements inside the card.
<div class="card card--skeleton">
Now, we have 2 Images inside the card, the 1st image is the image of the Pizza and the 2nd Image is the image of the Person. Both of these images are wrapped inside their own container and those containers have their specific height.
Now, we will remove both of these images.
<div class="card">
<div class="img-cont">
<!-- Removed Image -->
</div>
<div class="user-info">
<div class="user-avatar-cont">
<!-- Removed Image -->
</div>
<div class="user-details">
<div class="user-name"><span>Natalia Rodrigues</span></div>
<div class="user-profession"><span>Food Blogger</span></div>
</div>
</div>
</div>
And we will add a background color to the image containers as shown below using the skeleton class.
.card--skeleton .img-cont {
background: #eee;
}
.card--skeleton .user-avatar-cont {
background: #eee;
}
So the end result will look like this ⬇️
We will do the same thing with User Name and User Profession elements. We will change the background color of both of these elements along with the text color inside it. The background color and the text color will be the same.
I have also added some border-radius which is optional.
.card--skeleton .user-name span,
.card--skeleton .user-profession span {
background: #eee;
color: #eee;
border-radius: 5px;
}
Now, the end result will look like this ⬇️
Looking cool, right? 😉
So, now we can move on to the 3rd Step where we will add the shining effect animation ⚡
Step 3:
In this step, we will add the Shining Effect Animation on the entire Skeleton Card.
To implement that, we will target the before sudo class of card--skeleton as shown below.
.card--skeleton::before {
content: '';
position: absolute;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.9),
transparent
);
width: 50%;
height: 100%;
top: 0;
left: 0;
}
So, we have added the before sudo element which is absolutely positioned and has the same height as the card--skeleton and has 50% width of the class--skeleton.
We also added linear-gradient as the background with 3 colors ( transparent color, white color, transparent color ) in the right direction.
So, it will make our card--skeleton look like this ⬇️
Now we will use CSS keyframes to move the gradient from the left side to the right side. Inside keyframes, we will target the transform property to skew ( basically to turn the linear gradient from | to / ) the element and to translate it in the X-direction.
I have already added the overflow: hidden value to the card element so when the before element goes outside of the card boundaries because of the keyframes transform, it's not going to be visible outside of the boundaries of card which is what we need.
@keyframes loading {
0% {
transform: skewX(-10deg) translateX(-100%);
}
100% {
transform: skewX(-10deg) translateX(200%);
}
}
.card--skeleton::before {
...
/* Refer the Keyframe inside the Animation */
animation: loading 0.6s infinite;
}
So, the end result will finally look like this ⬇️
and that's what we wanted 🥳🤘
I hope you find this post to be helpful and thanks for reading it 😇
Important 😸
I regularly post useful content related to Web Development and Programming on Linkedin and Twitter. You should consider Connecting with me or Following me on these Platforms.
Linkedin Profile, Twitter Profile
P.S. ✌️
I recently created one OpenSource Project which is a Portfolio Website Template for Developers called Dopefolio and wrote an Article about it on dev.to.
Feel free to check the article here 😄
Please React with ❤️ + 🦄 + 🔖 , As it takes time to create such content so it will be very helpful if you show some love to this post.
Share your feedback by Commenting below 💬
Drop me a Follow for more Awesome content related to Web Development and Programming 🙌
Top comments (30)
How do the user detail elements get the width, when on skeleton mode (without text inside)?
Nice idea to set dimensions on containers for this effect!
Yeah, if there's no text inside then we can set height for the elements. Thanks for sharing it, Tomas 😄
How can I just use the avatar and text beside it alone
If you mean how I'm making the avatar and text sit on the same line then for that I'm using the flex property on the container which will make the items inside it like text and avatar sit on the same line.
Okay what if I want to use the avatar and text repeatedly just like messages in Whatsapp how will I do that
Its awesome 👍
Thanks a lot, @amircahyadi 😄
Awesome !
Thanks a lot, @midouwebdev 😄
I've re-implemented this as a lesson on codeamigo.dev. You can check it out here: codeamigo.dev/lessons/start/87 if you're interested!
Amazing, Thank you so much ❤️
Nice one, but how do you determine the number of skeletons to display compare to the number of incoming data
It's up to you on how many Skeletons you wanna display and if you have pagination like you only show n number of items then you can show n number of skeleton screens.
I will personally render the skeletons just enough to fill the items view port even though the number of incoming items are greater, less than or equal to the number of pre-rendered skeletons.
That's a great idea, I will keep that in mind and hopefully will implement enough skeletons to fit the viewport/layout. Thanks a lot for sharing 😄
nice
Thanks a lot, @zeonlife
An amazing good job, bro!!!! ❤💯
Thank you so much, @maitrungdong 😄
But how de we make the "shine" dissapear when the content is loaded?
The idea here is to keep the Skeleton loader as its own separate component and card ( where we will show the data ) as its own component. So, you don't need to use the skeleton card to show the loaded data, we will show that in the card component and we will only use the skeleton loader element/component when we request the data to the server and until the request returns the data, once the data is returned back from the server, then we will use the original card to display the data. Hope this helps.
Amazing!!
Thanks a lot, @alanrmachado 😄
Well explained
Thanks for sharing your feedback, @jedstroke 😄