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:
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
inside that section for adding hooks to a text. Assigned a class “skeleton-loader-background” for styling background.<span>
C. Specify
with light-gray color.background
Image: Skeleton Loader Background
<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
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.linear-gradient
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
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
name, duration and iteration count.animation
D. Using
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%).keyframes
Image: Skeleton Loader Animation
.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
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.:empty
<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.
will get fired on load of HTML DOM. We will perform JavaScript operations on receipt of this event.DOMContentLoaded
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.
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.