Loaders are extremely useful for informing a user that something is happening. The alternative to using loaders is just hoping that the user understands that they need to wait. Often, this just leads to the user becoming frustrated or, even worse, leaving the application. Obviously, we don't want this, instead, it's better to incorporate a visual loader in your application to improve the user experience.
Now, let's get to coding a bubble loader. We'll take inspiration from the Facebook Messenger typing loader. This comes up every time your friend is typing a message. It's a great way to let users know that they're getting a reply. It's all visual and everyone understands what it is.
What we'll be using
- React
- CSS Variables
- KeyFrames
Here's what our loader will look like at the end.
Diving into the code
Let's start with the jsx markup.
function Messenger() {
return (
<div className="Messenger">
<Loader />
</div>
);
}
function Loader() {
let dots = Array(3).fill(3);
return (
<div className="Wrapper">
<div className="Loader">
{dots.map((_, i) => (
<div style={{ "--i": i }} className="Dot" key={i} />
))}
</div>
<div className="Background" />
</div>
);
}
The Messenger component will just act as the wrapper. We'll use it to set the background color for the page and to align the Loader in the center.
In the Loader, we'll put three divs, each with their own CSS variable --i. We'll use this variable for setting the animation-delay.
We also have a Background div but I'll explain more about that later.
The main take away is that we can set the CSS variable to the index of the item. That's going to make our animation super easy to make! 🥳
Now to the CSS
.Messenger {
height: 100%;
width: 100%;
background: #F9B5AC;
display: flex;
justify-content: center;
align-items: center;
}
.Loader {
height:80px;
width: 200px;
display: flex;
justify-content: space-evenly;
align-items: center;
border-top-left-radius: 3px;
border-top-right-radius: 40px;
border-bottom-right-radius: 40px;
border-bottom-left-radius: 40px;
z-index: 3;
background: #9DBF9E;
cursor: pointer;
}
These are the basic styles and are self-explanatory.
Animation time! 🥳🥳
@keyframes move {
0% {
transform: translateY(0%);
}
25% {
transform: translateY(-40%)
}
50% {
transform: translateY(-80%)
}
75% {
transform: translateY(10%);
}
100% {
transform: translateY(0%);
}
}
.Dot {
transform: none;
animation: whoop 1s linear infinite forwards;
animation-delay: calc(var(--i) * .2s);
width: 20px;
height: 20px;
background: #EE7674;
border-radius: 50%;
z-index: 1;
}
As you can see, the keyframes define our animation. Next, we just apply an animation-delay by multiply the variable --i times .2s. This gives us the awesome effect of each dot moving after the other.
Let's take a look at the CSS variable again.
dots.map((_, i) => <div style={{ "--i": i }} className="Dot" key={i} />)
As you can see, we're applying the index of the array to the CSS variable.
Next, in the CSS we just multiply the variable times .2s and the result is each dot moving one after the other!
.Dot {
/*...*/
animation-delay: calc(var(--i) * .2s);
/*...*/
}
But wait, there's more!
Remember that Background div? Well, we can use it to be the box shadow of the loader. Then on:hover we add a transform of translate in the loader. That way we can hover over the loader and the Background will remain in place. This gives us a cool hover effect!
.Loader {
/*...*/
transition: transform .3s ease-in-out;
&:hover {
transform: translate(10px, 10px);
}
/*...*/
}
.Background {
position: absolute;
height:80px;
width: 200px;
top: 0;
border-top-left-radius: 3px;
border-top-right-radius: 40px;
border-bottom-right-radius: 40px;
border-bottom-left-radius: 40px;
box-shadow: 10px 10px .07rem rgba(rgb(69, 107, 129), .7);
z-index: 1;
}
Try it out by hovering your mouse over the loader 😇
So that's it! I hope you learned something!
Extra resources:
I learned the CSS variable trick from the fantastic youtube channel keyframers. They're focused on making performant CSS animations that are easy to understand. Check them out!
Top comments (1)
Nice, but one really annoying thing is that the implementation is with React.
The focus should be on CSS and key frames, and the whole wrap is a disturbance