Experimental: Check browser support before using this in production.
The sibling-count() CSS function returns the total number of sibling elements an element has, including itself. Think of it sort of as the CSS equivalent of JavaScript’s element.parentElement.children.length.
ul li {
width: calc(100% / sibling-count());
}
<sibling-count()> = sibling-count()
The sibling-count() function takes no arguments and returns an <integer>.
A common use case for sibling-index() is creating staggered animations. Now, if you combine it with sibling-count(), you can reverse the direction of that stagger — making the last item animate first, and the first item animate last.
Here’s how that looks in practice:
.item {
transition-duration: calc((sibling-count() - sibling-index() + 1) * 200ms);
}
Instead of delaying each item, I can control how long its transition lasts. That way, all items start at the same time, but earlier ones take longer to finish. So, if you have four items, the first one runs 800ms and the last one runs for 200ms.
At the time, this only works behind the Experimental Web Platform features flag in Chromium-based browsers. Here is a video so you can see how it turns out:
In our previous example, the bars that slide in with a reverse stagger also show another cool use of sibling-count() — setting size based on how many elements there are.
Instead of giving each bar a fixed height, like 25%, we used this:
.bar {
height: calc(100% / sibling-count());
}
That means no matter how many bars we add or remove, they’ll always split the space evenly. This trick works great for flex layouts — whether it’s height or width, you can use sibling-count() to set the sizing dynamically.
Let’s say you’re rendering a bunch of tags. With sibling-count(), you can automatically adjust the font size based on how many tags there are. That means: more tags equals smaller text, and fewer tags equals bigger text.
.tag {
font-size: calc(3rem - (sibling-count() * 0.1rem));
}
Try clicking the tags to remove them — watch the font size scale up as the count goes down.
sibling-count() works on the DOM tree, not the flat treeThe specification states:
These functions, to match selectors like
:nth-child(), operate on the DOM tree, rather than the flat tree like most CSS values do. They may, in the future, have variants that support counting flat tree siblings.
But what does that actually mean? Let’s look at an example to break it down.
<fancy-section>
<div>Slotted 1</div>
<div>Slotted 2</div>
<div>Slotted 3</div>
</fancy-section>
<script>
class FancySection extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' }).innerHTML = `
<section>
<slot></slot>
<div>Internal</div>
</section>
<style>
div {
width: calc(100% / sibling-count());
}
</style>
`;
}
}
customElements.define('fancy-section', FancySection);
</script>
At a glance, it looks like we have four <div>s visible in the page:
<div> inside the shadow DOMSo you might expect the sibling-count() to be 4. But it’s actually 2, and here’s why. When the browser renders a component, it creates two trees:
The sibling-count() function works on the DOM tree, which means it doesn’t see slotted content as siblings of internal elements. It only considers elements that are siblings in the same tree scope.
So, inside the <section> element, the DOM structure is:
<section>
<slot></slot> <!-- First child -->
<div>Internal</div> <!-- Second child -->
</section>
And that’s why the sibling-count() function returns a value of 2.
width: calc(100% / sibling-count()); /* 100% / 2 = 50% */
Even though visually we have four siblings, those are not real siblings in the DOM tree.
of selectorThe specification also states:
These functions may, in the future, be extended to accept an
of <complex-real-selector-list>argument, similar to:nth-child(), to filter on a subset of the children.
That means you might be able to do something like this in the future:
li {
... sibling-count(of .on-sale)
}
In this example, only list items with the .on-sale class are counted when calculating the siblings, regardless of where they are in the full list.
<ul>
<li>Regular item</li>
<li class="on-sale">Sale item 1</li>
<li>Regular item</li>
<li class="on-sale">Sale item 2</li>
<li class="on-sale">Sale item 3</li>
</ul>
The sibling-count() function is part of CSS Values and Units Module Level 5, which is currently in Working Draft status at the time of writing. That means a lot can change between now and when the feature becomes a formal Candidate Recommendation for implementation.
You can check Firefox’s work in Bugzilla Ticket #1953973.
ul li { transform: translateX(calc(sibling-index() * 10px)); }
.element:first-child { }
p:first-of-type { text-indent: 1rem; }
.element:nth-child(3n - 1) { }
li:nth-last-child(2) { }
.element:nth-last-of-type(2n + 1){ }
.element:nth-of-type(2n + 1) { }
.element:last-child { }
p:last-of-type { font-size: 0.75em; }