25 CSS Spinners15 / 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.
Published
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
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><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;
}
}.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;
}
}Here's a working CSS Spinner from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Bouncing Elastic Dots Spinner
Source: https://codefronts.com/motion/css-spinner-designs/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.
## HTML
```html
<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>
```
## CSS
```css
.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;
}
}
```Here's a working CSS Spinner from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Bouncing Elastic Dots Spinner
Source: https://codefronts.com/motion/css-spinner-designs/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.
## HTML
```html
<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>
```
## CSS
```css
.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.
Make it yours
- Edit
--c1,--c2,--c3to change dot colours — a monochrome white-to-grey gradient suits minimal dark UIs. - Increase bounce height from
-48pxto-70pxfor a more dramatic arc. - Reduce the squash/stretch values (e.g.
scaleX(0.95)instead of0.85) for a subtler, less cartoonish bounce. - Add a fourth dot at
animation-delay:0.45sfor a four-dot rolling bounce pattern. - Change dot size from
18pxto10pxfor a compact three-dot ellipsis-style loading indicator.
Gotchas — read before shipping
scaleXandscaleYat 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-beziertiming 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-endgrounds 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 leastmax-translate + dot-size.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 60+ | 12+ | 60+ | 60+ |
Compositor-based transform animation; no modern-only features required.