Animation Intermediate

Staggered animations without nth-child hacks

Staggered list animations used to require manually setting --index on each :nth-child or counting in JavaScript. The sibling-index() function gives every element automatic awareness of its position.

Old 12+ lines
1/* Manual index per nth-child */
2li:nth-child(1) { --i: 0; }
3li:nth-child(2) { --i: 1; }
4li:nth-child(3) { --i: 2; }
5li:nth-child(4) { --i: 3; }
6/* … repeat for every item … */
7
8li {
9  transition-delay: calc(0.1s * var(--i));
10}
Modern
5 lines
1li {
2  transition: opacity .25s ease, translate .25s ease;
3  transition-delay:
4    calc(0.1s * (sibling-index() - 1));
5}
click to replay the staggered animation
1
2
3
4
5
6
7
8

Automatic indexing

sibling-index() returns each element's 1-based position. No manual counting, no matter how many items.

Dynamic safe

Add or remove items and the stagger adapts automatically. The old nth-child approach breaks when the list changes.

Count siblings too

sibling-count() gives the total. Use both for radial layouts, equal distribution, and more.

Browser Support
72%
Chrome Edge Firefox 🔜 Safari 🔜
View on caniuse.com →
Lines Saved
12+ → 5
Scales to any list size
Old Approach
nth-child per item
Manual --index variables
Modern Approach
sibling-index()
Tree counting functions

How it works

The classic staggered animation technique required setting a custom property like --i on every :nth-child selector, then using calc(0.1s * var(--i)) for the delay. This was fragile: add a list item and you need another rule. Some developers set the index via inline styles or JavaScript instead, adding complexity.

The sibling-index() function returns a 1-based integer representing the element's position among its siblings. Use it directly in calc() for staggered delays, radial positioning, or any layout that depends on element order. Its companion sibling-count() returns the total number of siblings, enabling formulas like distributing items evenly around a circle.

ESC