Placeholders
Creating placeholders for page content can sometimes be as simple as adding a background effect to existing page elements. In this example, all list items share the same underlying HTML structure. However, the placeholders are styled with a gray background that uses a CSS animation to create a pulsing loading effect.
Depending on how your page elements are implemented, you may also need to adjust your CSS slightly to ensure that the placeholders still take up space even though they don't contain content. Often this takes the form of adding width
or height
properties to existing page elements. For instance, in this example, adding height: 1.5em
to .text-container
ensures that its placeholder remains visible even though it doesn't contain any text.
<div class="grid">
<div class="item">
<div class="image-container">
<img src="hats.jpg">
</div>
<div class="text-container">Hats</div>
</div>
<div class="item empty">
<div class="image-container">
<img src="">
</div>
<div class="text-container">Watches</div>
</div>
<div class="item empty">
<div class="image-container">
<img src="">
</div>
<div class="text-container"></div>
</div>
<div class="item empty">
<div class="image-container">
<img src="">
</div>
<div class="text-container"></div>
</div>
<div class="item empty">
<div class="image-container">
<img src="">
</div>
<div class="text-container"></div>
</div>
<div class="item empty">
<div class="image-container">
<img src="">
</div>
<div class="text-container"></div>
</div>
</div>
const data = [
{
description: "Watches",
src: "https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/GMPpoERpp9aM5Rihk5F2.jpg"
},
{
description: "Shirt",
src: "shirt.jpg"
},
{
description: "Shorts",
src: "shorts.jpg"
},
{
description: "Sunglasses",
src: "sunglasses.jpg"
},
{
description: "Shoes",
src: "shoes.jpg"
}
];
document.querySelectorAll(".item.empty").forEach((el, index) => {
if (data[index]) {
el.classList = "item loaded";
el.querySelector("img").src = data[index].src;
el.querySelector(".text-container").innerHTML = data[index].description;
}
});
:root {
--placeholder-primary: #eeeeee;
--placeholder-secondary: #cccccc;
}
.grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
gap: 1em;
width: 100%;
max-width: 650px;
margin: 1em 0em;
}
.item {
display: grid;
gap: .5em;
width: 200px;
}
.text-container {
font-size: 1em;
height: 1.5em;
text-align: center;
font-weight: bold;
}
.image-container {
width: 200px;
height: 200px;;
animation: placeholder ease-in-out 2s infinite;
}
.image-container img {
width: 100%;
}
@keyframes placeholder {
0% {
background-color: var(--placeholder-primary);
}
50% {
background-color: var(--placeholder-secondary);
}
100% {
background-color: var(--placeholder-primary);
}
}
@keyframes fadeIn {
0% {
opacity: 0%;
}
100% {
opacity: 100%;
}
}
.item.loaded .image-container {
animation: none;
}
.item.loaded .image-container img{
animation: fadeIn linear .5s;
}