Hey! Programmers, In This Post we are going to make Bubble Loading Animation With HTML & CSS. In this tutorial we will be focusing on the concepts that are used to make creative animation like keyframes, transform, etc
Bubble Loading animation can increase the user experience of website by engaging users till the content of application loads. This reduce the bounce rate & dwell time which are helpsfull to increase the SEO Of your website.
Also Read: Book Loading Animation With HTML & CSS
In this blog post we will understand HTML & CSS Code step by step so that you can make such project on your own. At the last of this project I will share the source code of this bubble loading animation so that you can run this on your local machine.
The HTML structure creates a loader with multiple animated circles for our bubble loading animation :
div
with the class loader
is the main container for the loading animation. It holds multiple circle
elements. <div class="loader">
loader
container, there are four div
elements with the class circle
. Each circle
contains a dot
and an outline
, which are used for the animations. <div class="circle">
<div class="dot"></div>
<div class="outline"></div>
</div>
<div class="circle">
<div class="dot"></div>
<div class="outline"></div>
</div>
<div class="circle">
<div class="dot"></div>
<div class="outline"></div>
</div>
<div class="circle">
<div class="dot"></div>
<div class="outline"></div>
</div>
.loader
class uses display: flex
to align the circle
elements horizontally. The justify-content: center
and align-items: center
properties center the circles within the container. Custom properties (CSS variables) are defined: --color
for the circle color and --animation
for the animation timing. .loader {
display: flex;
justify-content: center;
align-items: center;
--color: hsl(0, 0%, 87%);
--animation: 2s ease-in-out infinite;
}
.circle
class sets up the basic shape and size of each circle. It positions the circles relatively and sets a border with the color defined by --color
. The animation: circle-keys var(--animation)
applies the circle-keys
keyframe animation to each circle. .loader .circle {
display: flex;
align-items: center;
justify-content: center;
position: relative;
width: 20px;
height: 20px;
border: solid 2px var(--color);
border-radius: 50%;
margin: 0 10px;
background-color: transparent;
animation: circle-keys var(--animation);
}
.dot
class is positioned absolutely within the circle
and centered using transform: translate(-50%, -50%)
. It is smaller than the circle, with a background color matching --color
. The animation: dot-keys var(--animation)
applies the dot-keys
keyframe animation to each dot. .loader .circle .dot {
position: absolute;
transform: translate(-50%, -50%);
width: 16px;
height: 16px;
border-radius: 50%;
background-color: var(--color);
animation: dot-keys var(--animation);
}
.outline
class is also positioned absolutely within the circle
, with the same transform and positioning as the dot
. The animation: outline-keys var(--animation)
applies the outline-keys
keyframe animation to each outline. .loader .circle .outline {
position: absolute;
transform: translate(-50%, -50%);
width: 20px;
height: 20px;
border-radius: 50%;
animation: outline-keys var(--animation);
}
circle
and its children (dot
and outline
) have different animation delays set using nth-child
selectors to create a staggered effect. .circle:nth-child(2) {
animation-delay: 0.3s;
}
.circle:nth-child(3) {
animation-delay: 0.6s;
}
.circle:nth-child(4) {
animation-delay: 0.9s;
}
.circle:nth-child(5) {
animation-delay: 1.2s;
}
.circle:nth-child(2) .dot {
animation-delay: 0.3s;
}
.circle:nth-child(3) .dot {
animation-delay: 0.6s;
}
.circle:nth-child(4) .dot {
animation-delay: 0.9s;
}
.circle:nth-child(5) .dot {
animation-delay: 1.2s;
}
.circle:nth-child(1) .outline {
animation-delay: 0.9s;
}
.circle:nth-child(2) .outline {
animation-delay: 1.2s;
}
.circle:nth-child(3) .outline {
animation-delay: 1.5s;
}
.circle:nth-child(4) .outline {
animation-delay: 1.8s;
}
.circle:nth-child(5) .outline {
animation-delay: 2.1s;
}
@keyframes circle-keys
animation changes the transform
property to scale the circle and adjusts its opacity
. @keyframes circle-keys {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.5);
opacity: 0.5;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes dot-keys
animation scales the dot
from its original size down to zero and then back to its original size. @keyframes dot-keys {
0% {
transform: scale(1);
}
50% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes outline-keys
animation scales the outline
from zero to its full size while adjusting the outline
property and outline-offset
to create a growing and fading effect. @keyframes outline-keys {
0% {
transform: scale(0);
outline: solid 20px var(--color);
outline-offset: 0;
opacity: 1;
}
100% {
transform: scale(1);
outline: solid 0 transparent;
outline-offset: 20px;
opacity: 0;
}
}
HTML:
<div class="loader">
<div class="circle">
<div class="dot"></div>
<div class="outline"></div>
</div>
<div class="circle">
<div class="dot"></div>
<div class="outline"></div>
</div>
<div class="circle">
<div class="dot"></div>
<div class="outline"></div>
</div>
<div class="circle">
<div class="dot"></div>
<div class="outline"></div>
</div>
</div>
Also Read: Radar Loading Animation With HTML & CSS
CSS:
.loader {
display: flex;
justify-content: center;
align-items: center;
--color: hsl(0, 0%, 87%);
--animation: 2s ease-in-out infinite;
}
.loader .circle {
display: flex;
align-items: center;
justify-content: center;
position: relative;
width: 20px;
height: 20px;
border: solid 2px var(--color);
border-radius: 50%;
margin: 0 10px;
background-color: transparent;
animation: circle-keys var(--animation);
}
.loader .circle .dot {
position: absolute;
transform: translate(-50%, -50%);
width: 16px;
height: 16px;
border-radius: 50%;
background-color: var(--color);
animation: dot-keys var(--animation);
}
.loader .circle .outline {
position: absolute;
transform: translate(-50%, -50%);
width: 20px;
height: 20px;
border-radius: 50%;
animation: outline-keys var(--animation);
}
.circle:nth-child(2) {
animation-delay: 0.3s;
}
.circle:nth-child(3) {
animation-delay: 0.6s;
}
.circle:nth-child(4) {
animation-delay: 0.9s;
}
.circle:nth-child(5) {
animation-delay: 1.2s;
}
.circle:nth-child(2) .dot {
animation-delay: 0.3s;
}
.circle:nth-child(3) .dot {
animation-delay: 0.6s;
}
.circle:nth-child(4) .dot {
animation-delay: 0.9s;
}
.circle:nth-child(5) .dot {
animation-delay: 1.2s;
}
.circle:nth-child(1) .outline {
animation-delay: 0.9s;
}
.circle:nth-child(2) .outline {
animation-delay: 1.2s;
}
.circle:nth-child(3) .outline {
animation-delay: 1.5s;
}
.circle:nth-child(4) .outline {
animation-delay: 1.8s;
}
.circle:nth-child(5) .outline {
animation-delay: 2.1s;
}
@keyframes circle-keys {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.5);
opacity: 0.5;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes dot-keys {
0% {
transform: scale(1);
}
50% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes outline-keys {
0% {
transform: scale(0);
outline: solid 20px var(--color);
outline-offset: 0;
opacity: 1;
}
100% {
transform: scale(1);
outline: solid 0 transparent;
outline-offset: 20px;
opacity: 0;
}
}
Last Updated: June 18, 2024