skip to Main Content

I would like to apply a shimmer animation to a collection of HTML elements but have it be in unison as if it is one shimmer running through all the elements. I have seen this done with animations within Text by using background-clip: text; in the CSS and applying the animation to parent element.

Is it possible to do this kind of animation to a collection of elements in unison similar to using background-clip:text, but instead clipping to the HTML elements instead of text?

Link to fiddle

.shimmer-box {
  width: 300px;
  height: 75px;
  margin: 20px 20px;
}

.animation {
  background: linear-gradient(-45deg, #eee 40%, #fafafa 50%, #eee 60%);
  background-size: 300%;
  animation-name: shimmer;
  animation-duration: 1000ms;
  animation-timing-function: linear;
  animation-delay: 0;
  animation-iteration-count: infinite;
  animation-direction: normal;
  animation-fill-mode: none;
  animation-play-state: running;  
}

@keyframes shimmer {
  0% {
    background-position-x: 100%;
  }

  100% {
    background-position-x: 0%;
  }
}

.gray {
  background-color: #e2e2e2;
}

.flex-row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.circle {
  width: 30px;
  height: 30px;
  border-radius: 50%;
}

.short-row {
  height: 30px;
  flex: 1;
  border-radius: 8px;
}

.body-box {
  margin-top: 12px;
  width: 342px;
  height: 100px;
  border-radius: 8px;
}

.container {
  display: inline-block;
}
<h4>This animation works on a single element</h4>
<div class="shimmer-box animation"></div>

<h4>If I apply the animation to each element they are disjointed</h4>
<div class="container">
  <div class="flex-row">
    <div class="circle gray animation"></div>
    <div class="short-row gray animation"></div>
    <div class="short-row gray animation"></div>
  </div>
  <div class="body-box gray animation"></div>
</div>

<div>
<h4>I would like this animation to run in unison on all the elements here</h4>
<paragraph>Ideally it looks like one shimmer running accross all the elements in unison</paragraph>
</div>
<div class="container">
  <div class="flex-row">
    <div class="circle gray"></div>
    <div class="short-row gray"></div>
  </div>
  <div class="body-box gray"></div>
</div>

2

Answers


  1. Add the following line to the .animation ruleset:

    background-attachment: fixed;
    

    This attaches the background of all elements of that class to the viewport in such a way that they appear synchronised.

    .shimmer-box {
      width: 300px;
      height: 75px;
      margin: 20px 20px;
    }
    
    .animation {
      background: linear-gradient(-45deg, #eee 40%, #fafafa 50%, #eee 60%);
      /* add the following line: */
      background-attachment: fixed;
      background-size: 300%;
      animation-name: shimmer;
      animation-duration: 1000ms;
      animation-timing-function: linear;
      animation-delay: 0;
      animation-iteration-count: infinite;
      animation-direction: normal;
      animation-fill-mode: none;
      animation-play-state: running;
    }
    
    @keyframes shimmer {
      0% {
        background-position-x: 100%;
      }
      100% {
        background-position-x: 0%;
      }
    }
    
    .gray {
      background-color: #e2e2e2;
    }
    
    .flex-row {
      display: flex;
      align-items: center;
      gap: 12px;
    }
    
    .circle {
      width: 30px;
      height: 30px;
      border-radius: 50%;
    }
    
    .short-row {
      height: 30px;
      flex: 1;
      border-radius: 8px;
    }
    
    .body-box {
      margin-top: 12px;
      width: 342px;
      height: 100px;
      border-radius: 8px;
    }
    
    .container {
      display: inline-block;
    }
    <h4>This animation works on a single element</h4>
    <div class="shimmer-box animation"></div>
    
    <h4>If I apply the animation to each element they are disjointed</h4>
    <div class="container">
      <div class="flex-row">
        <div class="circle gray animation"></div>
        <div class="short-row gray animation"></div>
        <div class="short-row gray animation"></div>
      </div>
      <div class="body-box gray animation"></div>
    </div>
    
    <div>
      <h4>I would like this animation to run in unison on all the elements here</h4>
      <paragraph>Ideally it looks like one shimmer running accross all the elements in unison</paragraph>
    </div>
    <div class="container">
      <div class="flex-row">
        <div class="circle gray"></div>
        <div class="short-row gray"></div>
      </div>
      <div class="body-box gray"></div>
    </div>

    JS Fiddle demo.

    References:

    Login or Signup to reply.
  2. Here is an idea where you can control your animation using two classes. A wrapper class to be used on the container where all the elements should animate the same. And the animation class to be added to the element that should animate. Both can be used on the same element if only one element is concerned.

    .wrapper {
      position: relative; 
      z-index: 0;
    }
    .animation {
      mask: linear-gradient(#000 0 0); /* clip the pseudo-element to the element boundary (no, overflow: hidden won't work) */
    }
    /*
      the pseudo element is positioned relatively to the wrapper 
      making all the animations synchronized 
    */
    .animation:before {
      content:"";
      position: absolute;
      z-index: -1;
      inset: 0;
      background: 
        linear-gradient(-45deg, #eee 40%, #fafafa 50%, #eee 60%)
        right/300% 100%;
      animation: shimmer 1s linear infinite;
    }
    @keyframes shimmer {
      to { background-position: left;}
    }
    
    
    /* irrelevant code*/
    .gray {
      background-color: #e2e2e2;
    }
    .shimmer-box {
      width: 300px;
      height: 75px;
      margin: 20px 20px;
    }
    .flex-row {
      display: flex;
      align-items: center;
      gap: 12px;
    }
    .circle {
      width: 30px;
      height: 30px;
      border-radius: 50%;
    }
    .short-row {
      height: 30px;
      flex: 1;
      border-radius: 8px;
    }
    .body-box {
      margin-top: 12px;
      width: 342px;
      height: 100px;
      border-radius: 8px;
    }
    .container {
      display: inline-block;
    }
    <div class="wrapper shimmer-box animation"></div>
    <hr>
    <div class="container wrapper">
      <div class="flex-row">
        <div class="circle gray animation"></div>
        <div class="short-row gray animation"></div>
        <div class="short-row gray animation"></div>
      </div>
      <div class="body-box gray animation"></div>
    </div>
    <hr>
    <div class="container wrapper" style="margin-left: 200px">
      <div class="flex-row">
        <div class="circle gray animation"></div>
        <div class="short-row gray animation"></div>
        <div class="short-row gray animation"></div>
      </div>
      <div class="body-box gray animation"></div>
      <div class="body-box gray animation"></div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search