25 CSS Spinners 15 / 25

Bouncing Elastic Dots Spinner

Three coloured dots bounce with spring-like squash and stretch physics — compressing horizontally and elongating vertically at peak height — using a multi-step cubic-bezier keyframe.

Pure CSS MIT licensed
Live Demo Open in tab

This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.

Open in playground

The code

<div class="sp-15">
  <div class="sp-15__row">
    <div class="sp-15__dot"></div>
    <div class="sp-15__dot"></div>
    <div class="sp-15__dot"></div>
  </div>
</div>
.sp-15,.sp-15 *,.sp-15 *::before,.sp-15 *::after{box-sizing:border-box;margin:0;padding:0}
.sp-15{
  --bg:#0e0b1e;
  --c1:#ff6e6c;
  --c2:#cbacf9;
  --c3:#67e8f9;
  display:flex;
  align-items:center;
  justify-content:center;
  min-height:100vh;
  background:var(--bg);
}
.sp-15__row{
  display:flex;
  align-items:flex-end;
  gap:10px;
  height:70px;
}
.sp-15__dot{
  width:18px;
  height:18px;
  border-radius:50%;
  animation:sp-15-bounce 0.9s cubic-bezier(0.36,0.07,0.19,0.97) infinite;
}
.sp-15__dot:nth-child(1){
  background:var(--c1);
  box-shadow:0 0 10px var(--c1);
  animation-delay:0s;
}
.sp-15__dot:nth-child(2){
  background:var(--c2);
  box-shadow:0 0 10px var(--c2);
  animation-delay:0.15s;
}
.sp-15__dot:nth-child(3){
  background:var(--c3);
  box-shadow:0 0 10px var(--c3);
  animation-delay:0.3s;
}
@keyframes sp-15-bounce{
  0%,100%{transform:translateY(0) scaleX(1) scaleY(1)}
  30%{transform:translateY(-40px) scaleX(0.9) scaleY(1.1)}
  50%{transform:translateY(-48px) scaleX(0.85) scaleY(1.15)}
  70%{transform:translateY(-40px) scaleX(0.9) scaleY(1.1)}
  85%{transform:translateY(-4px) scaleX(1.1) scaleY(0.9)}
}
@media (prefers-reduced-motion: reduce){
  .sp-15__dot{animation:none}
}

How this works

Each dot uses a five-step sp-15-bounce keyframe that combines translateY (vertical movement) with scaleX and scaleY for squash-and-stretch: at launch the dot compresses slightly wide, narrows as it rises, reaches peak height with a tall-and-thin shape, then re-compresses on landing. The timing function cubic-bezier(0.36,0.07,0.19,0.97) applies a sharp ease-in (slow start → fast up) and ease-out (fast → slow at peak) that mimics gravity.

Three dots are staggered by 0s, 0.15s, and 0.3s delays, each in a distinct colour. The dots sit in a flex row with align-items:flex-end so the stationary baseline and the vertical bounce are both clearly readable.

Customize

  • Edit --c1, --c2, --c3 to change dot colours — a monochrome white-to-grey gradient suits minimal dark UIs.
  • Increase bounce height from -48px to -70px for a more dramatic arc.
  • Reduce the squash/stretch values (e.g. scaleX(0.95) instead of 0.85) for a subtler, less cartoonish bounce.
  • Add a fourth dot at animation-delay:0.45s for a four-dot rolling bounce pattern.
  • Change dot size from 18px to 10px for a compact three-dot ellipsis-style loading indicator.

Watch out for

  • scaleX and scaleY at the same keyframe step will compound with any parent transform — if dots are inside a scaled container, the squash values will be skewed.
  • The cubic-bezier timing function applies to the entire keyframe interpolation, not individual steps — large bounces with a single easing can feel mechanically off if the peak and floor transitions mismatch; split into separate up/down animations for precise control.
  • align-items:flex-end grounds the dots to the bottom of the flex container — if the container height is less than the bounce height (70px), dots will clip; ensure container height is at least max-translate + dot-size.

Browser support

ChromeSafariFirefoxEdge
60+ 12+ 60+ 60+

Compositor-based transform animation; no modern-only features required.

Search CodeFronts

Loading…