webdevpuneet.com

Deployment Pipeline Stage Tracker — Sequential CI/CD Stage Visualization with Real Halt-on-Failure

Deployment Pipeline Stage Tracker · Dashboards · Plain HTML, CSS & JS · Live preview

CategoryDashboards

Copy & paste

Source Code

<div class="demo">
  <div class="pipeline-card">
    <div class="pipeline-head">
      <span class="pipeline-title">deploy-web-app #482</span>
      <span class="pipeline-branch">main @ 7f3a2c1</span>
    </div>

    <div class="pipeline-track" id="pipelineTrack">
      <div class="stage" data-stage="build">
        <div class="stage-node"><span class="stage-icon"></span></div>
        <span class="stage-label">Build</span>
      </div>
      <div class="stage-connector"></div>
      <div class="stage" data-stage="test">
        <div class="stage-node"><span class="stage-icon"></span></div>
        <span class="stage-label">Test</span>
      </div>
      <div class="stage-connector"></div>
      <div class="stage" data-stage="staging">
        <div class="stage-node"><span class="stage-icon"></span></div>
        <span class="stage-label">Staging</span>
      </div>
      <div class="stage-connector"></div>
      <div class="stage" data-stage="prod">
        <div class="stage-node"><span class="stage-icon"></span></div>
        <span class="stage-label">Production</span>
      </div>
    </div>

    <div class="pipeline-detail" id="pipelineDetail" role="status" aria-live="polite">Click "Run pipeline" to start a deployment.</div>

    <button class="run-btn" id="runBtn">Run pipeline</button>
  </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

Genuinely sequential async/await execution — later stages never begin until the current one has finished and been evaluated
A real early return halts the loop on failure, leaving subsequent stages in their untouched default state
Every stage, including the final Production stage, has its own independent chance of failure
Three unambiguous, class-driven visual states per stage: running, done, and failed
Connector lines between stages visually reinforce progress via a sibling selector reacting to a completed stage
role="status" aria-live="polite" detail line announces the current stage and final outcome to screen readers
Retry restarts the entire pipeline cleanly from the first stage, matching real CI/CD retry behavior
Fully self-contained simulation, straightforward to replace with real pipeline status polling

About this UI Snippet

Deployment Pipeline Stage Tracker — Sequential Execution That Actually Halts on Failure

Screenshot of the Deployment Pipeline Stage Tracker snippet rendered live

A pipeline visualization is only trustworthy if it behaves like a real pipeline — later stages must never appear to run after an earlier one has failed. This widget implements that correctly: an async/await loop walks through each stage strictly in order, and a failure at any point stops the loop entirely, leaving every subsequent stage in its untouched, not-yet-run visual state.

A real sequential loop, not four independent animations

runPipeline() is a single async function with a for...of loop over the stages array, await-ing each stage's simulated duration before moving to the next iteration. This is a genuinely sequential execution model — stage 2 (test) only begins after stage 1 (build) has fully finished and been evaluated, mirroring how a real CI/CD pipeline actually runs stages one after another rather than kicking off several visual animations in parallel that merely *look* sequential through staggered CSS delays.

A failure genuinely stops execution, via a real return

Each stage's outcome is checked with an independent Math.random() < 0.18 roll — including for the *final* Production stage, deliberately, since a late-stage failure is a completely realistic scenario worth modeling. When a stage fails, the function calls return immediately after updating that stage's visual state and the status message — this is a genuine early exit from the for...of loop, which means the loop's remaining iterations (the subsequent stages) never execute their own stageEl.classList.add('running') line at all. Those later stages are left in whatever state they started in (their default, un-run appearance), correctly representing "this stage never got the chance to run" rather than any kind of skipped-but-attempted state.

Three distinct, unambiguous visual states per stage

Each .stage node can be running (pulsing dot, indigo border), done (solid green with a checkmark), failed (red), or none of these (its default untouched appearance) — and CSS handles all the styling differences through simple class-presence selectors, with the connecting line between two stages also picking up a green color once the stage *before* it (via the .stage.done + .stage-connector sibling selector) has completed, visually reinforcing forward progress along the track.

Retry starts genuinely fresh, not from the failed stage

Clicking "Retry pipeline" calls runPipeline() again from the top, and resetStages() explicitly clears every stage's running/done/failed classes before the loop restarts — so a retry always re-runs the entire pipeline from Build onward, matching how most real CI/CD systems handle a full pipeline retry rather than attempting to resume mid-pipeline from the point of failure.

Step by step

How to Use

  1. 1
    Click "Run pipeline" and watch it play outEach stage runs strictly in order; roughly an 18% chance per stage means you'll see both full-success and halted-on-failure outcomes.
  2. 2
    Watch a failure specificallyNotice that any stage after the one that failed remains untouched — the pipeline genuinely stops, it doesn't skip ahead.
  3. 3
    Adjust stage durationsChange the values in the STAGE_DURATIONS object to make specific stages run faster or slower in the simulation.
  4. 4
    Adjust the failure rateChange the 0.18 probability in runPipeline() to make failures more or less common during testing.
  5. 5
    Add a new stageAdd a new .stage element with a unique data-stage value plus a connector, and add matching entries to STAGE_DURATIONS and STAGE_LABELS.

Real-world uses

Common Use Cases

🚦
CI/CD dashboard widgets
Visualise Build, Test, Staging and Production stages, halting at the stage that fails so later ones never appear to run.
📦
Release management screens
Show release engineers exactly where a deployment stopped, with three unambiguous class-driven states for each stage: running, done and failed.
🧱
Build status pages
Reuse the pipeline strip on a status page, where every stage, including Production, has its own independent chance of failing.
🎓
Sequential async reference
Learn how an async/await loop with a real early return guarantees that later stages never begin until the current one succeeds.
Related: Habit Tracker Grid
See the Habit Tracker Grid for a related dashboards pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

It genuinely runs in order — a single async function awaits each stage's simulated duration inside a for...of loop before the next iteration begins, so stage 2 literally cannot start executing until stage 1's await has resolved and been evaluated, not just visually staggered CSS animations that merely appear sequential.

They never run at all — a failure triggers an immediate return from inside the loop, which exits the function entirely before any later iteration's stageEl.classList.add('running') line executes, so subsequent stages remain in their default, untouched visual state rather than being marked as skipped or attempted.

Any stage, including the final one, has its own independent Math.random() < 0.18 check — there's no special-casing that makes the last stage always succeed, since a late-stage failure is a completely realistic real-world scenario worth representing.

It calls runPipeline() again, which starts by calling resetStages() to clear every stage's running/done/failed classes back to their defaults, then re-runs the entire sequential loop from the very first stage — a full pipeline restart, not a resume from the point of failure.

Replace the wait() call and the randomized failure check inside the loop with actual polling of your CI/CD provider's API for that stage's real status, awaiting until it reports a terminal (success/failure) state before moving to the next stage in the loop.

Yes — the detail text region has role="status" aria-live="polite" and is updated with a plain-language description ("Running Test…", "Build failed — pipeline stopped.", etc.) at every stage transition, so the pipeline's progress and outcome are announced without requiring the user to visually track the stage nodes.