webdevpuneet.com

Stagger List — Free HTML CSS JS Animation Snippet

Staggered List Animation · Animations · Plain HTML, CSS & JS · Live preview

CategoryAnimations

Copy & paste

Source Code

<div class="scene">
  <div class="card" id="card">
    <div class="card-header">
      <h3>Recent activity</h3>
      <button class="replay" onclick="replay()">Replay ↺</button>
    </div>
    <ul class="list" id="list">
      <li><div class="li-icon" style="background:rgba(99,102,241,0.1);color:#6366f1">🚀</div><div class="li-body"><strong>Deployment completed</strong><span>main → production · just now</span></div><span class="li-badge green">Success</span></li>
      <li><div class="li-icon" style="background:rgba(245,158,11,0.1);color:#f59e0b">⚠️</div><div class="li-body"><strong>High memory usage</strong><span>Server #3 at 89% · 2m ago</span></div><span class="li-badge yellow">Warning</span></li>
      <li><div class="li-icon" style="background:rgba(14,165,233,0.1);color:#0ea5e9">👤</div><div class="li-body"><strong>New user registered</strong><span>[email protected] · 5m ago</span></div><span class="li-badge blue">Info</span></li>
      <li><div class="li-icon" style="background:rgba(16,185,129,0.1);color:#10b981">💳</div><div class="li-body"><strong>Payment received</strong><span>$299 from Acme Corp · 8m ago</span></div><span class="li-badge green">Paid</span></li>
      <li><div class="li-icon" style="background:rgba(220,38,38,0.1);color:#dc2626">❌</div><div class="li-body"><strong>Build failed</strong><span>PR #142 · TypeScript error · 12m ago</span></div><span class="li-badge red">Error</span></li>
    </ul>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

replay() removes .show from all items, then uses nested setTimeout(i*80ms)
Each item's .show class is added 80ms * index after the initial reset
Items start at opacity: 0; translateX(-16px) — invisible and offset left
.show transition: opacity 0.35s ease, transform 0.35s ease — smooth reveal
querySelectorAll picks up any number of list items automatically
Items contain icon, title, description in a flex row layout
Works with IntersectionObserver for scroll-triggered stagger
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Stagger List — setTimeout nth-item Delay, opacity+translateX Reveal

Screenshot of the Staggered List Animation snippet rendered live

A stagger list animates list items into view one by one with increasing delays — each item slides in from the left slightly after the previous one, the same stagger used by the reveal on scroll. Used on feature lists, team member grids, and anywhere a vertical list needs more visual energy than a static display.

The replay function

replay() first removes .show from all items simultaneously. After a brief 50ms pause (allowing the CSS to apply the hidden state), it loops through each item with setTimeout((li, i) => li.classList.add('show'), i * 80). The 80ms delay multiplied by the item index creates the stagger — item 0 appears at 0ms, item 1 at 80ms, item 2 at 160ms, etc.

The CSS reveal transition

Items start at opacity: 0; transform: translateX(-16px) — invisible and shifted 16px to the left. Adding .show transitions to opacity: 1; transform: translateX(0) via transition: opacity 0.35s ease, transform 0.35s ease. The 35ms transition fills the gap between item appearances, creating a smooth flow.

Combining with IntersectionObserver

Replace the replay button with an IntersectionObserver that triggers replay() when the list scrolls into view. Items reveal sequentially on first scroll, then remain visible.

How CSS animation-delay creates the stagger

Each list item has animation-delay: calc(N * 0.07s) where N is the item index. Item 0 animates immediately, item 1 waits 70ms, item 2 waits 140ms, and so on. The keyframe itself is identical for every item — only the delay differs. This single CSS technique creates the stagger without any JavaScript, making it highly performant and easy to apply to any number of items.

The slide-in keyframe

Each item animates from opacity: 0, transform: translateX(-20px) to opacity: 1, transform: translateX(0). The translateX movement adds directionality to the reveal — items appear to slide in from the left. Change to translateY(20px) for a slide-up entrance, or scale(0.8) for a zoom-in. The 0.4s duration with ease-out timing gives a natural deceleration on landing.

Triggering on scroll

