sibling-index() and sibling-count(): CSS finally knows which one it is

Firefox 154 brings these two functions to Baseline. They return the current element's position among its siblings and the total sibling count, so staggered animation, ordered colours, and even sizing can be computed in CSS instead of an inline variable per element.

Firefox 154 shipped support for sibling-index() and sibling-count(), which moves both functions to Baseline Newly available.

They do something small:

  • sibling-index() returns the current element’s position among its siblings, starting at 1
  • sibling-count() returns the total number of sibling DOM elements in the parent container, including the element itself

Those two numbers used to be JavaScript-only.

How it used to be done

For “each card fades in one after another”, the standard recipe was:

<li style="--i: 0">…</li>
<li style="--i: 1">…</li>
<li style="--i: 2">…</li>
li {
  animation-delay: calc(var(--i) * 60ms);
}

So every added element needs a hand-written --i, and missing one shifts the whole sequence. Templating languages need a loop variable; static sites have to keep the built output in sync with the source.

Now the inline style goes away:

li {
  animation-delay: calc((sibling-index() - 1) * 60ms);
}

Mind the 1-based part — if you want the first element to have no delay, subtract one. Forget it and the first item waits 60ms, which reads as a small stutter.

Three places this genuinely helps

Staggered animation

As above. Adding an element no longer touches the stylesheet, and the order follows the DOM by construction.

Ordered colours

Spreading hues evenly around the wheel:

.swatch {
  background: hsl(calc(sibling-index() * 360deg / sibling-count()) 70% 55%);
}

Change the number of elements and the palette recomputes. No palette variables to maintain.

Dividing available space

sibling-count() removes “I do not know how many there are” as an obstacle:

.bar {
  width: calc(100% / sibling-count());
}

Flex and grid are usually the better tool — this one earns its keep where flex is not an option: absolutely positioned children, or cases needing an explicit width.

How this differs from existing selectors

CSS already had :nth-child(), :first-child, :last-child. The distinction:

  • :nth-child(3) is a selector — it decides which rule matches which element, and only then can you style it
  • sibling-index() is a value — it returns a number that can go into calc(), into hsl(), into animation-delay

The former says “the third one turns red”. The latter says “the Nth one’s delay is N × 60ms”. The latter used to require JavaScript or an inline variable.

Support

Browser Version
Chrome / Edge 138
Firefox 154
Safari 26.2

All three have landed, hence Baseline Newly available — meaning “usable, but older browsers degrade”. Since these functions only affect appearance (delay, colour, width), degradation produces “everything appears at once / same colour / equal width” rather than a broken layout or an error. That makes them safe to adopt as progressive enhancement.

Where I land

These will not save you many lines. What they remove is a class of coupling that has always been maintained by convention:

“Which element this is” is an intrinsic property of the DOM, yet it had to be injected from outside by a build tool or a template variable. Injection means two places know the same fact, and two places drift — adding an element and forgetting to update --i is the textbook symptom.

Now the number is read straight from the DOM. One source of truth. Changes that turn an external convention into an internal query tend to be worth more for the bugs they delete than for the lines they save.

← Back to all posts

Comments

…