DEV Community

Cover image for Stagger animations with SCSS
Jenning Ho
Jenning Ho

Posted on • Edited on

Stagger animations with SCSS

Overview

Staggered animations consists of a series of sequential or overlapping animations, similar to a domino effect. Staggering animations is an effective way to add flare to animating a group of items. In the context of web animation, it can be done with the help of a JS animation plugin like GSAP, or simply with CSS, depending on the requirements.

Intro

In this post I will show how we can do it with SCSS. SCSS enable us to create staggered animations with its programming like features, namely variable, for loop, and calculations.

What staggered animations is, is a series of animations that are played in sequence with a tiny delay between each elements. We will need to calculate the delay needed for each elements and apply this delay to the transition-delay or the animation-delay property. The delay needs to be less than of the duration of the animation, so that the next animation will start before the current animation ended. Ideally the delay should be between 30% to 70% of the duration for the best effect.

Scenario

Given the scenario where there are 4 elements next to each other, and we want to fade in each elements with a duration of 300ms and a delay of 100ms between each other. Here are the HTML and CSS for the effect:

<ul class="list">
  <li class="list-item">A</li>
  <li class="list-item">B</li>
  <li class="list-item">C</li>
  <li class="list-item">D</li>
</ul>
Enter fullscreen mode Exit fullscreen mode
.list-item {
  opacity: 0;
  animation: fade-in 300ms ease forwards;
}

.list-item:nth-child(2) {
  animation-delay: 100ms;
}

.list-item:nth-child(3) {
  animation-delay: 200ms;
}

.list-item:nth-child(4) {
  animation-delay: 300ms;
}

@keyframes fade-in {
  to {
    opacity: 1;
  }
}
Enter fullscreen mode Exit fullscreen mode

2 key points from the above code:

  1. opacity: 0 to hide the elements before they animate.
  2. forwards as the animation-fill-mode value so that the end frame of the animation persists. In this scenario, fade-in will bring the element's opacity to 1 and persist. Without this, element will disappear as soon as the animation ends.

SCSS

With SCSS we can achieve the same CSS output with the following code:

$n: 4;

.list-item {
  opacity: 0;
  animation: fade-in 300ms ease forwards;

  @for $x from 2 through $n {
    &:nth-child(#{$x}) {
      animation-delay: 100ms * ($x - 1);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. We do a @for loop that starts from 2, and ends at 4.
  2. Print / interpolate variable $x with #{} to append it into nth-child().
  3. Calculate the delay by multiplying 100ms with ($x - 1).

Do note that the number of loops is dependant on the number of HTML elements to be animated. This does make the SCSS solution to be inflexible if the number of HTML elements are dynamic. So depending on your needs, SCSS might not be enough. In cases where you can safely determine the number of HTML elements, this little snippet can prove to be a valuable tool to staggering animations. Areas where I have applied this technique are such as navigation menu and cover text animations.

Formula

The formula to calculating the delay can be the key to manipulating the staggered timing to get the ideal effect. Here are a few that I find commonly applicable:

  • Reverse stagger
@for $x from 1 through ($n - 1) {
  &:nth-child(#{$x}) {
    animation-delay: ($n - $x) * 100ms;
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Ease-in stagger
@for $x from 2 through $n {
  &:nth-child(#{$x}) {
    animation-delay: 100ms * ($x - 1) / $x * $n;
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Ease-out stagger
@for $x from 2 through $n {
  &:nth-child(#{$x}) {
    animation-delay: 100ms * ($x - 1) / $n * $x;
  }
}
Enter fullscreen mode Exit fullscreen mode

Codepen

Here's a pen to show what's discussed in this post:

Application

Here are some of my pens where this technique is applied:

  1. CATS pets store
  2. Navigation menu
  3. Interactive progress bar

Happy animating!

Top comments (1)

Collapse
 
hedgehogform profile image
Alexander

Could you show how to make it loop? Like it plays 1 by 1 and repeats when the last has animated it restarts? Like staggered but it loops when the last one finishes. Is it possible only using scss? Without js.