Source Code

<div class="pf-card">
  <div class="pf-head">
    <h2 class="pf-title">Processing your video</h2>
    <p class="pf-sub" id="pfSub">This can take a minute…</p>
  </div>
  <ul class="pf-list" id="pfList">
    <li class="pf-item" data-label="Uploading file"><span class="pf-icon"></span><div class="pf-body"><span class="pf-text">Uploading file</span><div class="pf-track"><div class="pf-fill"></div></div></div></li>
    <li class="pf-item" data-label="Transcoding"><span class="pf-icon"></span><div class="pf-body"><span class="pf-text">Transcoding</span><div class="pf-track"><div class="pf-fill"></div></div></div></li>
    <li class="pf-item" data-label="Generating thumbnails"><span class="pf-icon"></span><div class="pf-body"><span class="pf-text">Generating thumbnails</span><div class="pf-track"><div class="pf-fill"></div></div></div></li>
    <li class="pf-item" data-label="Publishing"><span class="pf-icon"></span><div class="pf-body"><span class="pf-text">Publishing</span><div class="pf-track"><div class="pf-fill"></div></div></div></li>
  </ul>
  <button class="pf-replay" id="pfReplay" type="button">↻ Replay</button>
</div>

Stage Progress Fill Checklist — Per-Step Progress Bar Loader JS

Stage Progress Fill Checklist · Loaders · Plain HTML, CSS & JS · Live preview

What's included

Features

Each active stage gets its own real 0-100% progress fill, not an instant spinner-to-check flip
Strictly sequential execution — only one stage is ever active at a time
Per-stage randomized duration reflects that real stages rarely take equal time
Fill bar only visible on the currently active item, keeping the finished list clean
Spinner and checkmark icon states built entirely with CSS, no icon library
Dimmed idle state for stages not yet reached
Running subtitle mirrors the active stage label and the completion state
Replayable via a dedicated button with fresh randomized timing on each run
Data-driven STAGES array — add, remove, or retime stages freely
Zero dependencies — vanilla DOM and CSS only

About this UI Snippet

Stage Progress Fill Checklist \u2014 A Multi-Stage Loader Where Each Step Gets Its Own Real Fill Bar

Screenshot of the Stage Progress Fill Checklist snippet rendered live

A typical multi-stage checklist loader shows each item flipping instantly from a spinner to a checkmark, which communicates which stage is current but nothing about how far into that stage you actually are. This snippet adds a real per-stage progress bar: the currently active item expands to show its own 0% \u2192 100% fill, driven by a real elapsed-time calculation against that stage's expected duration, and only becomes a checkmark once its own bar genuinely reaches 100%.

One real fill bar per active stage

Each .pf-item has a hidden .pf-track/.pf-fill pair that only becomes visible (display: block) while that item carries the .pf-active class. runStage(index) starts a setInterval that computes (Date.now() - start) / duration roughly every 45ms and sets fillEl.style.width directly to that percentage \u2014 a genuine incremental progress read for the currently running stage, not a single CSS transition jumping straight from 0 to 100.

Strict sequencing with per-stage randomized duration

runStage() only calls itself for the next stage after the current stage's fill interval reports 100% completion, guaranteeing stages run one at a time in order. Each stage's duration is randomized between its own minMs and maxMs \u2014 Transcoding is deliberately given a longer range than Publishing, since real video processing pipelines genuinely spend more time on some stages than others, and a viewer watching the transcoding bar move slower than the publishing bar reads as more honest than four identically-paced steps.

Dimmed, idle, and complete states

Items that haven't started yet sit at reduced opacity with a plain empty ring icon and no visible fill track. The active item brightens, shows a spinning-border icon, and reveals its fill bar. A completed item shows a checkmark built from a rotated CSS border pseudo-element, its label dims to a muted tone, and its fill track hides again \u2014 so the finished list reads cleanly without four permanently-visible full progress bars cluttering the view.

A running subtitle

The subtitle line above the list mirrors the active stage's label (Transcoding…) and switches to a completion message once every stage has genuinely finished, giving a second, textual confirmation of the same real state the bars and icons are showing.

Customizing it

