Frontend interview: How would you use CSS sibling-index() and sibling-count()?
Prompt and scope
A responsive card list is added to and removed from by a CMS. Each card should compute its width from the sibling count and delay animation by its position, while preserving a real accessible position. Explain the semantics, boundaries, fallback, and verification for sibling-index() and sibling-count().
What the interviewer is testing
- Whether you know both functions return integers, with a 1-based index, over the flat tree.
- Whether you separate visual CSS order from DOM and assistive-technology order.
- Whether you account for Limited availability,
@supports, static fallback, and performance. - Whether you cover Shadow DOM, pseudo-elements, hidden nodes, and dynamic lists.
Clarifying questions to ask
- Is the goal visual width, animation timing, or a position users must hear? Their fallbacks differ.
- Do hidden, template, or virtualized nodes exist, and are they direct flat-tree children?
- What browser support range and JavaScript fallback policy apply?
- Are Shadow DOM, slots, or CSS counters involved?
30-second answer framework
sibling-count() returns the total direct-child count and sibling-index() returns the element's 1-based position; the specification uses the flat tree. They can drive calc(), grid widths, or animation delays, but they are not a reliable source of accessible text. Ship a fixed CSS fallback first, then enhance with @supports; use semantic HTML or a tested counter for positions. Test insertion, removal, slots, hidden nodes, unsupported browsers, and prefers-reduced-motion.
Step-by-step deep dive
1. Confirm the returned values
Both functions return integers. sibling-count() includes the element itself, and sibling-index() starts at 1. They count direct children of the parent in the flat tree; arbitrary descendants and visual order are different concepts.
2. Use them for size and animation
Cards can divide available width by the sibling count, and animation delay can increase by position:
.card {
--count: sibling-count();
--index: sibling-index();
flex-basis: calc(100% / var(--count));
animation-delay: calc((var(--index) - 1) * 60ms);
}This is a visual calculation, not a replacement for DOM order or a business identifier. Define bounds for empty lists, one item, and very large counts to avoid division or excessive delay.
3. Handle support and fallback
MDN marks these functions as Limited availability, so production CSS cannot assume support. Provide a fixed or flex layout, then enhance with @supports (width: calc(100% / sibling-count())). JavaScript may update custom properties when a real product requirement needs dynamic values, but first paint and content must work without it.
4. Flat tree, slots, and hidden nodes
The specification defines sibling relationships over the flat tree; Shadow DOM slot assignment therefore changes what must be tested. Verify whether display: none, template nodes, and virtual-list placeholders are direct children using the actual DOM. Do not infer the function's index from visual order.
5. Accessibility and semantics
Visual width and delay do not create an audible position. Use ol and li, or explicit aria-posinset and aria-setsize, and keep screen-reader order aligned with the DOM. Decorative CSS must not make content unintelligible when animation or the new functions are disabled.
6. Dynamic updates and performance
Sibling insertion and removal changes computed values and can trigger style recalculation. Measure style, layout, and animation cost for thousands of nodes instead of trusting a small demo. A virtualized list limits DOM size, but the functions then see only the window, not the total dataset.
Model high-quality answer
I would treat sibling-index() and sibling-count() as visual-calculation features: the former is a 1-based flat-tree position and the latter is the direct-child count. They can enhance card width and animation delay, while semantic position comes from ol, li, or ARIA. Ship a fixed layout fallback and gate enhancement with @supports. Test real DOM behavior for Shadow DOM, slots, hidden nodes, and virtualization. Measure style recalculation and layout for dynamic and large lists; keep content usable without support and honor prefers-reduced-motion.
Common mistakes
- Assume universal support → legacy browsers lose styles → ship a fixed layout and
@supportsfallback. - Infer index from visual
order→ CSS order differs from flat-tree order → test DOM and slot assignment. - Use CSS calculation as an accessible position → screen readers get no position → retain HTML semantics or ARIA.
- Treat a virtual window as the full count → functions see only current DOM → provide total count from data.
- Let animation delay grow without bounds → wait time and recalculation become costly → cap it and disable for reduced motion.
Follow-up questions and responses
Does sibling-count() include descendants?
No. It counts the parent's direct children and includes the current element; descendants do not participate.
How should a slotted element be tested?
Inspect the Shadow DOM light tree, the flat tree after slot assignment, and the accessibility tree. Browser-level verification is more reliable than source order alone.
Can JavaScript be the only fallback for unsupported browsers?
It can be progressive enhancement, but base layout and content must work without script. A script that updates custom properties also needs insertion, SSR, no-script, and reduced-motion handling.