Search code examples
csscss-animationslinear-gradients

CSS linear gradient progress animation


I've put together an animation which indicates a countdown until a toast notification disappears:

.toastDiv {
  animation: toastProgress 3s ease;
  border: 1px solid rgba(0, 0, 0, .5);
  border-radius: 5px;
  box-shadow: 0 0 5px 0 rgba(0, 0, 0, .25);
  margin: 0 0 1ex 0;
  padding: 1ex 1em;
}

@keyframes toastProgress {
  0% {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 0%, white 0%, white 100%);
  }
  10% {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 10%, white 10%, white 100%);
  }
  20% {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 20%, white 20%, white 100%);
  }
  30% {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 30%, white 30%, white 100%);
  }
  40% {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 40%, white 40%, white 100%);
  }
  50% {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 50%, white 50%, white 100%);
  }
  60% {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 60%, white 60%, white 100%);
  }
  70% {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 70%, white 70%, white 100%);
  }
  80% {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 80%, white 80%, white 100%);
  }
  90% {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 90%, white 90%, white 100%);
  }
  100% {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 100%, white 100%, white 100%);
  }
}
<div class="toastDiv">hello</div>

However, it is very tedious to have to spell out the individual animation stages and at the granularity I chose, I am getting choppy results.

I tried using this:

@keyframes toastProgress {
  from {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 0%, white 0%, white 100%);
  }
  to {
    background: linear-gradient(to right, aliceblue 0%, aliceblue 100%, white 100%, white 100%);
  }
}

But this transitions from one solid background to the next instead of animating the color stops from left to right.

Is there a way to make this progress-style gradient animation using only from and to and not percent-steps?


Solution

  • You can rely on background-size animation and steps() like below:

    .toastDiv {
      border: 1px solid rgba(0, 0, 0, .5);
      border-radius: 5px;
      box-shadow: 0 0 5px 0 rgba(0, 0, 0, .25);
      margin: 0 0 1ex 0;
      padding: 1ex 1em;
      background:
        linear-gradient(aliceblue 0 0) left no-repeat,
        white;
      animation: toastProgress 5s steps(10,jump-none); 
    }
    
    @keyframes toastProgress {
      0% {
        background-size:0% 100%;
      }
      100% {
        background-size:100% 100%;
      }
    }
    <div class="toastDiv">hello</div>
    
    <div class="toastDiv" style="animation-timing-function:ease">without Steps</div>

    Related to understand how steps() works: https://stackoverflow.com/a/51843473/8620333