Replace each stage's simulated duration range with your real backend's actual processing time estimates, or better, drive fillEl.style.width directly from real percentage-complete events reported by your job queue for that specific stage, skipping the internal setInterval entirely. Pair it with an agent task segment tracker for a compact horizontal alternative to this vertical checklist layout.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Instead of assuming every stage's fill bar is decorative, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how runStage()'s recursive structure guarantees stages run strictly one at a time, and why each active stage's fill bar recomputes its width from a real elapsed-time-versus-expected-duration ratio on a fast interval rather than relying on a single CSS width transition. The same assistant can help optimize it \u2014 for instance asking whether the 45ms interval tick rate is fine-grained enough for smooth visual movement without wasting unnecessary CPU cycles. It's also useful for extending it: ask it to replace the simulated per-stage duration with real percentage-complete events from a backend job queue, add an error/retry state to an individual stage, or make the fill bar's color shift as a stage nears completion. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a multi-stage checklist loader in plain HTML, CSS, and JavaScript \u2014 no libraries \u2014 where each active stage shows its OWN real progress fill bar (0% to 100%) before advancing, instead of instantly flipping a spinner icon to a checkmark.

Requirements:
- A vertical list of named stages (e.g. Uploading file, Transcoding, Generating thumbnails, Publishing), each with a status icon on the left and a label plus a progress track on the right.
- Only the currently active stage's progress track should be visible; stages not yet reached should be dimmed with a plain idle icon and no visible track, and completed stages should show a checkmark icon (built with pure CSS, no icon library) with their track hidden again.
- Stages must run strictly one at a time in sequence: the next stage's icon and track can only activate once the current stage's own fill bar has genuinely reached 100%, driven by a real repeating interval that computes elapsed time against that stage's expected duration \u2014 not a single CSS width transition jumping from 0 straight to 100.
- Give each stage its own randomized duration within its own min/max range (make at least one stage's range noticeably longer than another's), so the pacing varies realistically between stages and between runs rather than every stage taking an identical amount of time.
- Show a running subtitle above the list that mirrors the current stage's label while active, and switches to a distinct completion message once every stage has genuinely finished.
- Include a "Replay" button, disabled while the sequence is running, that resets every stage back to idle and re-runs the whole sequence with freshly randomized durations.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSThe first stage ("Uploading file") activates immediately and its fill bar begins filling.
  2. 2
    Watch each stage fillThe active item\u2019s own progress bar climbs from 0% to 100% before it becomes a checkmark.
  3. 3
    Watch stages advance in orderOnly one stage is ever active at a time; the next begins the instant the previous completes.
  4. 4
    Reach completionThe subtitle switches to a done message and the replay button re-enables.
  5. 5
    Click "Replay"Every item resets and the sequence runs again with fresh randomized durations.
  6. 6
    Edit the STAGES arrayRename stages and adjust their minMs/maxMs duration ranges to match your real pipeline.

Real-world uses

Common Use Cases

Video/media processing pipelines
The primary use case — show real per-stage progress for upload, transcode, and publish steps.
Multi-step deployment or build progress
Give each build phase its own honest fill instead of one ambiguous spinner per step.
Document processing and export tools
Narrate "Extracting… Formatting… Generating PDF…" with real per-stage progress.
Teaching sequential per-stage progress patterns
A clear reference for combining a checklist with individually-tracked fill bars.
Account and workspace provisioning screens
Show meaningful per-step progress instead of a single vague loading message.
Related: Multi-Stage Loading Checklist
See the Multi-Stage Loading Checklist for the simpler instant-checkmark version of this pattern.

Got questions?

Frequently Asked Questions

A plain checklist swaps a spinner icon for a checkmark the moment a stage starts and finishes, with no sense of how far along that stage actually is. This component adds a real per-stage fill bar, visible only on the currently active item, that climbs from 0% to 100% based on a genuine elapsed-time calculation before the checkmark appears.

No. runStage(index) only calls itself for the next stage inside the current stage\u2019s own completion branch, once its fill interval reports 100%. Stages always run strictly one at a time, in order, never overlapping.

Each STAGES entry defines its own minMs and maxMs, and Transcoding is given a longer range than Publishing because real video pipelines genuinely spend more time on some stages than others. Giving every stage an identical duration would read as obviously fake once a viewer compares a "quick" step to a "slow" one.

Replace the internal setInterval-driven fraction calculation with real percentage updates pushed from your job queue or processing backend (e.g. over SSE or polling) \u2014 call fillEl.style.width = realPercent + "%" directly whenever a new value arrives for the currently active stage, and call the same completion branch (removing pf-active, adding pf-complete, calling runStage for the next index) once that stage reports 100%.

The .pf-track element is only shown via display: block while its parent .pf-item carries the pf-active class; that class is removed the instant the stage completes. This keeps the finished checklist visually clean, since a fully-filled bar sitting next to a checkmark for every completed item would be redundant and cluttered.

Yes. Keep an activeIndex and a per-stage percentage in state, advancing activeIndex only once the active stage\u2019s percentage state reaches 100 inside an effect-driven interval or real progress event handler. Render each item\u2019s icon, label, and conditional fill bar from that state; the CSS classes and transitions carry over directly.