Skip to main content
Featured Article

Mastering sibling-index(): Staggered Animations Without JavaScript ๐Ÿš€

Complete guide to CSS sibling-index() and sibling-count() functions for dynamic staggered animations, dynamic layouts, and zero-JS UI effects that scale automatically.

  • 2 MIN
  • Pankaj Kumar
Updated: coding

Share

  • Whatsapp Icon
  • Twitter Icon
  • Telegram Icon
  • Linkedin Icon
  • Facebook Icon
Mastering sibling-index(): Staggered Animations Without JavaScript ๐Ÿš€
coding 2 min read

Complete guide to CSS sibling-index() and sibling-count() functions for dynamic staggered animations, dynamic layouts, and zero-JS UI effects that scale automatically.

CSS sibling-index(): Staggered Animations Without JavaScript ๐Ÿš€

  • The new sibling-index() and sibling-count() CSS functions return an elementโ€™s position (1-based) and total sibling count as integers, enabling dynamic calculations without JavaScript or manual :nth-child() selectors โš™๏ธ.
  • These functions work with calc(), @starting-style, and container queries to create production-ready staggered animations that automatically recalculate when DOM elements change ๐Ÿ’ก.
  • No more forEach loops, CSS custom properties on each item, or animation librariesโ€”pure CSS handles it all with native 120fps performance ๐ŸŒ.

๐ŸŽฏ How sibling-index() Works

/* Returns 1, 2, 3, 4, 5 for 5 siblings */
transition-delay: calc((sibling-index() - 1) * 0.08s);

/* Zero-based: 0s, 0.08s, 0.16s, 0.24s, 0.32s */

Key Insight: Subtract 1 from sibling-index() for zero-based timing. First item gets 0s delay, second gets your base delay, etc.

๐Ÿ—๏ธ Core Implementation Pattern

1. Final State (aligned, visible)

.list-item {
  opacity: 1;
  transform: translateY(0) rotate(0deg);
  transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
  /* Magic happens here */
  transition-delay: calc((sibling-index() - 1) * 0.08s);
}

2. Initial State (@starting-style)

@starting-style {
  .list-item {
    /* Dynamic initial rotation per item */
    --rotate-deg: calc((sibling-index() - 1) * 2deg);
    opacity: 0;
    transform: rotate(var(--rotate-deg)) translateY(50px);
  }
}

๐Ÿ“ฑ Complete Production Example

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>sibling-index() Staggered Animations</title>
  <style>
    * {
      box-sizing: border-box;
    }

    body {
      font-family: -apple-system, BlinkMacSystemFont, sans-serif;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      min-height: 100vh;
      margin: 0;
      padding: 2rem;
      display: grid;
      place-items: center;
    }

    .demo-container {
      max-width: 600px;
      width: 100%;
    }

    h1 {
      color: white;
      text-align: center;
      font-size: clamp(1.8rem, 5vw, 3rem);
      margin-bottom: 3rem;
      line-height: 1.2;
    }

    /* MAIN STAGGER ANIMATION */
    .cards {
      list-style: none;
      padding: 0;
      margin: 0;
      display: grid;
      gap: 1.5rem;
    }

    .card {
      background: rgba(255, 255, 255, 0.95);
      backdrop-filter: blur(20px);
      border-radius: 24px;
      padding: 2.5rem;
      box-shadow: 
        0 25px 50px -12px rgba(0, 0, 0, 0.25),
        0 0 0 1px rgba(255, 255, 255, 0.1);
      
      /* FINAL STATE */
      opacity: 1;
      transform: translateY(0) scale(1) rotate(0deg);
      
      /* STAGGER TRANSITION */
      transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
      transition-delay: calc((sibling-index() - 1) * 0.1s);
    }

    .card-content {
      font-size: 1.25rem;
      font-weight: 600;
      color: #1f2937;
      text-align: center;
    }

    /* INITIAL STATE - Triggers automatic transition */
    @starting-style {
      .card {
        opacity: 0;
        transform: translateY(60px) scale(0.9);
        /* Dynamic rotation based on position */
        rotate: calc((sibling-index() - 1) * 1.5deg * var(--direction, 1));
      }
    }

    /* DYNAMIC BACKGROUND BY POSITION */
    .card {
      background: hsl(
        200deg, 
        calc(60% + (sibling-index() - 1) * 8%), 
        calc(95% - (sibling-index() - 1) * 2%)
      );
    }

    /* INTERACTION EFFECTS */
    .card:hover {
      transform: translateY(-12px) scale(1.05);
      transition-delay: 0s !important;
      box-shadow: 
        0 35px 70px -12px rgba(0, 0, 0, 0.4),
        0 0 0 1px rgba(255, 255, 255, 0.2);
    }

    /* CONTROL BUTTONS */
    .controls {
      display: flex;
      gap: 1rem;
      justify-content: center;
      margin: 2rem 0;
    }

    .btn {
      padding: 12px 24px;
      border: none;
      border-radius: 12px;
      background: rgba(255, 255, 255, 0.2);
      color: white;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.2s ease;
      backdrop-filter: blur(10px);
    }

    .btn:hover {
      background: rgba(255, 255, 255, 0.3);
      transform: translateY(-2px);
    }

    /* RESPONSIVE STAGGER SPEED */
    @container (max-width: 500px) {
      .card {
        transition-delay: calc((sibling-index() - 1) * 0.06s);
        padding: 2rem;
      }
    }
  </style>
