CSS sibling-index() Function

The CSS sibling-index() function returns an integer representing an element's position among its siblings, starting at 1.

It can be used in CSS calculations to assign different values to elements based on their order in the document. Common uses include staggered animations, progressive sizing, color variations, and automatically calculated spacing without assigning a separate class to each element.

Syntax

sibling-index()

The sibling-index() function does not accept parameters.

It returns the position of the current element among its sibling elements, using a one-based index.

For example:

width: calc(sibling-index() * 40px);

This multiplies the element's sibling position by 40px.

Other examples include:

animation-delay: calc(sibling-index() * 100ms);
transition-delay: calc(sibling-index() * 50ms);
margin-left: calc(sibling-index() * 10px);
opacity: calc(sibling-index() / 5);
font-size: calc(12px + sibling-index() * 2px);

The function returns a CSS <integer> value.

The first child element returns 1, the second returns 2, and each subsequent child returns the next integer.

Parameters

The sibling-index() function does not accept any arguments.

Feature Description
Syntax sibling-index()
Parameters None
Return type <integer>
First child Returns 1.
Second child Returns 2.
Last child Returns the total number of child elements.

The following declaration is valid:

width: calc(sibling-index() * 50px);

The following is invalid because the function does not accept an argument:

width: calc(sibling-index(2) * 50px);

The function automatically determines the index from the element's position in the DOM tree.

How It Works

The sibling-index() function determines the position of the current element among all child elements of the same parent.

Consider the following HTML:

<div class="container">
  <div class="item">One</div>
  <div class="item">Two</div>
  <div class="item">Three</div>
  <div class="item">Four</div>
</div>

Each child receives a different result.

Element sibling-index() Result
One 1
Two 2
Three 3
Four 4

Now consider this CSS:

.item {
  width: calc(sibling-index() * 40px);
}

The browser calculates the widths as follows:

  • First item: 40px
  • Second item: 80px
  • Third item: 120px
  • Fourth item: 160px

Each element receives a different width without requiring separate CSS selectors.

If the elements are reordered in the DOM, their calculated values update to reflect their new positions.

Understanding Sibling Order

The sibling-index() function uses the order of elements in the DOM tree.

Indexing Starts at 1

The first child returns 1, not 0.

<div class="group">
  <span>First</span>
  <span>Second</span>
  <span>Third</span>
</div>

The three span elements return 1, 2, and 3, respectively.

Different Element Types Are Included

<div class="group">
  <h3>Heading</h3>
  <p>Paragraph</p>
  <button>Button</button>
</div>

The heading returns 1, the paragraph returns 2, and the button returns 3.

The function does not count only elements matching the current CSS selector.

Text Nodes Are Not Counted

<div class="group">
  Text before
  <span>First</span>
  Text between
  <span>Second</span>
</div>

The first span returns 1, and the second returns 2.

Text nodes, comments, and whitespace do not affect the index.

Nested Elements Have Their Own Sibling Order

<div class="group">
  <div class="item">
    <span>Nested element</span>
  </div>
  <div class="item">Second item</div>
</div>

The two outer .item elements return 1 and 2.

The nested span returns 1 because it is the first and only child element of its own parent.

Hidden Elements Are Still Indexed

<div class="group">
  <div class="item">One</div>
  <div class="item" style="display: none;">Two</div>
  <div class="item">Three</div>
</div>

The third item still returns 3, even though the second item is hidden.

The function uses the DOM structure rather than the visible layout.

Visual Reordering Does Not Change the Index

Changing the visual order of Flexbox or Grid items does not change their sibling indexes.

.item:first-child {
  order: 3;
}

The first child remains index 1, even if the order property moves it to a different visual position.

Using sibling-index() in Calculations

The sibling-index() function can be combined with CSS math functions to calculate values from an element's position.

Multiplication

.item {
  width: calc(sibling-index() * 30px);
}

The width increases by 30px for each successive child.

Addition

.item {
  font-size: calc(12px + sibling-index() * 2px);
}

The first child receives a font size of 14px, the second 16px, and the third 18px.

Subtraction

.item {
  margin-left: calc((sibling-index() - 1) * 10px);
}

Subtracting 1 makes the first child's margin 0px.

The second child receives 10px, the third 20px, and so on.

