Zooming Concentric (Striped) Circles | KIRUPA

One of the cool things you can do with radial gradients in CSS is create concentric circles, often referred to as striped circles:

This is a companion discussion topic for the original entry at https://www.kirupa.com/animations/animating_circular_concentric_stripes.htm

Why’d you need to use JS for this? I realize I’m questioning you without providing code to the contrary, but… :evil:

I couldn’t figure out a way to do this using only CSS. With that said, maybe I didn’t try hard enough either! I can imagine it may be possible to have one keyframe suddenly change the CSS variable colors and the gradient stop back to its original value! After I typed this out, I’m actually certain it is possible haha. I’ll see what I can do about it.

Here is my first attempt:

<!DOCTYPE html>

  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
  <title>Zooming Radial Stripes</title>
    body {
      padding: 25px;

    :root {
      --color1: #F2545B;
      --color2: #F3F7F0;
      --tempColor: #FFF;
      --offset: 0px;

    #outerContainer {
      display: grid;
      grid-template-columns: 200px 200px;
      grid-gap: 40px;

    .container {
      width: 300px;
      height: 300px;
      background-color: #3891A6;
      transform: translateZ(0);
      border-radius: 50%;
      border: #A93F55 solid 10px;

    .radialStripes {
      background-image: radial-gradient(circle,
        var(--color1) 0px,
        var(--color1) calc(var(--offset) + 0px),
        var(--color2) calc(var(--offset) + 0px),
        var(--color2) calc(var(--offset) + 20px),
        var(--color1) calc(var(--offset) + 20px),
        var(--color1) calc(var(--offset) + 40px),
        var(--color2) calc(var(--offset) + 40px),
        var(--color2) calc(var(--offset) + 60px),
        var(--color1) calc(var(--offset) + 60px),
        var(--color1) calc(var(--offset) + 80px),
        var(--color2) calc(var(--offset) + 80px),
        var(--color2) calc(var(--offset) + 100px),
        var(--color1) calc(var(--offset) + 100px),
        var(--color1) calc(var(--offset) + 120px),
        var(--color2) calc(var(--offset) + 120px),
        var(--color2) calc(var(--offset) + 140px),
        var(--color1) calc(var(--offset) + 140px),
        var(--color1) calc(var(--offset) + 160px),
        var(--color2) calc(var(--offset) + 160px),
        var(--color2) calc(var(--offset) + 180px),
        var(--color1) calc(var(--offset) + 180px),
        var(--color1) calc(var(--offset) + 200px),
        var(--color2) calc(var(--offset) + 200px),
        var(--color2) calc(var(--offset) + 220px)
      animation: zoomStripes 2s infinite;

    @keyframes zoomStripes {
      0% {
        --offset: 0px;
      99% {
        --offset: 20px;
      100% {
        --offset: 0px;
        --tempColor: var(--color1);
        --color1: var(--color2);
        --color2: var(--tempColor);


  <div id="outerContainer">
    <div class="container radialStripes"></div>



Changing the stripes isn’t smooth for, I’m guessing, custom CSS properties/variables aren’t animatable. I would need to create many more intermediate keyframes to have the value for --offset gradually change from 0px to 20px.

As for the colors, I tried to create a way to swap them. That doesn’t work. I wonder if it is simply not supported to have one CSS variable set to the value of another CSS variable. That doesn’t seem right, so I’m making a mistake somewhere.

My general idea was to have all of your initial rings start out as rings-as-circles that overlap each other, then have each one animate out to the edge. At the end of the animation you just reset everything.