CSS Loading Animation
CSS is all about presentation. Discover how CSS Loading Animation works to transform plain HTML into a premium user experience in the guide below.
Introduction
When a visitor interacts with a button on our website, they anticipate receiving a response from the developer. Lack of feedback may lead the user to assume a potential issue and subsequently leave the site abruptly.
We can avoid these issues by implementing a loading animation. By incorporating a loading animation, we enhance the user experience by indicating that a background process is taking place.
What is a Loading Animation?
In CSS, a loading animation serves as a form of notification to reassure users that the system is actively processing their request. This animation is triggered when a user interacts with a button and continues until the request is fully processed.
Moreover, certain animations feature a status bar showing the time left for the request to finish. This feature offers users a dynamic update, enhancing their engagement. Numerous methods are available for crafting loading animations, with CSS standing out as the optimal choice for this purpose.
CSS for Loading Animations
We can also create the animation loader with the help of another language, such as JavaScript or a simple GIF loader. However, CSS provides some strengths for creating the animation loader. Those strengths are as follows.
- We can easily edit it. We can also quickly edit the duration, color, speed, and other animation elements.
- If we change the scale of the animation, then it does not lose its quality.
- If we use a graphics processing unit, then it makes the animation fast and smooth.
- We can also pause the animation with the help of the animation-play-state property.
Some web browsers like Internet Explorer 9 and Opera Mini might not have support for displaying the loading animation. In such instances, it becomes necessary to utilize GIF images.
Examples of CSS Loading Animations
There exists a wide array of loading animations accessible online. Let's explore a few illustrations.
1. Dump Truck Loading Animation
We can generate this animation using the HTML code provided below.
Code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dump Truck Loading Animation</title>
<style>
*, *:before, *:after {
border: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
font-size: 20px;
}
body {
background: #87beff;
color: #222;
font: 1em "Anton", sans-serif;
line-height: 1.5;
overflow-x: hidden;
}
/* all */
.progress-container, .progress-state, .dump-truck, .dump-truck div {
position: absolute;
}
/* progress */
.progress-container, .progress-box {
width: 6em;
}
.progress-container {
top: 50%;
left: calc(50% - 3em);
height: 7.5em;
z-index: 2;
}
.progress-box {
border-radius: 0 0 0.5em 0.5em;
border: 0.5em solid #505050;
border-top: 0;
height: 4em;
}
.progress-fill {
animation: fillDirt 10s linear forwards;
background: #643232;
transform: scaleY(0);
transform-origin: 50% 100%;
transition: transform 0.5s ease-out;
width: 100%;
height: 100%;
}
.progress-state {
bottom: 0;
left: 0;
font-size: 1.5em;
text-align: center;
}
.state-load {
animation: hideLoad 20s linear forwards;
}
.state-finish {
animation: showFinish 0.3s 20s linear forwards;
visibility: hidden;
}
/* trucks */
.dump-truck {
opacity: 0;
top: calc(50% - 4em);
width: 14em;
height: 8em;
}
.dump-truck:nth-of-type(2) {
animation: backupL 10s ease-in-out 2 forwards;
right: 50%;
transform: scaleX(-1) translateX(5em);
}
.dump-truck:nth-of-type(2) .bucket {
animation: dump 10s linear 2;
}
.dump-truck:nth-of-type(2) .dirt {
animation: rotateDirt 10s linear 2;
}
.dump-truck:nth-of-type(2) .dirt-spill {
animation: pourDirt 10s linear 2;
}
.dump-truck:nth-of-type(2) .wheel {
animation: spinL 10s ease-in-out 2;
}
.dump-truck:last-of-type {
animation: backupR 10s 5s ease-in-out 2 forwards;
left: 50%;
transform: translateX(5em);
}
.dump-truck:last-of-type .bucket {
animation: dump 10s 5s linear 2;
}
.dump-truck:last-of-type .dirt {
animation: rotateDirt 10s 5s linear 2;
}
.dump-truck:last-of-type .dirt-spill {
animation: pourDirt 10s 5s linear 2;
}
.dump-truck:last-of-type .wheel {
animation: spinR 10s 5s ease-in-out 2;
}
.front {
background: linear-gradient(#ff3f30, #ff3f30) 2.7em 0.75em/0.2em 2em, linear-gradient(#a0a0a0, #a0a0a0) 1em 3.5em/0.8em 0.2em, linear-gradient(#505050, #505050) 3.3em 6.1em/0.5em 0.2em, linear-gradient(#505050, #505050) 3.3em 6.8em/0.5em 0.2em, linear-gradient(83deg, #282828 49%, rgba(40, 40, 40, 0) 50%) 1em 0.75em/5em 2em, radial-gradient(3.4em 4.6em at 33% 100%, rgba(255, 255, 255, 0) 49%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 59%, rgba(255, 255, 255, 0) 60%), radial-gradient(3.4em 4.6em at 33% 100%, rgba(255, 63, 48, 0) 49%, #ff3f30 50%);
background-repeat: no-repeat;
border-radius: 10% 20% 5% 25%/10% 50% 5% 100%;
top: 0.4em;
left: 10.1em;
width: 3.9em;
height: 7em;
}
.bucket {
background: repeating-linear-gradient(95deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0) 0.85em, rgba(0, 0, 0, 0.15) 0.9em, rgba(0, 0, 0, 0.15) 1.75em, rgba(0, 0, 0, 0.15) 1.8em) 0.1em 1.6em/7.8em 3.2em, linear-gradient(#505050, #505050) 8em 0/2em 0.5em, linear-gradient(95deg, rgba(80, 80, 80, 0) 0.45em, #505050 0.5em, #505050 1.45em, rgba(80, 80, 80, 0) 1.5em) 7em 0/1.6em 5.5em, linear-gradient(rgba(80, 80, 80, 0) 1em, #505050 1em) 0 0/7.5em 5.5em;
background-repeat: no-repeat;
border-radius: 0 0 0 25%/0 0 0 15%;
top: 0;
left: 0;
width: 10em;
height: 5.5em;
transform: rotate(0deg);
transform-origin: 22% 100%;
}
.dirt {
background: #643232;
border-radius: 0 0 50% 50%/0 0 100% 100%;
top: 1.1em;
left: -3.2em;
width: 7.4em;
height: 3em;
transform: rotate(-25deg);
transform-origin: 50% 0;
z-index: -1;
}
.dirt-spill {
background: #643232;
border-radius: 0.4em;
top: 0;
left: -5.25em;
transform: rotate(-40deg) scale(1, 1);
transform-origin: 5.25em 0em;
width: 5.5em;
height: 0.8em;
}
.base {
background: #7f5b58;
border-radius: 0.25em;
top: 5.5em;
left: 2em;
width: 8.3em;
height: 1.5em;
}
.wheel {
background: radial-gradient(100% 100%, #282828 9%, rgba(40, 40, 40, 0) 10%), radial-gradient(100% 100%, #a0a0a0 29%, rgba(160, 160, 160, 0) 30%), linear-gradient(90deg, rgba(255, 255, 255, 0.15) 49%, rgba(255, 255, 255, 0) 50%), radial-gradient(100% 100%, #282828 49%, rgba(40, 40, 40, 0) 50%);
border-radius: 50%;
bottom: 0;
width: 2.4em;
height: 2.4em;
}
.wheel-front {
left: 10.4em;
}
.wheel-middle {
left: 4.5em;
}
.wheel-back {
left: 2em;
}
/* animations */
@keyframes backup {
from, 50%, to {
opacity: 0;
transform: scaleX(-1) translateX(20em);
}
12.5%, 37.5% {
opacity: 1;
transform: scaleX(-1) translateX(5em);
}
}
@keyframes backup {
from, 50%, to {
opacity: 0;
transform: translateX(20em);
}
12.5%, 37.5% {
opacity: 1;
transform: translateX(5em);
}
}
@keyframes dump {
from, 12.5%, 37.5%, to {
transform: rotate(0deg);
}
20%, 30% {
transform: rotate(-40deg);
}
}
@keyframes rotateDirt {
from, 19.9%, 30.1%, to {
opacity: 0;
}
20%, 30% {
opacity: 1;
transform: rotate(-40deg);
}
25% {
transform: rotate(-25deg);
}
}
@keyframes pourDirt {
from, 20% {
transform: rotate(-40deg) scale(0, 0);
}
25% {
transform: rotate(-40deg) scale(1, 1);
}
30%, to {
transform: rotate(-25deg) scale(0.8, 0);
}
}
@keyframes spinL {
from, 50%, to {
transform: rotate(-0.5turn);
}
12.5%, 37.5% {
transform: rotate(-2.5turn);
}
}
@keyframes spinR {
from, 50%, to {
transform: rotate(0);
}
12.5%, 37.5% {
transform: rotate(-2turn);
}
}
@keyframes hideLoad {
from {
visibility: visible;
}
to {
visibility: hidden;
}
}
@keyframes showFinish {
from {
transform: scale(0);
visibility: visible;
}
80% {
transform: scale(1.2);
}
to {
transform: scale(1);
visibility: visible;
}
}
</style>
</head>
<body>
<div class="progress-container">
<div class="progress-box">
<div class="progress-fill"></div>
</div>
<div class="progress-state state-load">Loading?</div>
<div class="progress-state state-finish">Complete!</div>
</div>
<div class="dump-truck">
<div class="front"></div>
<div class="bucket"></div>
<div class="dirt">
<div class="dirt-spill"></div>
</div>
<div class="base"></div>
<div class="wheel wheel-front"></div>
<div class="wheel wheel-middle"></div>
<div class="wheel wheel-back"></div>
</div>
<div class="dump-truck">
<div class="front"></div>
<div class="bucket"></div>
<div class="dirt">
<div class="dirt-spill"></div>
</div>
<div class="base"></div>
<div class="wheel wheel-front"></div>
<div class="wheel wheel-middle"></div>
<div class="wheel wheel-back"></div>
</div>
<script>
window.addEventListener("DOMContentLoaded", loading)
function loading() {
var percents = [0.25,0.5,0.75,1],
step = 0,
truckLoopDur = 10,
fill = function() {
let fillEl = document.querySelector(".progress-fill");
fillEl.style.transform = "scaleY(" + percents[step] + ")";
++step;
if (step < percents.length) {
setTimeout(fill, (truckLoopDur * 1e3)/2);
}
};
setTimeout(fill, (truckLoopDur * 1e3)/4);
}
</script>
</body>
</html>
Output:
2. Rainbow Loader
HTML CODE:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.loader {
background: #000;
background: radial-gradient(#222, #000);
bottom: 0;
left: 0;
overflow: hidden;
position: fixed;
right: 0;
top: 0;
z-index: 99999;
}
.loader-inner {
bottom: 0;
height: 60px;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0;
width: 100px;
}
.loader-line-wrap {
animation:
spin 2000ms cubic-bezier(.175, .885, .32, 1.275) infinite
;
box-sizing: border-box;
height: 50px;
left: 0;
overflow: hidden;
position: absolute;
top: 0;
transform-origin: 50% 100%;
width: 100px;
}
.loader-line {
border: 4px solid transparent;
border-radius: 100%;
box-sizing: border-box;
height: 100px;
left: 0;
margin: 0 auto;
position: absolute;
right: 0;
top: 0;
width: 100px;
}
.loader-line-wrap:nth-child(1) { animation-delay: -50ms; }
.loader-line-wrap:nth-child(2) { animation-delay: -100ms; }
.loader-line-wrap:nth-child(3) { animation-delay: -150ms; }
.loader-line-wrap:nth-child(4) { animation-delay: -200ms; }
.loader-line-wrap:nth-child(5) { animation-delay: -250ms; }
.loader-line-wrap:nth-child(1) .loader-line {
border-color: hsl(0, 80%, 60%);
height: 90px;
width: 90px;
top: 7px;
}
.loader-line-wrap:nth-child(2) .loader-line {
border-color: hsl(60, 80%, 60%);
height: 76px;
width: 76px;
top: 14px;
}
.loader-line-wrap:nth-child(3) .loader-line {
border-color: hsl(120, 80%, 60%);
height: 62px;
width: 62px;
top: 21px;
}
.loader-line-wrap:nth-child(4) .loader-line {
border-color: hsl(180, 80%, 60%);
height: 48px;
width: 48px;
top: 28px;
}
.loader-line-wrap:nth-child(5) .loader-line {
border-color: hsl(240, 80%, 60%);
height: 34px;
width: 34px;
top: 35px;
}
@keyframes spin {
0%, 15% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
</style>
<body>
<div class="loader">
<div class="loader-inner">
<div class="loader-line-wrap">
<div class="loader-line"></div>
</div>
<div class="loader-line-wrap">
<div class="loader-line"></div>
</div>
<div class="loader-line-wrap">
<div class="loader-line"></div>
</div>
<div class="loader-line-wrap">
<div class="loader-line"></div>
</div>
<div class="loader-line-wrap">
<div class="loader-line"></div>
</div>
</div>
</div>
</body>
</html>
Output:
3. Square in a circle - Loading Animation
HTML code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
display:flex;
align-items:center;
height:100vh;
background-color:#001524;
}
.container {
display:block;
height:300px;
width:300px;
margin:0 auto;
}
.baton {
display:block;
height:2px;
width:70px;
background-color:#459fa5;
animation: scale 1.25s infinite linear;
-webkit-transform-origin: 0;
-moz-transform-origin: 0;
-ms-transform-origin: 0;
-o-transform-origin: 0;
transform-origin: 0;
-webkit-transform: rotate(-10deg);
-moz-transform: rotate(-10deg);
-ms-transform: rotate(-10deg);
-o-transform: rotate(-10deg);
transform: rotate(-10deg);
&:before {
content:'';
display:block;
height:5px;
width:5px;
background-color:#f5a51c;
position:absolute;
top:-2px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-ms-border-radius: 5px;
-o-border-radius: 5px;
border-radius: 5px;
}
&:after {
content:'';
display:block;
height:5px;
width:5px;
background-color:#f5a51c;
position:absolute;
top:-2px;
right:0;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-ms-border-radius: 5px;
-o-border-radius: 5px;
border-radius: 5px;
}
}
.metronome {
-webkit-transform-origin: 0;
-moz-transform-origin: 0;
-ms-transform-origin: 0;
-o-transform-origin: 0;
transform-origin: 0;
animation: metronome 1.25s infinite linear;
}
@for $i from 0 through 35 {
.baton-#{$i} {
height:1px;
width:150px;
position:absolute;
top:calc(50% - 8px);
-webkit-transform-origin: 100%;
-moz-transform-origin: 100%;
-ms-transform-origin: 100%;
-o-transform-origin: 100%;
transform-origin: 100%;
-webkit-transform: rotate($i * 10deg);
-moz-transform: rotate($i * 10deg);
-ms-transform: rotate($i * 10deg);
-o-transform: rotate($i * 10deg);
.baton, .baton:after, .metronome {
animation-delay: -$i * 0.14s;
}
}
}
@keyframes metronome {
0% {
-webkit-transform: rotate(-25deg);
-moz-transform: rotate(-25deg);
-ms-transform: rotate(-25deg);
-o-transform: rotate(-25deg);
transform: rotate(-25deg); }
50% {
-webkit-transform: rotate(25deg);
-moz-transform: rotate(25deg);
-ms-transform: rotate(25deg);
-o-transform: rotate(25deg);
transform: rotate(25deg); }
100% {
-webkit-transform: rotate(-25deg);
-moz-transform: rotate(-25deg);
-ms-transform: rotate(-25deg);
-o-transform: rotate(-25deg);
transform: rotate(-25deg); }
}
@keyframes scale {
0% {
-webkit-transform: scaleX(1);
-moz-transform: scaleX(1);
-ms-transform: scaleX(1);
-o-transform: scaleX(1);
transform: scaleX(1); }
25% {
-webkit-transform: scaleX(0.74);
-moz-transform: scaleX(0.74);
-ms-transform: scaleX(0.74);
-o-transform: scaleX(0.74);
transform: scaleX(0.74); }
50% {
-webkit-transform: scaleX(1);
-moz-transform: scaleX(1);
-ms-transform: scaleX(1);
-o-transform: scaleX(1);
transform: scaleX(1); }
75% {
-webkit-transform: scaleX(1.16);
-moz-transform: scaleX(1.16);
-ms-transform: scaleX(1.16);
-o-transform: scaleX(1.16);
transform: scaleX(1.16); }
100% {
-webkit-transform: scaleX(1);
-moz-transform: scaleX(1);
-ms-transform: scaleX(1);
-o-transform: scaleX(1);
transform: scaleX(1); }
}
</style>
</head>
<body>
<div class="container">
<div class="baton-0"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-1"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-2"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-3"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-4"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-5"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-6"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-7"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-8"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-9"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-10"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-11"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-12"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-13"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-14"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-15"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-16"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-17"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-18"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-19"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-20"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-21"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-22"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-23"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-24"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-25"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-26"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-27"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-28"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-29"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-30"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-31"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-32"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-33"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-34"><div class="metronome"><div class="baton"></div></div></div>
<div class="baton-35"><div class="metronome"><div class="baton"></div></div></div>
</div>
</body>
</html>
Output:
4. Single Element CSS-Only Absolute Center Overlay Spinner
HTML code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/* Absolute Center Spinner */
.loading {
position: fixed;
z-index: 999;
height: 2em;
width: 2em;
overflow: visible;
margin: auto;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
/* Transparent Overlay */
.loading:before {
content: '';
display: block;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.3);
}
/* :not(:required) hides these rules from IE9 and below */
.loading:not(:required) {
/* hide "loading..." text */
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
.loading:not(:required):after {
content: '';
display: block;
font-size: 10px;
width: 1em;
height: 1em;
margin-top: -0.5em;
-webkit-animation: spinner 1500ms infinite linear;
-moz-animation: spinner 1500ms infinite linear;
-ms-animation: spinner 1500ms infinite linear;
-o-animation: spinner 1500ms infinite linear;
animation: spinner 1500ms infinite linear;
border-radius: 0.5em;
-webkit-box-shadow: rgba(0, 0, 0, 0.75) 1.5em 0 0 0, rgba(0, 0, 0, 0.75) 1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) 0 1.5em 0 0, rgba(0, 0, 0, 0.75) -1.1em 1.1em 0 0, rgba(0, 0, 0, 0.5) -1.5em 0 0 0, rgba(0, 0, 0, 0.5) -1.1em -1.1em 0 0, rgba(0, 0, 0, 0.75) 0 -1.5em 0 0, rgba(0, 0, 0, 0.75) 1.1em -1.1em 0 0;
box-shadow: rgba(0, 0, 0, 0.75) 1.5em 0 0 0, rgba(0, 0, 0, 0.75) 1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) 0 1.5em 0 0, rgba(0, 0, 0, 0.75) -1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) -1.5em 0 0 0, rgba(0, 0, 0, 0.75) -1.1em -1.1em 0 0, rgba(0, 0, 0, 0.75) 0 -1.5em 0 0, rgba(0, 0, 0, 0.75) 1.1em -1.1em 0 0;
}
/* Animation */
@-webkit-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-moz-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-o-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<div class="loading">Loading?</div>
</body>
</html>
Output: