Bouncing Dots Loader
A playful three-dot loader with a smooth bouncing animation. It uses only HTML and CSS, so it stays lightweight and easy to reuse.
<div class="bouncing-dots-loader" aria-label="Loading">
<span></span>
<span></span>
<span></span>
</div>
.bouncing-dots-loader{
display:flex;
align-items:flex-end;
gap:10px;
height:42px;
}
.bouncing-dots-loader span{
width:14px;
height:14px;
border-radius:50%;
background:#22d3ee;
animation:bounceDot .8s ease-in-out infinite alternate;
}
.bouncing-dots-loader span:nth-child(2){
animation-delay:.15s;
}
.bouncing-dots-loader span:nth-child(3){
animation-delay:.3s;
}
@keyframes bounceDot{
from{
transform:translateY(0);
}
to{
transform:translateY(-22px);
}
}
How This Bouncing Dots Loader Works
The Bouncing Dots Loader is a CSS-only loading indicator built from one parent <div> and three empty <span> elements. Each span becomes a circular cyan dot, and the dots move upward at slightly different times to create a continuous bouncing sequence. The component does not use JavaScript, so the complete effect is handled through HTML and CSS.
The .bouncing-dots-loader container uses display: flex, which places the three dots next to each other in a horizontal row. A gap of 10px creates equal spacing between them, keeping the animation easy to read without requiring margins on the individual spans.
The container is 42px high and uses align-items: flex-end. This flexbox setting places all three dots along the bottom edge of the loader before their animations move them upward. Because the dots begin from the same baseline, the difference in their animation timing creates a clear bouncing pattern.
Each span is 14px wide and 14px high. The equal dimensions create a square initially, while border-radius: 50% turns each one into a circle. The dots use the cyan color #22d3ee as their background.
All three dots run the same bounceDot animation. It lasts .8s, uses an ease-in-out timing function, repeats infinitely, and includes the alternate animation direction.
The bounceDot keyframes are intentionally simple. In the from state, each dot uses transform: translateY(0), so it remains at its original position along the bottom of the container. In the to state, the transform changes to translateY(-22px).
The negative Y translation moves the dot upward by 22 pixels. Since CSS coordinates increase downward on the vertical axis, a negative value moves an element in the opposite direction. For more information about this type of movement, see the MDN CSS transform documentation.
The alternate value is especially important to the bouncing behavior. After a dot reaches translateY(-22px), the next animation cycle plays in reverse and returns it to translateY(0). This creates a smooth up-and-down movement without needing separate intermediate keyframes.
The three dots do not begin their cycles at exactly the same time. The first dot has no additional delay, while the second uses animation-delay: .15s and the third uses .3s. These staggered delays create the wave-like sequence in which one dot rises shortly after another.
The ease-in-out timing function also affects the character of the motion. Each dot accelerates and decelerates during the movement instead of traveling at one constant speed. Combined with the alternating direction, this produces a softer bounce than a linear animation would.
There is no JavaScript attached to the loader. Once the element is present and its CSS is loaded, the three dots continue bouncing indefinitely. The parent also includes aria-label="Loading", which gives the visual indicator a simple accessible description.
Customizing the Bouncing Dots Loader
The loader uses only a few CSS values, so its dimensions, spacing, color, and movement can be adjusted without changing its basic structure.
- Change the
10pxgap to increase or reduce the spacing between the dots. - Adjust the
42pxcontainer height if you need more or less vertical room for the animation. - Change the
14pxwidth and height to make each dot larger or smaller. - Replace
#22d3eewith another color to match your interface. - Modify the
.8sanimation duration to make the bouncing sequence faster or slower. - Change
translateY(-22px)to increase or reduce how high each dot travels. - Adjust the
.15sand.3sdelays to change the timing between the second and third dots. - Replace
ease-in-outwith another timing function if you want a different acceleration pattern.
Where to Use the Bouncing Dots Loader
This loader works well in places where you need to show ongoing activity but do not have a meaningful percentage or progress value to display.
- Form submission states while a request is being processed.
- Chat or messaging interfaces that need a compact activity indicator.
- Dashboard cards waiting for remote data.
- Modal windows while asynchronous content is loading.
- Buttons or small interface areas that need a lightweight loading state.
- Full-page loading screens where a minimal animation is preferred.
For another dot-based loading pattern, the Chasing Dots Loader is a related CodeRipple component that uses multiple dots arranged around a circle. The Bouncing Dots Loader is better suited to layouts where a small horizontal indicator fits naturally into the surrounding interface.
Frequently Asked Questions
Does the Bouncing Dots Loader use JavaScript?
No. The component contains no JavaScript. Its complete bouncing effect is created with CSS transforms, animation timing, and staggered delays.
How do the dots move upward and downward?
The animation changes each dot from translateY(0) to translateY(-22px). The alternate animation direction then reverses the movement automatically on the next cycle.
Why do the three dots bounce at different times?
The second and third dots use delays of .15s and .3s. These delays offset their animation cycles and create the staggered bouncing sequence.
Can I make the dots bounce higher?
Yes. Increase the negative value in translateY(-22px). For example, a larger negative translation will move each dot farther upward from the baseline.
The Bouncing Dots Loader keeps its implementation compact by combining three circular elements, one shared keyframe animation, and staggered delays to create a clear CSS-only loading state.
