Back

Creating a Sophisticated Pulsing Concentric Circles Loader

Learn how to build an elegant, rhythmic loading animation with expanding rings using pure CSS animations and no JavaScript.

HTML

The HTML structure for this loader is remarkably simple. We need a container with the class "loader" that houses four empty div elements. Each div will become an animated circle in our final design.
        <div class="loader">
          <div></div>
          <div></div>
          <div></div>
          <div></div>
        </div>
      

CSS

First, let's establish the container that will house our animation. By setting its position to relative, we create a positioning context for the absolutely positioned circles within it. The dimensions of 10rem × 10rem provide adequate space for our animation to unfold without overwhelming the interface.
        .loader {
            position: relative;
            width: 10rem;
            height: 10rem;
        }
      
Now we'll style the animated elements—each div inside our loader container. These will become our pulsing circles. By using absolute positioning, we can control exactly where each circle appears and expands from. The 10px border with a light green color creates hollow circles, while border-radius: 50% ensures perfect circular shapes. We're also applying our animation named "load" that will run for 2 seconds with an ease-out timing function, creating a natural-feeling expansion that slows as it reaches its maximum size.
        .loader div {
            position: absolute;
            border: 10px solid rgb(151, 197, 12);
            border-radius: 50%;
            animation: load 2s ease-out infinite;
        }
      
The heart of our animation is the @keyframes rule. This carefully orchestrated sequence creates the pulsing effect: - At 0%, our circles are invisible (opacity: 0) and have no dimensions, positioned at the center of the container - We maintain this state until 4.9% of the animation duration - At exactly 5%, we make the circles visible (opacity: 1) but still with no dimensions - From there until 100%, we gradually expand the circles outward to 8rem in both width and height, while fading them back to invisible This creates the effect of circles emanating from the center point and fading as they expand outward.
        @keyframes load {
            0% {
                top: 4rem;
                left: 4rem;
                width: 0;
                height: 0;
                opacity: 0;
            }
            4.9% {
                top: 4rem;
                left: 4rem;
                width: 0;
                height: 0;
                opacity: 0;
            }
            5% {
                top: 4rem;
                left: 4rem;
                width: 0;
                height: 0;
                opacity: 1;
            }
            100% {
                top: 0px;
                left: 0px;
                width: 8rem;
                height: 8rem;
                opacity: 0;
            }
        }
      
To create a rhythmic, cascading effect rather than having all circles pulse simultaneously, we stagger the animation start times for each circle using negative animation delays. We target each div individually using the nth-child selector and offset their animation start points by 0.5 seconds each. With our 2-second animation duration, this creates a continuous, fluid motion where a new circle begins to pulse every half second.
        .loader div:nth-child(2) {
            animation-delay: -0.5s;
        }
        .loader div:nth-child(3) {
            animation-delay: -1s;
        }
        .loader div:nth-child(4) {
            animation-delay: -1.5s;
        }
      
The beauty of this loader lies in its simplicity and efficiency. Using only CSS and minimal HTML, we've created a sophisticated animation that communicates an active loading state to users while maintaining visual interest. The staggered timing creates a perpetual motion that doesn't have a discernible start or end point, making it perfect for indeterminate loading scenarios.

Whole code

<div class="loader">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<style>
.loader {
  position: relative;
  width: 10rem;
  height: 10rem;
}
.loader div {
  position: absolute;
  border: 10px solid rgb(151, 197, 12);
  border-radius: 50%;
  animation: load 2s ease-out infinite;
}
.loader div:nth-child(2) {
  animation-delay: -0.5s;
}
.loader div:nth-child(3) {
  animation-delay: -1s;
}
.loader div:nth-child(4) {
  animation-delay: -1.5s;
}
@keyframes load {
  0% {
    top: 4rem;
    left: 4rem;
    width: 0;
    height: 0;
    opacity: 0;
  }
  4.9% {
    top: 4rem;
    left: 4rem;
    width: 0;
    height: 0;
    opacity: 0;
  }
  5% {
    top: 4rem;
    left: 4rem;
    width: 0;
    height: 0;
    opacity: 1;
  }
  100% {
    top: 0px;
    left: 0px;
    width: 8rem;
    height: 8rem;
    opacity: 0;
  }
}

</style>
      
Thank you for reading this article.

Comments

No comments yet. Be the first to comment!

More loaders

Similar

See also