DEV Community

Cover image for OhSnap! Staggered CSS Animations + SASS Loops
Gedalya Krycer
Gedalya Krycer

Posted on • Updated on

OhSnap! Staggered CSS Animations + SASS Loops

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

View Demo In Full Screen ↗️

View Code SandBox ↗️


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

// Row.js Component
const Row = (props) => {
  return (
    <div
      style={{ "--delay": props.delay }}
      className={β€’β€’β€’}
    >
      β€’β€’β€’
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

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

// 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> 
  );
};
Enter fullscreen mode Exit fullscreen mode

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()

.row--enter {
  animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.320, 1.275) calc(0.15s * var(--delay)) both;
}
Enter fullscreen mode Exit fullscreen mode

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

{[...Array(6)].map((_, i) => (
  <div key={i} className={`bg__circle--${i + 1}`}></div>
))}
Enter fullscreen mode Exit fullscreen mode

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

$circles: ('1': 261, '2': 387, '3': 513, '4': 640, '5': 767, '6': 893);
Enter fullscreen mode Exit fullscreen mode

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

@each $name, $size in $circles {
    &__circle--#{$name} {
      max-height: $size + px;
      max-width: $size + px;
      β€’β€’β€’
    }
  }
Enter fullscreen mode Exit fullscreen mode

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


Thumbnail designed with Figma

Top comments (0)