By default the animation fires on page load. To trigger when the list scrolls into view, add animation-play-state: paused to .item initially, then use IntersectionObserver to set animation-play-state: running when the list container enters the viewport. Call obs.disconnect() inside the callback so the animation only triggers once per page view.

Dynamic item count

When items are added dynamically, apply the .item class with the correct animation-delay computed from the new item index: item.style.animationDelay = (existingCount * 0.07) + 's'. The CSS animation runs once automatically when the class is first applied, so new items animate in on insertion without any additional JavaScript.

How CSS animation-delay creates the stagger

Each list item has animation-delay: calc(N * 0.07s) where N is the item index. Item 0 animates immediately, item 1 waits 70ms, item 2 waits 140ms, and so on. The keyframe itself is identical for every item — only the delay differs. This single CSS technique creates the stagger without any JavaScript, making it highly performant and easy to apply to any number of items.

The slide-in keyframe

Each item animates from opacity: 0, transform: translateX(-20px) to opacity: 1, transform: translateX(0). The translateX movement adds directionality to the reveal — items appear to slide in from the left. Change to translateY(20px) for a slide-up entrance, or scale(0.8) for a zoom-in. The 0.4s duration with ease-out timing gives a natural deceleration on landing.

Triggering on scroll

By default the animation fires on page load. To trigger when the list scrolls into view, add animation-play-state: paused to .item initially, then use IntersectionObserver to set animation-play-state: running when the list container enters the viewport. Call obs.disconnect() inside the callback so the animation only triggers once per page view.

Dynamic item count

When items are added dynamically, apply the .item class with the correct animation-delay computed from the new item index: item.style.animationDelay = (existingCount * 0.07) + 's'. The CSS animation runs once automatically when the class is first applied, so new items animate in on insertion without any additional JavaScript.

Step by step

How to Use

  1. 1
    Click ReplayClick the Replay button to see all items hide then reveal sequentially with 80ms stagger.
  2. 2
    Update the list itemsIn the HTML panel, change the icon, title, and description text in each <li> element.
  3. 3
    Add more itemsCopy an <li> and paste it inside the <ul>. The replay() function picks up any number of items via querySelectorAll.
  4. 4
    Change stagger timingUpdate the 80 (ms per item delay) in the JS panel.
  5. 5
    Add IntersectionObserver triggerReplace the replay button with an IntersectionObserver that triggers on scroll.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

Feature and benefit lists on landing pages
Animate feature bullet points in sequence as a section scrolls into view. Each item draws attention individually.
Step-by-step process sections
Reveal numbered process steps one by one for a guided storytelling effect on how-it-works sections.
Learn nested setTimeout stagger pattern
Edit the 80ms delay multiplier and the transition timing in the CSS panel to understand the relationship between JS timing and CSS transitions.
Pricing plan feature comparisons
Animate feature list items within each pricing card as the user scrolls to the pricing section.
Team member list reveals
Reveal team member cards in a stagger as the team section scrolls into view. Each card slides in from the left.
Combine with IntersectionObserver
Use this pattern instead of a library like AOS or ScrollReveal. Replace the replay button trigger with an IntersectionObserver for scroll-based activation.

Got questions?

Frequently Asked Questions

replay() removes .show from all items simultaneously. After a 50ms pause, it loops through items with setTimeout(callback, i * 80). Item 0 shows at 50ms, item 1 at 130ms, item 2 at 210ms, etc. Each item's CSS transition (0.35s) fills the gap between shows.

Create an IntersectionObserver on the .card container: when it enters the viewport, call replay(). Call it only once with obs.unobserve(target) to prevent re-triggering on scroll back.

Change the starting translateX(-16px) to translateX(16px) in the default CSS state. The items slide in from the right and settle at 0.

Yes. Give each grid item a unique data-index attribute. In the stagger loop, use parseInt(item.dataset.index) instead of the forEach index. Items can be staggered in any order regardless of DOM order.

Yes. Click "JSX" for a React component. Use useState to track whether items are shown. Map items to elements, applying a transitionDelay style based on index. Toggle the shown state on mount or scroll.

Change the stagger loop to go from items.length - 1 to 0: for (let i = items.length-1; i >= 0; i--) { setTimeout(() => items[i].classList.add("show"), (items.length-1-i) * 80); }