AI CSS Animations

Liquid Droplet Spinner

Four droplets circle a faint track at different speeds and melt into each other as they pass, then pinch apart again. An inline SVG goo filter in objectBoundingBox units makes the liquid necks.

Also known as: gooey spinner, metaball spinner, liquid loader.

HTML

<div class="ldr-gooey-spinner" role="status" aria-label="Loading">
  <svg width="0" height="0" aria-hidden="true">
    <filter id="ldr-gooey-spinner-goo" primitiveUnits="objectBoundingBox">
      <feGaussianBlur stdDeviation="0.05" />
      <feColorMatrix values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 20 -9" />
    </filter>
  </svg>
  <div class="ldr-gooey-spinner-drops"><span></span><span></span><span></span><span></span></div>
</div>

CSS

.ldr-gooey-spinner {
  --s: var(--ldr-size, 48px);
  --c: var(--ldr-color, #3b82f6);
  position: relative;
  width: var(--s);
  height: var(--s);
}
.ldr-gooey-spinner::before {
  content: "";
  position: absolute;
  inset: 16%;
  border: calc(var(--s) / 24) solid color-mix(in srgb, var(--ldr-color, #3b82f6) 18%, transparent);
  border-radius: 50%;
}
.ldr-gooey-spinner > svg {
  position: absolute;
}
.ldr-gooey-spinner-drops {
  position: absolute;
  inset: 0;
  filter: url(#ldr-gooey-spinner-goo);
}
.ldr-gooey-spinner-drops span {
  position: absolute;
  inset: 0;
  animation: ldr-gooey-spinner-spin var(--t) cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite;
}
.ldr-gooey-spinner-drops span::before {
  content: "";
  position: absolute;
  top: 16%;
  left: 50%;
  width: var(--d);
  height: var(--d);
  margin: calc(var(--d) / -2) 0 0 calc(var(--d) / -2);
  border-radius: 50%;
  background: var(--c);
}
.ldr-gooey-spinner-drops span:nth-child(1) { --d: calc(var(--s) * 0.26); --t: 1.5s; }
.ldr-gooey-spinner-drops span:nth-child(2) { --d: calc(var(--s) * 0.2); --t: 2s; }
.ldr-gooey-spinner-drops span:nth-child(3) { --d: calc(var(--s) * 0.22); --t: 2.5s; }
.ldr-gooey-spinner-drops span:nth-child(4) { --d: calc(var(--s) * 0.17); --t: 3s; }
@keyframes ldr-gooey-spinner-spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .ldr-gooey-spinner, .ldr-gooey-spinner *, .ldr-gooey-spinner::before, .ldr-gooey-spinner::after, .ldr-gooey-spinner *::before, .ldr-gooey-spinner *::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