For decades, web developers have grappled with a fundamental irony of the Document Object Model (DOM): the browser knows exactly where an element sits in the tree, yet CSS—the language responsible for styling that tree—was functionally blind to it. That era of "willful ignorance" is coming to an end. With the introduction of sibling-index() and sibling-count(), CSS is gaining the ability to perform native tree-counting, effectively eliminating the need for complex Sass loops, fragile JavaScript workarounds, and hundreds of lines of redundant code.
This development marks a significant milestone in the evolution of "Intelligent CSS," where the stylesheet can adapt dynamically to the quantity and position of content without external intervention.
Main Facts: The Logic of sibling-index() and sibling-count()
The core of this update lies in two functions defined within the CSS Values and Units Module Level 5 spec. These functions allow developers to access an element’s position and its environment’s total volume directly within a CSS declaration.
What are the functions?
sibling-index(): This function returns an<integer>representing the position of the current element among its siblings. It is 1-indexed, meaning the first child returns1, the second returns2, and so on.sibling-count(): This function returns an<integer>representing the total number of sibling elements within the same parent container.
Why this is a Breakthrough
Until now, if a developer wanted to create a staggered animation—where each card in a grid fades in 100ms after the one before it—they had two unpleasant choices. They could write a Sass loop to generate dozens of :nth-child() rules:
li:nth-child(1) --idx: 1;
li:nth-child(2) --idx: 2;
/* ...and so on for every possible item... */
Alternatively, they could use JavaScript to iterate through the DOM and inject inline styles (e.g., style="--index: 3"). Both methods are flawed. The CSS approach is static and bloats the stylesheet, while the JavaScript approach couples layout logic with scripting, often leading to "layout thrashing" or maintenance headaches when components are refactored.
The new native functions collapse these complex workflows into a single, performant line of CSS:
li
animation-delay: calc(sibling-index() * 100ms);
This works whether there are five items or five thousand, and it updates automatically if items are added or removed.
Chronology: From W3C Proposal to Browser Implementation
The journey of tree-counting functions from a theoretical "wish list" item to a shipping feature spans several years of technical debate within the World Wide Web Consortium (W3C).

- Initial Friction: For years, developers relied on the "Quantity Queries" hack—a complex series of
:nth-last-childselectors—to style elements based on the total count of their siblings. While clever, it was difficult to read and maintain. - W3C Issue #4559: The formal proposal for
sibling-index()andsibling-count()gained momentum in the CSS Working Group (CSSWG) under issue #4559. Developers argued that the browser already possessed this data during the cascade phase; withholding it from CSS was an unnecessary limitation. - The "O(√N)" Era: Before native support, engineers like Roman Komarov developed highly sophisticated strategies to "count" in CSS using binary-like selector logic. These strategies could cover 1,023 elements with "only" 63 rules—a brilliant but exhausting workaround that highlighted the desperate need for a native solution.
- Approval and Specification: The functions were eventually integrated into the CSS Values and Units Module Level 5 draft. Unlike
counter(), which returns a string and is limited to thecontentproperty of pseudo-elements,sibling-index()andsibling-count()were designed to resolve as true integers, making them compatible withcalc(). - 2025 Ship Dates: In June 2025, Chrome and Edge 138 released stable support for these functions. Safari 26.2 followed shortly after, bringing native tree-counting to a majority of modern browser users.
Supporting Data: Practical Patterns and Mathematical Layouts
The power of these functions becomes evident when they are combined with other modern CSS features, such as trigonometric functions (sin(), cos()) and advanced math functions (mod(), round()).
1. Proportional Widths and Grids
In a world of fluid design, sibling-count() allows for automatic distribution of space. A tab bar can now calculate the width of its children without needing Flexbox’s flex-grow or manual percentages:
.tab
width: calc(100% / sibling-count());
If a user adds a fourth tab to a three-tab menu, the widths transition from 33.3% to 25% instantly, driven entirely by the DOM structure.
2. Algorithmic Color Theory
By using sibling-index() and sibling-count() inside an hsl() color function, developers can create perfectly distributed color palettes.
.swatch
background-color: hsl(
calc((360 / sibling-count()) * sibling-index()),
70%, 50%
);
This ensures that whether there are three items or twenty, they will always be spaced evenly around the color wheel, creating a harmonious gradient effect that adapts to the content.
3. Radial and Circular Layouts
Placing items in a circle used to require a JavaScript library or complex manual coordinate calculation. Now, it can be handled with a single CSS variable:
.radial-item
--angle: calc((360deg / sibling-count()) * sibling-index());
left: calc(50% + var(--radius) * cos(var(--angle)));
top: calc(50% + var(--radius) * sin(var(--angle)));
Official Responses and Browser Support Landscape
The reception from the browser engine teams has been overwhelmingly positive, though implementation timelines vary.
- Google (Chromium): With the release of Chrome 138, Google signaled that tree-counting functions are a priority for modern web performance. By moving these calculations to the browser’s C++ engine during the style recalculation phase, Chromium reduces the "Time to Interactive" (TTI) compared to JavaScript-based index injection.
- Apple (WebKit): Safari’s early adoption (v26.2) reflects Apple’s commitment to CSS-first solutions for animations, particularly for the high-end UI effects common in macOS and iOS-style web apps.
- Mozilla (Firefox): While Firefox has not yet shipped the feature in its stable branch, Mozilla’s official standards position is "positive." Development is actively tracked under Bugzilla issue #1953973.
- The "Baseline" Status: Currently, the feature is not yet "Baseline" (a status indicating support across all major engines). Developers are encouraged to use
@supportsfor progressive enhancement:@supports (z-index: sibling-index()) /* Advanced mathematical layouts here */
Implications: Performance, Accessibility, and the "Gotchas"
While these functions are transformative, they introduce new considerations for performance and user experience that developers must navigate.

