AI CSS Animations

Tumbling Dots Loader

Two dots walk by vaulting over each other in turn, each one pivoting on its partner's center, while the row drifts back so the pair stays put. Two rotate keyframes and a linear drift.

Also known as: dots flipping over each other, rolling dots loader, walking dots animation.

HTML

<div class="ldr-tumbling-dots" role="status" aria-label="Loading">
  <span></span><span></span>
</div>

CSS

.ldr-tumbling-dots {
  --s: var(--ldr-size, 48px);
  --c: var(--ldr-color, #3b82f6);
  --d: calc(var(--s) / 3.4);
  --g: calc(var(--d) * 1.08);
  position: relative;
  width: calc(var(--s) * 1.4);
  height: calc(var(--g) + var(--d));
  animation: ldr-tumbling-dots-drift 1.6s linear infinite;
}
.ldr-tumbling-dots span {
  position: absolute;
  bottom: 0;
  left: calc(50% - var(--g) * 0.8 - var(--d) / 2);
  width: var(--d);
  height: var(--d);
  border-radius: 50%;
  background: var(--c);
  transform-origin: calc(50% + var(--g)) 50%;
  animation: ldr-tumbling-dots-first 1.6s infinite;
}
.ldr-tumbling-dots span:last-child {
  left: calc(50% + var(--g) * 0.2 - var(--d) / 2);
  animation-name: ldr-tumbling-dots-second;
}
@keyframes ldr-tumbling-dots-first {
  0%        { transform: rotate(0deg); animation-timing-function: cubic-bezier(0.55, 0, 0.3, 1); }
  38%, 100% { transform: rotate(180deg); }
}
@keyframes ldr-tumbling-dots-second {
  0%, 50%   { transform: rotate(0deg); animation-timing-function: cubic-bezier(0.55, 0, 0.3, 1); }
  88%, 100% { transform: rotate(180deg); }
}
@keyframes ldr-tumbling-dots-drift {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(var(--g) * -2)); }
}

@media (prefers-reduced-motion: reduce) {
  .ldr-tumbling-dots, .ldr-tumbling-dots *, .ldr-tumbling-dots::before, .ldr-tumbling-dots::after, .ldr-tumbling-dots *::before, .ldr-tumbling-dots *::after {
    animation-duration: 3s;
  }
}

Customizing this loader

  • Color: set --ldr-color on the element or a parent. Currently previewing #3b82f6.
  • Size: set --ldr-size. Every dimension in the snippet is derived from it. Currently previewing 64px.
  • Speed: change the duration on the animation shorthand. Delays on the children are relative, so they keep their rhythm.
  • Accessibility: the root carries role="status" and an aria-label so screen readers announce the wait. The reduced-motion rule at the end slows the animation for users who asked for less motion.

Similar loaders