Staggered animations

2019/09/03

ui, design, css, javascript

Today, let’s talk about integrating motion into our websites and user interfaces.

Your odds of winning an Academy Award are about 10 times better than your odds of experiencing an earthquake.

people in motion on a sidewalk

It’s pretty obious that movement happening in perfect sync seems unnatural to us. Unless you’re going for the 1970’s kung fu movie vibe, you probably want to stagger and tweak your animations to act like they’re bound by the laws of physics.

Our brains like smooth, sequential motion that overlaps, speeds up, and slows down in a way that feels familiar to the real world. In animation and motion design, a quick way to acheive this effect is through staggered or overlapping movement.

Simultaneous

Staggered

Despite being almost in sync, the slight variation between each element in the second example makes their movement much more interesting and appealing.

Hopefully this illustrates why we’d want to use staggered motion in our designs, but what’s the best way to code it? There are a few options at our disposal, so we’ll look at the pros and cons of each.

  • Pure CSS
  • SCSS for loop
  • Javascript forEach method

The Pure CSS Approach

.stagger-child-transitions>:nth-child(2) { transition-delay: .1s }
.stagger-child-transitions>:nth-child(3) { transition-delay: .2s }
.stagger-child-transitions>:nth-child(4) { transition-delay: .3s }
.stagger-child-transitions>:nth-child(5) { transition-delay: .4s }
/* etc.... */

If you’ll always have a managable number of children, this approach is probably the most straightforward. Just put your stagger-child-transitions class on the parent element, and animate the child elements however you like!

Another benefit to the pure CSS approach is that it’s easy to override the transition delay if necessary. Type selectors like :nth-child() are low-specificity, which means those styles can be overriden with a class or ID at the same level:

  • Using a class: .stagger-child-transitions .child { transition-delay: 0s; }
  • Or an ID: #special { transition-delay: 0s; }

Using the SCSS for loop

The styles we wrote above can be cleaned up (sort of) with SCSS. Since SCSS is a a pre-processed language that compiles into regular CSS, it makes writing CSS quite a bit easier for us, but doesn’t affect the experience of the user, for better or worse.

@for $i from 1 through 100 {
    .stagger-child-transitions>:nth-child(#{$i}) {
        transition-delay: ($i * .1)s;
    }
}

That looks nice and clean! The only drawback of this approach is that our compiled CSS will start to bloat from the 100 extra lines with 100 extra rules (~10kb). 10kb isn’t much in literal file size (considering a jpg can be easily 300kb+), but it does weigh in at around 15-20% of a standard CSS file. CSS isn’t render-blocking like Javascript, but still has to be parsed and rendered by the browser. If you’re anything like me, those extra bytes will start to bug you.

Using the Javascript forEach() method

100 items was probably overkill, but we might want a method with less limitations in case we increase our element count later. This is where a couple lines of Javascript could be a good alternative to the SCSS approach.

document.querySelectorAll('.stagger-child-transitions').forEach(element => {
    Array.from(element.children).map((child, index) => {
        child.style.transitionDelay = (index * .1) + 's';
    })
})

This solves our code bloating problem, and won’t noticably affect the performance of our page. The main advantage to using Javascript is that it’s not limited to a finite number of elements, and it allows us to do more complex things, like this.

However, adding styles with Javascript does have drawbacks. Unlike CSS, adding styles with Javascript immediately maxes out our specificity ‘headroom’. Because the styles are applied directly to the element, the only way we can change them is to write more Javascript. Another drawback to this approach is that elements added to the DOM after our script runs will not be affected. We have to call the function again to catch those.

Depending on your application, SCSS or Javascript might be better suited for staggering your UI animations. No matter what you choose, adding staggered motion and choreography will almost always make your application feel more friendly and interesting. :)

If you want to read more about this, check out the Material Design spec on animation customization.

Thanks for reading. If you learned something useful, share this article with your followers.

Want to dive deeper where design and code meet?

Join my email list and become a dev-signer!