The "Hidden Element" Paradox
One of the most significant technical nuances is that sibling-index() reads the DOM tree, not the layout tree. If an element is set to display: none, it is removed from the visual layout, but it still occupies a slot in the DOM.
- Example: In a list of three items where the second is hidden via
display: none, the third item will still return asibling-index()of3.
This can create "gaps" in staggered animations or color distributions if developers use CSS to filter lists. For continuous numbering, developers must physically remove filtered nodes from the DOM.
Performance at Scale
Browser engines handle style recalculations efficiently, but sibling-index() is not "free." In a container with 10,000 children, inserting a new element at the very beginning forces the browser to recalculate the index for all 9,999 subsequent siblings. For standard UI components like navigation bars or card grids, this is negligible. However, for "infinite scroll" feeds or massive data tables, JavaScript virtualization remains the more performant choice.
Accessibility and the Visual-Semantic Gap
The W3C and accessibility advocates have raised a crucial warning: visual order is not semantic order.
If a developer uses sibling-index() to mathematically reorder elements (for example, using the order property or grid placement), the screen reader will still read the items in their original DOM order. Furthermore, ARIA attributes like aria-posinset (the item’s position in a set) and aria-setsize (the total count) do not automatically sync with these CSS functions. Developers must ensure that any visual reordering is reflected in the underlying HTML to avoid confusing users of assistive technology.
Shadow DOM Scoping
To maintain the security and encapsulation of Web Components, sibling-index() cannot "see" across Shadow DOM boundaries. If a stylesheet in the "Light DOM" tries to access the index of an element inside a component’s "Shadow DOM" via ::part(), the browser will return 0. This prevents external styles from probing the internal structure of third-party widgets.
The Future: of <selector> and Beyond
The current implementation is just the beginning. The CSSWG is already discussing an extension to these functions that would mirror the functionality of :nth-child().
The proposed sibling-index(of .active) syntax would allow developers to count only siblings that match a specific class. This would solve the "hidden element" problem mentioned earlier; if a developer filters a list by adding a .hidden class, they could use sibling-index(of :not(.hidden)) to maintain a perfectly sequential count of only the visible items.
Furthermore, proposals for children-count() (allowing a parent to know how many children it has) and descendant-count() (a recursive count of all nested items) are on the horizon.
Conclusion
The arrival of sibling-index() and sibling-count() represents a shift in the philosophy of web development. By giving CSS access to the browser’s inherent knowledge of the DOM tree, we are moving away from "duct-tape" solutions and toward a more declarative, efficient, and intelligent web. The "fundamentally stupid" feeling of writing ten :nth-child rules is finally being replaced by the elegance of a single, mathematical line of code.

