Pulse Loader
A clean animated loader with three pulsing dots. It works with pure HTML and CSS, so you can use it without JavaScript or extra libraries.
<div class="pulse-loader" aria-label="Loading">
<span></span>
<span></span>
<span></span>
</div>
.pulse-loader{
display:flex;
align-items:center;
gap:10px;
}
.pulse-loader span{
width:14px;
height:14px;
border-radius:50%;
background:#22d3ee;
animation:pulseDot 1.2s ease-in-out infinite;
}
.pulse-loader span:nth-child(2){
animation-delay:.15s;
}
.pulse-loader span:nth-child(3){
animation-delay:.3s;
}
@keyframes pulseDot{
0%,100%{
transform:scale(.65);
opacity:.45;
}
50%{
transform:scale(1);
opacity:1;
}
}
How This Pulse Loader Works
The Pulse Loader is a CSS-only loading indicator built from one parent <div> and three empty <span> elements. Each span becomes a circular dot, and the three dots pulse at slightly different times to create a repeating loading sequence. The component does not use JavaScript.
The .pulse-loader container uses display: flex, so the three dots appear in a horizontal row. align-items: center keeps them vertically aligned, while a 10px gap creates equal spacing between each dot.
Every span is 14px wide and 14px high. The equal dimensions form a square initially, and border-radius: 50% turns that square into a circle. All three dots use the cyan background color #22d3ee.
Each dot runs the same pulseDot animation. The animation lasts 1.2s, uses an ease-in-out timing function, and repeats infinitely. Instead of moving the dots across the page, the animation changes their scale and opacity.
At the 0% and 100% points of the keyframes, each dot uses transform: scale(.65) and opacity: .45. This makes the dot smaller and partially transparent at the beginning and end of each cycle.
At 50%, the scale increases to 1 and opacity becomes 1. The dot therefore grows to its full 14px size while becoming completely visible. After reaching that midpoint, it gradually returns to the smaller, faded state.
The three dots do not reach their largest state at the same time. The first span has no extra delay, while the second span uses animation-delay: .15s and the third uses .3s. These staggered delays make the pulse travel from the first dot to the third instead of having all three expand together.
The ease-in-out timing function gives the scale change a gradual start and finish. This makes each pulse appear softer than it would with a constant linear speed. For more information about controlling animation timing, see the MDN CSS animation documentation.
Unlike a bouncing loader, this component does not change the dots’ positions. The animation is limited to scaling and opacity, so the loader remains compact and stable while still indicating ongoing activity.
The parent element also includes aria-label="Loading". This provides a simple accessible description for assistive technologies even though there is no visible text inside the loader.
Customizing the Pulse Loader
The Pulse Loader can be adjusted by changing a small number of size, spacing, color, and animation values in the existing CSS.
- Change the
10pxgap to increase or reduce the spacing between the dots. - Adjust the
14pxwidth and height to make each dot larger or smaller. - Replace
#22d3eewith another color to match your interface. - Change the
1.2sanimation duration to make each pulse faster or slower. - Modify
scale(.65)to control how small the dots become between pulses. - Adjust the
.45opacity value to make inactive dots more or less visible. - Change the
.15sand.3sanimation delays to alter the timing between the three dots. - Replace
ease-in-outwith another timing function if you want a different pulse rhythm.
Where to Use the Pulse Loader
This loader works well in interfaces that need a small indefinite loading indicator without displaying an exact completion percentage.
- Form submission states while data is being processed.
- Chat or messaging interfaces that need a compact activity indicator.
- Dashboard cards waiting for remote content.
- Modal windows during asynchronous operations.
- Buttons or small controls that temporarily enter a loading state.
- Cards, widgets, or panels where a simple horizontal loader fits naturally.
If you want a similar multi-dot loader with visible vertical movement, the Bouncing Dots Loader is a related CodeRipple component to consider. The Pulse Loader is better suited to layouts where you want the loader to stay in one position while still providing noticeable visual feedback.
Frequently Asked Questions
Does the Pulse Loader use JavaScript?
No. The component contains no JavaScript. Its complete effect is created with CSS animation, scale transforms, opacity changes, and staggered delays.
How does each dot create the pulsing effect?
The dots alternate between scale(.65) with .45 opacity and scale(1) with full opacity. This makes each dot appear to grow brighter and larger before shrinking again.
Why do the dots pulse one after another?
The second dot uses a .15s animation delay, while the third uses .3s. These delays offset the same animation and create the staggered sequence.
Can I make the pulse effect stronger?
Yes. Reduce the starting scale below .65 or lower the starting opacity below .45. A bigger difference between the inactive and active states will make the pulse more noticeable.
The Pulse Loader uses three simple dots, one shared keyframe animation, and small timing offsets to create a clean CSS-only loading indicator without any scripting.
