Ripple Loader
A smooth ripple loader made with expanding circular rings. It uses pure HTML and CSS with no JavaScript required.
<div class="ripple-loader" aria-label="Loading">
<span></span>
<span></span>
</div>
.ripple-loader{
position:relative;
width:70px;
height:70px;
}
.ripple-loader span{
position:absolute;
inset:0;
border:3px solid #22d3ee;
border-radius:50%;
opacity:0;
animation:rippleWave 1.6s ease-out infinite;
}
.ripple-loader span:nth-child(2){
animation-delay:.8s;
}
@keyframes rippleWave{
0%{
transform:scale(.25);
opacity:1;
}
100%{
transform:scale(1);
opacity:0;
}
}
How This Ripple Loader Works
The Ripple Loader is a CSS-only loading indicator built from one parent <div> and two empty <span> elements. Each span becomes a circular outline that expands outward and fades away, creating a repeating ripple effect. The component does not use JavaScript, so the animation starts automatically when the CSS is applied.
The .ripple-loader container is 70px wide and 70px high. It uses position: relative, which creates the positioning reference for both animated spans inside it. The parent itself has no visible border or background; its job is to define the area in which the ripple circles expand.
Both spans use position: absolute together with inset: 0. This makes each span fill the full 70px by 70px dimensions of the parent. A 3px solid #22d3ee border creates the visible cyan outline, while border-radius: 50% turns each span into a circle.
The circles begin with opacity: 0 in their base style, but the animation immediately controls their visibility. Both spans run the same rippleWave keyframes for 1.6s, use an ease-out timing function, and repeat infinitely.
At the start of the animation, the circle uses transform: scale(.25) and opacity: 1. This means the ring begins at one quarter of its full size and is completely visible.
By the end of the animation, the transform reaches scale(1) while opacity falls to 0. The ring therefore grows outward until it reaches the full 70px diameter, while gradually fading away at the same time.
The second span uses animation-delay: .8s. Since the full animation lasts 1.6s, this delay places the second ripple halfway through the first ripple’s cycle. As a result, one circle begins expanding while the other is already farther along, producing a continuous sequence instead of both rings appearing at once.
The ease-out timing function makes the expansion move more quickly at the beginning and slow toward the end of the cycle. Combined with the fading opacity, this gives the circles a softer outward ripple instead of a mechanically constant expansion.
The scaling itself is handled through the CSS transform property. For more information about resizing elements with this property, see the MDN CSS transform documentation.
The parent element also includes aria-label="Loading". This gives the loader a short accessible description even though the component does not display visible text.
Customizing the Ripple Loader
The component can be adjusted through a few existing size, color, border, and animation values.
- Change the
70pxwidth and height of.ripple-loaderto make the overall ripple area larger or smaller. - Adjust the
3pxborder width to create thinner or heavier ripple rings. - Replace
#22d3eewith another border color that matches your interface. - Change the starting
scale(.25)to make each ripple begin smaller or larger. - Modify the
1.6sanimation duration to make the expansion faster or slower. - Adjust the
.8sdelay on the second span to change the spacing between the two ripple waves. - Replace
ease-outwith another timing function if you want a different expansion pattern. - Adjust the ending opacity if you want the rings to remain slightly visible instead of fading completely.
Where to Use the Ripple Loader
This loader works well for interfaces that need a simple indefinite activity indicator without showing an exact completion percentage.
- Form submission states while a request is being processed.
- Dashboard cards waiting for data to load.
- Modal windows that are fetching remote content.
- Full-page loading overlays with a centered indicator.
- Cards or widgets that load independently from the rest of the interface.
- Minimal interfaces where a soft circular animation fits better than a spinner.
If you need a loader that also displays measurable progress, the Neon Energy Progress Loader is a related CodeRipple component worth considering. The Ripple Loader is better suited to situations where the duration is unknown and you only need to show that work is still in progress.
Frequently Asked Questions
Does the Ripple Loader use JavaScript?
No. The component contains no JavaScript. Its complete effect is created with CSS borders, scaling, opacity changes, animation delays, and keyframes.
Why are there two span elements?
Each span creates one expanding circle. The second starts .8s later than the first, which produces overlapping ripple waves and keeps the animation continuous.
How does each ripple expand outward?
The animation changes the ring from scale(.25) to scale(1). This gradually increases its size from one quarter of the final dimensions to the full 70px circle.
How can I make the ripple effect faster?
Reduce the current 1.6s animation duration. You may also reduce the .8s second-ring delay proportionally if you want to keep the two ripples evenly spaced.
The Ripple Loader uses two circular borders and one shared scale-and-fade animation to create a compact loading effect with minimal markup and no scripting.
