Stagger animations with SCSS
Tags: animation, css, scss, tutorial
Published: 2020-04-11 11:36:13

### 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:

```html
<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>
```

```css
.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;
  }
}
```

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:

```scss
$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);
    }
  }
}
```

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
```scss
@for $x from 1 through ($n - 1) {
  &:nth-child(#{$x}) {
    animation-delay: ($n - $x) * 100ms;
  }
}
```

* Ease-in stagger
```scss
@for $x from 2 through $n {
  &:nth-child(#{$x}) {
    animation-delay: 100ms * ($x - 1) / $x * $n;
  }
}
```

* Ease-out stagger
```scss
@for $x from 2 through $n {
  &:nth-child(#{$x}) {
    animation-delay: 100ms * ($x - 1) / $n * $x;
  }
}
```


### Codepen

Here's a pen to show what's discussed in this post:
{% codepen https://codepen.io/jenning/pen/bGVNvqM default-tab=css,result %}


### Application

Here are some of my pens where this technique is applied:
1. [CATS pets store](https://codepen.io/jenning/full/WNQNwVg)
2. [Navigation menu](https://codepen.io/jenning/full/ExarqGq)
3. [Interactive progress bar](https://codepen.io/jenning/full/bQvqMm)

Happy animating!