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>
);
};
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>
);
};
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;
}
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>
))}
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);
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;
β’β’β’
}
}
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)