Division

.item {
  opacity: calc(sibling-index() / 5);
}

For five children, the opacity values are 0.2, 0.4, 0.6, 0.8, and 1.

Opacity values are limited to the valid range when rendered, so larger indexes do not increase opacity beyond full visibility.

Using clamp()

.item {
  width: clamp(60px, calc(sibling-index() * 30px), 180px);
}

The width increases with the sibling index but remains between 60px and 180px.

Using min() and max()

.item {
  padding: min(24px, calc(sibling-index() * 4px));
}

This increases padding with the index but prevents it from exceeding 24px.

Math functions are useful for controlling calculated values when the number of children varies.

Staggered Animations

One of the most useful applications of sibling-index() is creating staggered animations.

Instead of assigning a different delay to every child, the delay can be calculated automatically.

For example:

.item {
  animation: fade-in 0.6s ease both;
  animation-delay: calc((sibling-index() - 1) * 150ms);
}
@keyframes fade-in {
  from {
    opacity: 0;
    transform: translateY(15px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

The calculated delays for the first five children are:

Child Animation Delay
First 0ms
Second 150ms
Third 300ms
Fourth 450ms
Fifth 600ms

The first item begins immediately, and each following item starts 150ms later.

The both animation fill mode preserves the initial hidden and translated state during the delay and the final state after the animation finishes.

Without subtracting 1, the first child would also receive a delay.

This technique is useful for navigation menus, cards, lists, and other groups of repeated elements.

Staggered Transitions

The sibling-index() function can also create staggered transitions.

For example:

.menu-item {
  opacity: 0.5;
  transform: translateY(5px);
  transition: opacity 0.3s ease, transform 0.3s ease;
  transition-delay: calc((sibling-index() - 1) * 75ms);
}
.menu:hover .menu-item,
.menu:focus-within .menu-item {
  opacity: 1;
  transform: translateY(0);
}

When the menu is hovered or receives keyboard focus, its items transition with increasing delays.

Unlike an animation, a transition occurs when a CSS property changes between states.

The function determines the delay from each element's DOM position.

For accessibility, provide a reduced-motion alternative:

@media (prefers-reduced-motion: reduce) {
  .menu-item {
    transition: none;
    transition-delay: 0ms;
  }
}

This removes the transition for users who request reduced motion.

CSS Custom Properties

The sibling-index() function can be stored in a CSS custom property for reuse in calculations.

For example:

.item {
  --index: sibling-index();
  margin-left: calc((var(--index) - 1) * 10px);
  animation-delay: calc((var(--index) - 1) * 100ms);
}

The custom property contains the index expression, which is evaluated when used in a compatible CSS calculation.

This keeps related calculations consistent.

Using a Fallback

A fallback value can be assigned before the newer function is used.

.item {
  --index: 1;
  margin-left: calc((var(--index) - 1) * 10px);
}
@supports (width: calc(sibling-index() * 1px)) {
  .item {
    --index: sibling-index();
  }
}

In supporting browsers, each item uses its actual sibling index.

In older browsers, the example uses the fallback index of 1.

For more precise fallbacks, established selectors such as :nth-child() can assign different values to individual children.

sibling-index() vs. sibling-count()

The sibling-index() and sibling-count() functions both examine the DOM structure, but they return different information.

Feature sibling-index() sibling-count()
Returns Position of the current element among its siblings. Total number of sibling elements, including the current element.
First of four children 1 4
Second of four children 2 4
Third of four children 3 4
Fourth of four children 4 4
Typical use Sequential styling and staggered effects. Calculations based on the total number of children.

The functions can be combined.

For example:

.item {
  opacity: calc(sibling-index() / sibling-count());
}

For four children, the resulting opacity values are 0.25, 0.5, 0.75, and 1.

If a fifth child is added, the values automatically adjust to 0.2, 0.4, 0.6, 0.8, and 1.

This creates a progressive effect that adapts to the total number of elements.

sibling-index() vs. :nth-child()

The sibling-index() function and the :nth-child() pseudo-class both use an element's position among its siblings, but they serve different purposes.

Feature sibling-index() :nth-child()
Type CSS function CSS pseudo-class
Purpose Returns a numeric position for calculations. Selects elements based on their position.
Return value <integer> Selector matching result
Parameters None Accepts an index or an An+B expression, optionally with an of selector list.
Typical use Calculate different values automatically. Apply styles to selected positions.

For example, using :nth-child():

.item:nth-child(1) {
  margin-left: 0;
}
.item:nth-child(2) {
  margin-left: 10px;
}
.item:nth-child(3) {
  margin-left: 20px;
}

The equivalent calculation using sibling-index():

.item {
  margin-left: calc((sibling-index() - 1) * 10px);
}

The function automatically extends the pattern to additional elements.

Unlike :nth-child(), sibling-index() does not accept a selector to filter which siblings are counted.

For styling only specific child positions, :nth-child() remains a useful and widely supported option.

Example

The following example increases the width of each item according to its sibling index.

HTML:

<div class="index-demo">
  <div class="index-item">One</div>
  <div class="index-item">Two</div>
  <div class="index-item">Three</div>
  <div class="index-item">Four</div>
</div>

CSS:

.index-demo {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 350px;
}
.index-item {
  box-sizing: border-box;
  width: 60px;
  padding: 10px;
  color: #fff;
  background-color: #3498db;
  text-align: center;
}
.index-item:nth-child(2) {
  width: 120px;
}
.index-item:nth-child(3) {
  width: 180px;
}
.index-item:nth-child(4) {
  width: 240px;
}
@supports (width: calc(sibling-index() * 60px)) {
  .index-item {
    width: calc(sibling-index() * 60px);
  }
}
Browser Result
One
Two
Three
Four

The first item is 60px wide, the second is 120px, the third is 180px, and the fourth is 240px.

In supporting browsers, the function calculates each width automatically.

The :nth-child() declarations provide a fallback for browsers without support.

Animation Example

The following example creates a staggered entrance animation using each element's sibling index.

HTML:

<div class="stagger-demo">
  <div class="stagger-item">HTML</div>
  <div class="stagger-item">CSS</div>
  <div class="stagger-item">JavaScript</div>
  <div class="stagger-item">Responsive Design</div>
</div>

CSS:

.stagger-demo {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 15px 0;
}
.stagger-item {
  padding: 12px 18px;
  color: #fff;
  background-color: #3498db;
  animation: stagger-appear 0.6s ease both;
}
.stagger-item:nth-child(2) {
  animation-delay: 150ms;
}
.stagger-item:nth-child(3) {
  animation-delay: 300ms;
}
.stagger-item:nth-child(4) {
  animation-delay: 450ms;
}
@supports (animation-delay: calc(sibling-index() * 100ms)) {
  .stagger-item {
    animation-delay: calc((sibling-index() - 1) * 150ms);
  }
}
@keyframes stagger-appear {
  from {
    opacity: 0;
    transform: translateY(15px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
@media (prefers-reduced-motion: reduce) {
  .stagger-item {
    animation: none;
  }
}
Browser Result
HTML
CSS
JavaScript
Responsive Design

The first item appears immediately, and each following item begins 150ms later.

The animation runs once when the page or example loads.

In supporting browsers, adding another child automatically extends the staggered delay pattern.

Browsers without sibling-index() support use the supplied :nth-child() delays.

Dynamic Color Example

The sibling-index() function can be combined with sibling-count() to distribute colors across a group of elements.

HTML:

<div class="index-color-demo">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
</div>

CSS:

.index-color-demo {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.index-color-demo > div {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 65px;
  height: 65px;
  color: #fff;
  background-color: #3498db;
}
@supports (background-color: hsl(calc(360deg / sibling-count() * sibling-index()) 70% 45%)) {
  .index-color-demo > div {
    background-color: hsl(calc(360deg / sibling-count() * sibling-index()) 70% 45%);
  }
}
Browser Result
1
2
3
4
5

The hue is calculated from the total number of siblings and the position of each element.

With five children, the hue changes in 72-degree increments.

Adding or removing children changes the spacing between the calculated hues.

The fallback uses a single blue background color in browsers without support for the functions.

Check color contrast when using dynamically calculated colors, especially when displaying text.

Adding and Reordering Elements

The sibling-index() function responds to changes in the DOM structure.

For example:

<div class="dynamic-group">
  <div class="item">One</div>
  <div class="item">Two</div>
  <div class="item">Three</div>
</div>

With the following CSS:

.dynamic-group > .item {
  margin-left: calc((sibling-index() - 1) * 10px);
}

The three items receive left margins of 0px, 10px, and 20px.

If a new element is inserted at the beginning of the container, the existing items move to indexes 2, 3, and 4.

Their calculated margins update automatically.

If an element is removed, the remaining indexes update to reflect the new sibling order.

The function does not change the DOM structure itself. It only calculates values based on the current structure.

Changing an element's visual order using Flexbox or Grid does not change its index because the function follows DOM order.

Common Uses

The sibling-index() function is useful when CSS values should change according to an element's position among its siblings.

  • Staggered animations: Automatically calculate increasing animation delays.
  • Staggered transitions: Apply sequential transition delays to repeated elements.
  • Progressive sizing: Increase widths, heights, or other dimensions according to sibling position.
  • Dynamic spacing: Calculate margins, padding, and offsets from an element's index.
  • Color variations: Assign different calculated colors to sibling elements.
  • Opacity effects: Gradually change opacity across a group of elements.
  • Reusable components: Avoid writing separate selectors for every child position.
  • Dynamic content: Automatically update calculated values when elements are inserted, removed, or reordered.

For example, a group of cards can use increasing animation delays:

.card {
  animation: card-enter 0.5s ease both;
  animation-delay: calc((sibling-index() - 1) * 100ms);
}
@keyframes card-enter {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

The animation sequence adapts to the number and order of cards without requiring separate delay declarations.

Use the function when numeric values should depend on sibling position. Use :nth-child() when the goal is simply to select particular child elements.

Limitations

The sibling-index() function has several requirements and behaviors to consider.

  • The function does not accept parameters.
  • It returns an integer representing an element's position among its sibling elements.
  • Indexing begins at 1, not 0.
  • The index is based on DOM order rather than visual layout order.
  • All sibling element types are included, regardless of the selector used.
  • Text nodes, comments, and whitespace are not counted.
  • Nested descendants have their own sibling indexes within their respective parents.
  • Elements hidden with display: none still affect the indexes of following siblings.
  • The function cannot filter siblings using a selector argument.
  • It returns a numeric value rather than a generated text string.
  • It does not create, remove, or reorder elements.
  • Its result must be used in a CSS value context that accepts the resulting numeric type.
  • Advanced pseudo-element and shadow DOM cases follow additional specification rules.
  • Older browsers may not support the function.

The following declaration is valid:

animation-delay: calc(sibling-index() * 100ms);

The following is invalid because the function does not accept a parameter:

animation-delay: calc(sibling-index(2) * 100ms);

The following is also invalid because the function does not accept a selector argument:

animation-delay: calc(sibling-index(.item) * 100ms);

Feature Detection

Use @supports to provide a fallback for browsers without sibling-index() support.

.item {
  animation-delay: 0ms;
}
@supports (animation-delay: calc(sibling-index() * 100ms)) {
  .item {
    animation-delay: calc((sibling-index() - 1) * 100ms);
  }
}

In supporting browsers, each item receives a calculated delay.

In older browsers, all items use the default delay.

Pseudo-Elements and Shadow DOM

For ordinary pseudo-elements such as ::before and ::after, the function generally uses the index of the originating element.

Element-backed pseudo-elements and shadow DOM references have additional rules. In some tree-scoping situations, the function resolves to 0 rather than exposing information from another tree.

For ordinary HTML layouts, the most important distinction is that the index follows the actual DOM element order.

Browser Support

The CSS sibling-index() function has Baseline Newly available browser support as of August 2026. It works across the latest major browser versions, but older browsers may not support it.

Support includes recent versions of Chrome, Edge, Firefox, and Safari.

Because the feature is relatively new, provide a fallback when supporting older browsers. Established selectors such as :nth-child() can reproduce many position-based styling patterns.

For current compatibility information, visit Can I Use? and search for sibling-index().

Specifications

The sibling-index() function is defined in the CSS Values and Units Module Level 5 specification.

The specification defines how the function calculates an element's position, its integer return value, and its behavior in different tree-scoping contexts.

Related features include sibling-count(), calc(), min(), max(), clamp(), and the :nth-child() pseudo-class.