</head>
<body>
  <div class="demo-container">
    <h1>CSS <code>sibling-index()</code><br><small>Staggered Animations -  Zero JS โœจ</small></h1>
    
    <ul class="cards" id="cards">
      <li class="card">
        <div class="card-content">๐ŸŽจ Creative Design</div>
      </li>
      <li class="card">
        <div class="card-content">โšก Lightning Fast</div>
      </li>
      <li class="card">
        <div class="card-content">๐Ÿš€ Modern CSS</div>
      </li>
      <li class="card">
        <div class="card-content">๐Ÿ“ฑ Fully Responsive</div>
      </li>
      <li class="card">
        <div class="card-content">๐Ÿ”ฎ Future-Proof</div>
      </li>
      <li class="card">
        <div class="card-content">โœจ Production Ready</div>
      </li>
    </ul>

    <div class="controls">
      <button class="btn" onclick="addCard()">โž• Add Card</button>
      <button class="btn" onclick="removeCard()">โž– Remove Card</button>
      <button class="btn" onclick="reverseDirection()">๐Ÿ”„ Reverse</button>
    </div>
  </div>

  <script>
    let reverseDir = false;
    
    function addCard() {
      const cards = document.getElementById('cards');
      const newCard = document.createElement('li');
      newCard.className = 'card';
      newCard.innerHTML = `<div class="card-content">โœจ New Item #${cards.children.length + 1}</div>`;
      cards.appendChild(newCard);
    }
    
    function removeCard() {
      const cards = document.getElementById('cards');
      if (cards.children.length > 1) {
        cards.removeChild(cards.lastElementChild);
      }
    }
    
    function reverseDirection() {
      reverseDir = !reverseDir;
      document.documentElement.style.setProperty('--direction', reverseDir ? '-1' : '1');
    }
  </script>
</body>
</html>

๐Ÿ”ง Advanced Patterns & Techniques

1. Center-Out Staggering

.card {
  --center-distance: min(
    (sibling-index() - 3), 
    (sibling-count() - sibling-index())
  );
  transition-delay: calc(var(--center-distance) * 0.08s);
}

2. Dynamic Color Gradients

.card {
  background: hsl(
    calc(200deg + (sibling-index() - 1) * 20deg),
    70%,
    calc(90% - (sibling-index() - 1) * 3%)
  );
}

3. Proportional Spacing

.card {
  margin-left: calc((sibling-index() - 1) / sibling-count() * 100%);
}

๐Ÿ›ก๏ธ Production Fallbacks

/* Feature detection + graceful degradation */
@supports not (transition-delay: calc((sibling-index() - 1) * 0.1s)) {
  .card:nth-child(1) { transition-delay: 0s; }
  .card:nth-child(2) { transition-delay: 0.1s; }
  .card:nth-child(3) { transition-delay: 0.2s; }
  .card:nth-child(4) { transition-delay: 0.3s; }
  /* Limit to expected max items */
}

/* Universal fallback */
@supports not (sibling-index: 1) {
  .card {
    transition-delay: 0.1s; /* All animate together */
  }
}

โš–๏ธ Before vs After Comparison

TechniqueJS + CSS Varsnth-childsibling-index()
Code50+ lines JS10+ CSS rules3 lines CSS
Bundle5KB JS0KB0KB
DynamicManual recalcStaticAutomatic
Performance60fpsNativeNative 120fps
MaintainComplexRepetitiveScalable

๐ŸŽฏ Browser Support (April 2026)

BrowserVersionStatus
Chrome/Edge120+โœ… Full
SafariTP 26+โœ… Full
FirefoxNightly๐ŸŸก Partial
Global~82%๐Ÿ“ˆ Growing

๐Ÿ”ฅ Production Checklist

โœ… Use calc((sibling-index() - 1) * delay) for zero-based timing
โœ… Pair with @starting-style for automatic transitions  
โœ… Add @supports fallbacks for older browsers
โœ… Test dynamic add/remove (staggers recalculate automatically)
โœ… Use container queries for responsive timing
โœ… Combine with sibling-count() for proportional effects

๐Ÿš€ Real-World Use Cases

  1. Pricing Cards - Staggered entrance from center-out
  2. Feature Lists - Left-to-right cascade
  3. Testimonials - typewriter-style entrance
  4. Product Grids - Masonry with dynamic spacing
  5. Navigation Menus - Mobile menu slide-in

sibling-index() is the end of JavaScript stagger loops. Add, remove, reorder itemsโ€”animations automatically recalculate with zero runtime cost. Pure CSS, production-ready, infinitely scalable โœจ.

Explore Related Topics

Stay Updated with Our Latest Articles

Subscribe to our newsletter and get exclusive content, tips, and insights delivered directly to your inbox.

We respect your privacy. Unsubscribe at any time.

About the Author

pankaj kumar - Author

pankaj kumar

Blogger

pankaj.syal1@gmail.com