The Staggered Animation Problem You've Definitely Encountered
You have a grid of cards. You want them to fade in one by one, each with a slightly longer delay than the last — that satisfying cascading reveal that makes an interface feel alive and polished. The design looks great in the mockup. The concept is simple enough. And yet, the moment you sit down to implement it in CSS, something feels fundamentally off about how much code it takes to pull off such a basic idea.
If you've ever found yourself writing out ten separate :nth-child() rules just to assign incrementally increasing animation delays, you already know the frustration. It works, technically, but it doesn't scale. Add an eleventh card and you're back in the stylesheet manually appending another rule. The logic is clear in your head, but CSS has never had a clean way to express it — until now.
Enter sibling-index() and sibling-count(), two new CSS functions that bring genuine mathematical awareness to the language, letting elements know exactly where they sit among their peers and how many peers they have in total.
What Are sibling-index() and sibling-count()?
These two functions are part of a broader push toward more expressive, data-driven CSS. At their core, they do something deceptively simple: they expose positional information about an element directly within CSS property values.
sibling-index() returns the position of the current element among its siblings, starting from 1. So the first child returns 1, the second returns 2, and so on. sibling-count() returns the total number of siblings an element has, giving every element in a parent container access to the same count.
Combined with CSS custom properties and the calc() function, these values become powerful building blocks. Instead of writing a separate rule for each child, you write one rule that computes the right value dynamically based on position.
Solving Staggered Animations the Right Way
The classic use case is exactly what we opened with: staggered animations. Previously, you might approach this with a block of repetitive selectors like :nth-child(1), :nth-child(2), and so on, each with a hardcoded animation-delay value. The logic is linear and predictable, but CSS had no way to express that linearity directly. You had to spell it out manually or reach for JavaScript to set inline custom properties on each element.
With sibling-index(), you can write something like animation-delay: calc(sibling-index() * 100ms) directly in your CSS rule. Every element in the container automatically gets the right delay based on its position, with no JavaScript, no inline styles, and no fragile list of nth-child overrides. The code is shorter, more readable, and scales effortlessly no matter how many items you add to the DOM.
Layout Math That Scales: More Than Just Animation
Staggered animations are the crowd-pleasing demo, but the real power of these functions goes deeper. Any time you want a visual property to progress across a set of siblings — color, opacity, size, spacing, rotation — you now have a CSS-native way to express that progression.
Consider a navigation bar where you want each item to grow slightly larger as it approaches the center. Or a set of chart bars where height should correspond to rank. Or a decorative layout where elements rotate at evenly distributed angles across a full circle. All of these require the same underlying math: knowing where you are in the sequence and how long that sequence is. sibling-index() and sibling-count() hand that data directly to your styles.
You can even combine them. Dividing sibling-index() by sibling-count() gives you a normalized value between 0 and 1, perfect for driving color stops, gradient positions, or any property that benefits from a proportional range across a group of elements.
What the DevTools Tell You
One of the quieter benefits of this approach is debuggability. When you inspect an element that uses these functions, modern browser DevTools can surface the computed values, giving you direct insight into what number the browser resolved for a given element. Compare that to debugging a chain of :nth-child() overrides or tracing a JavaScript loop that set inline styles — the clarity difference is significant.
What's Coming Next: The Full Tree-Counting Picture
The current specification counts all element siblings without filtering, but the CSS Working Group has already documented a planned extension. A proposed of <selector> argument, similar to what :nth-child() already supports, would allow something like sibling-index(of .active). This would count only siblings matching a specific selector, so an element that is the eighth child overall but the third .active child would return 3. For dynamic interfaces where items are filtered, toggled, or conditionally visible, this would keep indexes sequential and meaningful without requiring any DOM manipulation to reorder elements.
There are also active CSSWG discussions around two complementary functions: children-count() and descendant-count(). The former would tell a parent element how many direct children it contains — useful for parent-driven layout decisions — while the latter would recursively count all descendants. Both remain at the proposal stage, but they point toward a complete tree-counting vocabulary in CSS. Where sibling-index() and sibling-count() give you the horizontal view — where am I among my peers? — these new functions would give you the vertical view: what lies below me in the tree?
Browser Support and Practical Adoption
As with any emerging CSS feature, browser support is the practical consideration that governs when you can ship it in production. These functions are newer additions to the CSS Values specification, so checking current compatibility tables before adopting them in critical interfaces is always wise. That said, progressive enhancement patterns work well here. You can define a sensible fallback value and let sibling-index() override it in supporting browsers, meaning users with older clients get a functional but non-animated experience while everyone else gets the full effect.
Why This Matters for Modern CSS Architecture
The broader significance of sibling-index() and sibling-count() is what they represent philosophically for CSS as a language. For years, anything that required positional awareness beyond simple matching meant reaching for JavaScript. Inline custom properties set via script, dynamic class names, or generated stylesheets were all workarounds for the same fundamental gap: CSS could not compute based on where an element stood in its context.
These functions close that gap. They treat the DOM tree as a data source, letting your styles respond to structure in a way that feels native rather than bolted on. Combined with container queries, cascade layers, and the expanding power of calc(), they signal a CSS that is steadily becoming a fully expressive layout language — one where the feeling of doing something fundamentally stupid just to achieve a simple visual effect is becoming a thing of the past.

