OhSnap! Staggered CSS Animations + SASS Loops
Tags: css, sass, react, tutorial
Published: 2021-03-01 09:30:36

*The "OhSnap!" series explores bite-sized tips that you can apply today.*

---

A while back I came across this helpful technique to stagger animations with CSS variables. (Original sources included at the bottom.)

While building the demo I also got a chance to play with SASS loops...and I am in love. 😍 So today we are going to cover both staggering animations and why SASS loops are a huge time saver. 🎉 

There is some math involved, but I promise that it isn't overwhelming. I hate math, so if I can get it, then you can definitely do it! 💪

---

##Full Demo

{% codesandbox kind-poincare-ch3nt %}


###[View Demo In Full Screen ↗️](https://ch3nt.csb.app/)
###[View Code SandBox ↗️](https://codesandbox.io/s/kind-poincare-ch3nt?file=/src/styles.scss)


---

#Breakdowns...

##1. Staggering CSS Animations

####What we will do:
In this demo, we have a list of contact rows that each animate in or out in a staggered manner. 

Our goal is to dynamically change the `delay` value on the CSS animation so that the first element animates slightly before the second and so forth. 

We will do this by assigning an increasing numeral value to each HTML/JSX element in a CSS variable.

This allows us to pass that number to the animation and control how long the delay should be with some `calc()` magic. 

####Steps:

**1 — Add CSS Variables to HTML/JSX**

```jsx
// Row.js Component
const Row = (props) => {
  return (
    <div
      style={{ "--delay": props.delay }}
      className={•••}
    >
      •••
    </div>
  );
};
```

Add a CSS variable called `--delay` to the parent HTML/JSX element for the contact row. We can do this by placing it in a `style` attribute and assigning it a number value via props. 

*(Note how the variable starts with two dashes.)*


**2 — Pass the map's index via props**

```jsx
// App.js Component
export default function App() {
  •••
  return (
      <section>
        •••
        {data.map((d, index) => {
          return (
            <Row
              key={index * 2}
              text={d.text}
              delay={index}
              animate={animate}
            />
          );
        })}
      </section> 
  );
};
```

In our scenario, the contact row is its own component that just provides structure and styling. We then map through it in the `App.js` component and pass down props for all the values.

We use the `index` argument from `map()` and pass it down as a prop to the Row's CSS variable. (Both the prop and the variable are named "delay"). 

This makes the `props.delay` value 0 for the first row, 1 for the 2nd row, and so forth. 


**3 — Apply the CSS Variable to the animation with `calc()`**
```SASS
.row--enter {
  animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.320, 1.275) calc(0.15s * var(--delay)) both;
}
```

The important part to focus on is `calc(0.15s * var(--delay))` which sits in the "delay" section of the animation. 

Using `calc()` we are multiplying 0.15 seconds by the number in the `--delay` CSS variable. If the number were 2, then the rendered delay value would be 0.30 seconds. 

Because we are mapping through the elements, we are firing this animation each time and passing it the index of the number. 

For 6 elements this is how the delays would look like...

* `0.15` * `0` = **`0 sec`**
* `0.15` * `1` = **`0.15 sec`**  
* `0.15` * `2` = **`0.30 sec`**
* `0.15` * `3` = **`0.45 sec`**
* `0.15` * `4` = **`0.60 sec`**
* `0.15` * `5` = **`0.75 sec`**

If we wanted to increase the delay amount, we just need to increase `0.15` to a higher value. :) 



---

##2. SASS Maps & Loops

####What we will do:
Using JSX we are mapping through an array to create 6 `<div>` elements. Each `<div>` has a class that makes it into a circle. 

Our goal is to use SASS variables and loops to make each circle bigger than the last one, using the same class. 

####Steps:

**1 — Create a group of HTML Elements with the same class**
```JSX
{[...Array(6)].map((_, i) => (
  <div key={i} className={`bg__circle--${i + 1}`}></div>
))}
```
Since we are in React, we can quickly create a group of divs using a `map()`. We can use a spread on the `Array()` method to create an array with 6 "spots", which will allow us to create 6 divs.

Using the `index` we can create unique BEM class names by adding the index via template literals. (Adding `+ 1` starts the first class name at 1.) 



**2 —  Create a SASS Map with property key/value pairs**
```SASS
$circles: ('1': 261, '2': 387, '3': 513, '4': 640, '5': 767, '6': 893);
```

Using a SASS map, we can store key/value pairs in a variable. These will be used as reference points and property values in the loop. 

The `$name` will be the first number in each pair and the `$size` is the second larger number.   


**3 — Store class in a SASS forEach loop**
```SASS
@each $name, $size in $circles {
    &__circle--#{$name} {
      max-height: $size + px;
      max-width: $size + px;
      •••
    }
  }
```

Here is the fun part! We can create a forEach loop using the `@each` handler. It will look for the SASS map we created above and pull the arguments `$name` and `$size` from it. 

We are then applying the `$name` to the class name and the `$size` to the width and height. 

This will output 6 `<div>` elements that have unique class names and sizing properties based on the map. 🤯




---

###Resources

* [SASS Loops](https://www.gavsblog.com/blog/for-each-while-loops-sass-scss)

* [SASS Loops on Maps](https://sass-lang.com/documentation/at-rules/control/each#with-maps)

* [Staggered Animations with CSS Custom Properties](https://cloudfour.com/thinks/staggered-animations-with-css-custom-properties/)

* [Staggered animation](https://www.30secondsofcode.org/css/s/staggered-animation)

---

*Thumbnail designed with [Figma](https://www.figma.com/)*