AI CSS Animations

Rolling Ball Border

A small ball rolls around the outside of a card, hugging every rounded corner without slipping. Generated keyframes keep its speed even, and its radius fits a whole number of turns per lap.

Also known as: rolling border, rolling border animation css, ball rolling around border.

Rolling out v2.4 12 of 31 regions

HTML

<div class="bdr-rolling-ball-border">
  <span class="bdr-rolling-ball-border-ball" aria-hidden="true"><span class="bdr-rolling-ball-border-spin"></span></span>
  <span class="bdr-rolling-ball-border-title">Rolling out v2.4</span>
  <span class="bdr-rolling-ball-border-text">12 of 31 regions</span>
</div>

CSS

.bdr-rolling-ball-border {
  --c: var(--bdr-color, #3b82f6);
  font-size: var(--bdr-size, 16px);
  position: relative;
  display: grid;
  place-items: center;
  width: 15em;
  height: 8.5em;
  border-radius: 1em;
  font-family: system-ui, sans-serif;
  font-weight: 600;
  --t: 8s;
  place-content: center;
  gap: 0.25em;
  background: color-mix(in srgb, var(--c) 6%, transparent);
  box-shadow: 0 0 0 0.1em color-mix(in srgb, var(--c) 28%, transparent);
}
.bdr-rolling-ball-border-ball {
  position: absolute;
  top: 0;
  left: 0;
  width: calc(0.609em * 2);
  height: calc(0.609em * 2);
  margin: -0.609em 0 0 -0.609em;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, color-mix(in srgb, var(--c) 30%, #fff), var(--c) 55%, color-mix(in srgb, var(--c) 70%, #000));
  box-shadow: 0 0.12em 0.3em -0.05em color-mix(in srgb, var(--c) 60%, #000);
  animation: bdr-rolling-ball-border-lap var(--t) linear infinite;
}
.bdr-rolling-ball-border-spin {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 18%, #fff 0 0.09em, transparent 0.1em),
    radial-gradient(circle at 50% 82%, color-mix(in srgb, #fff 55%, transparent) 0 0.07em, transparent 0.08em);
  animation: bdr-rolling-ball-border-spin var(--t) linear infinite;
}
.bdr-rolling-ball-border-title { font-size: 1em; }
.bdr-rolling-ball-border-text {
  font-size: 0.75em;
  font-weight: 450;
  opacity: 0.6;
}
@keyframes bdr-rolling-ball-border-lap {
  0% { transform: translate(1em, -0.709em); }
  26.153% { transform: translate(14em, -0.709em); }
  27.051% { transform: translate(14.442em, -0.651em); }
  27.948% { transform: translate(14.854em, -0.48em); }
  28.846% { transform: translate(15.208em, -0.208em); }
  29.743% { transform: translate(15.48em, 0.146em); }
  30.641% { transform: translate(15.651em, 0.558em); }
  31.538% { transform: translate(15.709em, 1em); }
  44.615% { transform: translate(15.709em, 7.5em); }
  45.513% { transform: translate(15.651em, 7.942em); }
  46.41% { transform: translate(15.48em, 8.354em); }
  47.308% { transform: translate(15.208em, 8.708em); }
  48.205% { transform: translate(14.854em, 8.98em); }
  49.103% { transform: translate(14.442em, 9.151em); }
  50% { transform: translate(14em, 9.209em); }
  76.153% { transform: translate(1em, 9.209em); }
  77.051% { transform: translate(0.558em, 9.151em); }
  77.948% { transform: translate(0.146em, 8.98em); }
  78.846% { transform: translate(-0.208em, 8.708em); }
  79.743% { transform: translate(-0.48em, 8.354em); }
  80.641% { transform: translate(-0.651em, 7.942em); }
  81.538% { transform: translate(-0.709em, 7.5em); }
  94.615% { transform: translate(-0.709em, 1em); }
  95.513% { transform: translate(-0.651em, 0.558em); }
  96.41% { transform: translate(-0.48em, 0.146em); }
  97.308% { transform: translate(-0.208em, -0.208em); }
  98.205% { transform: translate(0.146em, -0.48em); }
  99.103% { transform: translate(0.558em, -0.651em); }
  100% { transform: translate(1em, -0.709em); }
}
/* A whole number of turns per lap, so the spin lines up at the wrap. */
@keyframes bdr-rolling-ball-border-spin {
  to { transform: rotate(4680deg); }
}
@media (prefers-reduced-motion: reduce) {
  .bdr-rolling-ball-border-ball,
  .bdr-rolling-ball-border-spin { animation-play-state: paused; }
}

@media (prefers-reduced-motion: reduce) {
  .bdr-rolling-ball-border, .bdr-rolling-ball-border *, .bdr-rolling-ball-border::before, .bdr-rolling-ball-border::after, .bdr-rolling-ball-border *::before, .bdr-rolling-ball-border *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Customizing this snippet

  • Color: set --bdr-color on the element or a parent. Currently previewing #3b82f6.
  • Base size: set --bdr-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 16px.
  • Speed: change the duration on the transition or animation shorthand. Staggered delays are relative, so they keep their rhythm.
  • Reduced motion: the rule at the end of the CSS turns the motion off for people who asked their OS for less of it. Keep it.

Similar border animations