Open demo

CSS code

HTML
<div><span class="lbl">Dots, the current step stretches into a pill</span>
<div class="wz wz-tour" data-wizard role="group" aria-labelledby="tour-title">
  <h2 class="wz-title" id="tour-title">Welcome to Lumen Notes</h2>
  <p class="wz-sub">Five things you'll use every day, in under a minute.</p>

  <section class="wz-panel" data-step aria-labelledby="tour-h1">
    <span class="wz-badge"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M13 3 5 14h6l-1 7 8-11h-6z"/></svg></span>
    <h3 class="wz-h" id="tour-h1" tabindex="-1"><span class="wz-kick">Step 1 of 5</span>Capture from anywhere</h3>
    <p class="wz-p">Press Ctrl + Shift + N in any app to open a quick note. It waits in your Inbox until you file it.</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="tour-h2">
    <span class="wz-badge"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3.5 7.5A1.5 1.5 0 0 1 5 6h4l2 2h8a1.5 1.5 0 0 1 1.5 1.5v8A1.5 1.5 0 0 1 19 19H5a1.5 1.5 0 0 1-1.5-1.5z"/></svg></span>
    <h3 class="wz-h" id="tour-h2" tabindex="-1"><span class="wz-kick">Step 2 of 5</span>Sort with notebooks and tags</h3>
    <p class="wz-p">Notebooks hold projects, tags cut across them. A note can live in one notebook and carry any number of tags.</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="tour-h3">
    <span class="wz-badge"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M10 14a4 4 0 0 0 5.66 0l3-3a4 4 0 0 0-5.66-5.66l-1 1"/><path d="M14 10a4 4 0 0 0-5.66 0l-3 3a4 4 0 0 0 5.66 5.66l1-1"/></svg></span>
    <h3 class="wz-h" id="tour-h3" tabindex="-1"><span class="wz-kick">Step 3 of 5</span>Link ideas together</h3>
    <p class="wz-p">Type two square brackets to link another note. Every note lists the notes that point back to it.</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="tour-h4">
    <span class="wz-badge"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="6"/><path d="m20 20-4.2-4.2"/></svg></span>
    <h3 class="wz-h" id="tour-h4" tabindex="-1"><span class="wz-kick">Step 4 of 5</span>Find it in a second</h3>
    <p class="wz-p">Search reads your notes, PDFs and the text inside photos, so a snapped whiteboard turns up too.</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="tour-h5">
    <span class="wz-badge"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 11a8 8 0 0 0-14.3-4.9L4 8"/><path d="M4 4v4h4"/><path d="M4 13a8 8 0 0 0 14.3 4.9L20 16"/><path d="M20 20v-4h-4"/></svg></span>
    <h3 class="wz-h" id="tour-h5" tabindex="-1"><span class="wz-kick">Step 5 of 5</span>Pick up on any device</h3>
    <p class="wz-p">Notes sync to your phone, laptop and the web, and stay editable offline. Changes merge when you reconnect.</p>
  </section>
  <section class="wz-panel wz-done" data-done hidden aria-labelledby="tour-h6">
    <span class="wz-badge"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>
    <h3 class="wz-h" id="tour-h6" tabindex="-1">You're all set</h3>
    <p class="wz-p">Your first notebook, Ideas, is ready and waiting.</p>
    <button type="button" class="wz-btn ghost" data-restart>Replay the tour</button>
  </section>

  <div class="wz-tour-nav">
    <button type="button" class="wz-btn ghost" data-back hidden>Back</button>
    <ol class="wz-dots" data-stepper aria-label="Progress">
      <li><span class="sr-only">Capture</span><span class="sr-only" data-sr></span></li>
      <li><span class="sr-only">Organise</span><span class="sr-only" data-sr></span></li>
      <li><span class="sr-only">Link</span><span class="sr-only" data-sr></span></li>
      <li><span class="sr-only">Search</span><span class="sr-only" data-sr></span></li>
      <li><span class="sr-only">Sync</span><span class="sr-only" data-sr></span></li>
    </ol>
    <button type="button" class="wz-btn" data-next hidden>Next</button>
    <button type="button" class="wz-btn" data-finish>Get started</button>
  </div>
</div></div>

<div><span class="lbl">Icons with labels under them</span>
<div class="wz" data-wizard role="group" aria-labelledby="ord-title">
  <h2 class="wz-title" id="ord-title">Order #LX-2291</h2>
  <p class="wz-sub">Larkfield Home · 2 items · Tracked by Parcelway</p>
  <ol class="wz-icons" data-stepper aria-label="Progress">
    <li><span class="wz-ico"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 3.5h6v3H9z"/><path d="M9 5H6.5A1.5 1.5 0 0 0 5 6.5v13A1.5 1.5 0 0 0 6.5 21h11a1.5 1.5 0 0 0 1.5-1.5v-13A1.5 1.5 0 0 0 17.5 5H15"/><path d="m9 13.5 2 2 4-4"/></svg></span><span class="wz-name">Placed</span><span class="sr-only" data-sr></span></li>
    <li><span class="wz-ico"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20.5 7.5 12 3 3.5 7.5v9L12 21l8.5-4.5z"/><path d="m3.5 7.5 8.5 4.5 8.5-4.5M12 12v9"/></svg></span><span class="wz-name">Packed</span><span class="sr-only" data-sr></span></li>
    <li><span class="wz-ico"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.5 16.5v-10h11v10M13.5 9.5h4l3 3.5v3.5h-1.5M9 16.5h6"/><circle cx="6.5" cy="17" r="2"/><circle cx="17" cy="17" r="2"/></svg></span><span class="wz-name">Shipped</span><span class="sr-only" data-sr></span></li>
    <li><span class="wz-ico"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 21s-6.5-5.6-6.5-11a6.5 6.5 0 0 1 13 0c0 5.4-6.5 11-6.5 11z"/><circle cx="12" cy="10" r="2.3"/></svg></span><span class="wz-name">Out for delivery</span><span class="sr-only" data-sr></span></li>
    <li><span class="wz-ico"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 10.5 12 4l8 6.5V20H4z"/><path d="m9 14 2.2 2.2L15.5 12"/></svg></span><span class="wz-name">Delivered</span><span class="sr-only" data-sr></span></li>
  </ol>

  <section class="wz-panel" data-step aria-labelledby="ord-h1">
    <h3 class="wz-h" id="ord-h1" tabindex="-1"><span class="wz-kick">Step 1 of 5</span>Order placed</h3>
    <p class="wz-p">A Hekla stoneware mug set and an Oslo linen throw. Payment of €84.90 is confirmed.</p>
    <p class="wz-meta">Mon 13 Oct, 18:40 · Online store</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="ord-h2">
    <h3 class="wz-h" id="ord-h2" tabindex="-1"><span class="wz-kick">Step 2 of 5</span>Packed and labelled</h3>
    <p class="wz-p">Both items are in one 2.1 kg parcel, waiting for the Parcelway collection.</p>
    <p class="wz-meta">Tue 14 Oct, 09:12 · Rotterdam hub</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="ord-h3">
    <h3 class="wz-h" id="ord-h3" tabindex="-1"><span class="wz-kick">Step 3 of 5</span>On its way</h3>
    <p class="wz-p">Your parcel left the hub and is travelling to your local depot.</p>
    <p class="wz-meta">Tue 14 Oct, 16:05 · Rotterdam hub</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="ord-h4">
    <h3 class="wz-h" id="ord-h4" tabindex="-1"><span class="wz-kick">Step 4 of 5</span>Out for delivery</h3>
    <p class="wz-p">Sanne is driving today. You are stop 14 of 22, expected between 12:30 and 14:30.</p>
    <p class="wz-meta">Thu 16 Oct, 07:30 · Utrecht depot</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="ord-h5">
    <h3 class="wz-h" id="ord-h5" tabindex="-1"><span class="wz-kick">Step 5 of 5</span>Delivered</h3>
    <p class="wz-p">Left with your neighbour at number 16 and signed for by M. de Vries.</p>
    <p class="wz-meta">Thu 16 Oct, 13:48 · Utrecht</p>
  </section>

  <div class="wz-nav">
    <button type="button" class="wz-btn ghost" data-back hidden>Back</button>
    <button type="button" class="wz-btn" data-next hidden>Next</button>
  </div>
</div></div>

<div><span class="lbl">Progress line with markers</span>
<div class="wz" data-wizard role="group" aria-labelledby="mtg-title">
  <h2 class="wz-title" id="mtg-title">Mortgage pre-approval</h2>
  <p class="wz-sub">Halden Bank · Application HB-40718</p>
  <ol class="wz-line" data-stepper aria-label="Progress">
    <li><span class="wz-mark"></span>Income<span class="sr-only" data-sr></span></li>
    <li><span class="wz-mark"></span>Assets<span class="sr-only" data-sr></span></li>
    <li><span class="wz-mark"></span>Credit check<span class="sr-only" data-sr></span></li>
    <li><span class="wz-mark"></span>Offer<span class="sr-only" data-sr></span></li>
  </ol>

  <section class="wz-panel" data-step aria-labelledby="mtg-h1">
    <h3 class="wz-h" id="mtg-h1" tabindex="-1"><span class="wz-kick">Step 1 of 4</span>Your income is verified</h3>
    <p class="wz-p">We matched your last three payslips with your bank statements.</p>
    <dl class="wz-facts"><div><dt>Annual income</dt><dd>£64,200</dd></div><div><dt>Employer</dt><dd>Merrow Health Trust</dd></div></dl>
  </section>
  <section class="wz-panel" data-step aria-labelledby="mtg-h2">
    <h3 class="wz-h" id="mtg-h2" tabindex="-1"><span class="wz-kick">Step 2 of 4</span>Your deposit is confirmed</h3>
    <p class="wz-p">Savings held for more than three months count toward the deposit.</p>
    <dl class="wz-facts"><div><dt>Deposit</dt><dd>£48,000</dd></div><div><dt>Held in</dt><dd>Savings and ISA</dd></div></dl>
  </section>
  <section class="wz-panel" data-step aria-labelledby="mtg-h3">
    <h3 class="wz-h" id="mtg-h3" tabindex="-1"><span class="wz-kick">Step 3 of 4</span>Credit check passed</h3>
    <p class="wz-p">A soft search: other lenders can't see it and it doesn't affect your score.</p>
    <dl class="wz-facts"><div><dt>Result</dt><dd>Excellent</dd></div><div><dt>Checked</dt><dd>Wed 22 Oct</dd></div></dl>
  </section>
  <section class="wz-panel" data-step aria-labelledby="mtg-h4">
    <h3 class="wz-h" id="mtg-h4" tabindex="-1"><span class="wz-kick">Step 4 of 4</span>You can borrow up to £285,000</h3>
    <p class="wz-p">This agreement in principle is valid for 90 days. It is not a final mortgage offer.</p>
    <dl class="wz-facts"><div><dt>Rate</dt><dd>4.19% fixed, 5 years</dd></div><div><dt>Monthly</dt><dd>£1,392 over 30 years</dd></div></dl>
  </section>
  <section class="wz-panel wz-done" data-done hidden aria-labelledby="mtg-h5">
    <span class="wz-badge"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>
    <h3 class="wz-h" id="mtg-h5" tabindex="-1">Offer accepted</h3>
    <p class="wz-p">A copy of agreement HB-40718 is in your inbox. An adviser will call within two working days.</p>
    <button type="button" class="wz-btn ghost" data-restart>Start over</button>
  </section>

  <div class="wz-nav" data-nav>
    <button type="button" class="wz-btn ghost" data-back hidden>Back</button>
    <button type="button" class="wz-btn" data-next hidden>Next</button>
    <button type="button" class="wz-btn" data-finish>Accept offer</button>
  </div>
</div></div>

<div><span class="lbl">Mobile header with a progress bar</span>
<div class="wz wz-app" data-wizard role="group" aria-labelledby="bank-title">
  <h2 class="sr-only" id="bank-title">Add a bank account</h2>
  <div class="wz-appbar">
    <button type="button" class="wz-iconbtn" data-back hidden aria-label="Back"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19 12H5M11 18l-6-6 6-6"/></svg></button>
    <p class="wz-apptitle" aria-hidden="true"><span data-count>Step 1 of 4</span><b data-name>Choose bank</b></p>
  </div>
  <div class="wz-meter" aria-hidden="true"><span></span></div>
  <ol class="sr-only" data-stepper aria-label="Progress">
    <li>Choose bank<span data-sr></span></li>
    <li>Sign in<span data-sr></span></li>
    <li>Pick account<span data-sr></span></li>
    <li>Confirm<span data-sr></span></li>
  </ol>

  <section class="wz-panel" data-step aria-labelledby="bank-h1">
    <h3 class="wz-h" id="bank-h1" tabindex="-1"><span class="sr-only">Step 1 of 4: </span>Choose your bank</h3>
    <p class="wz-p">Pennant pays your invoices out to this account. We connect to 40 UK banks.</p>
    <p class="wz-meta">Selected: Fernbank</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="bank-h2">
    <h3 class="wz-h" id="bank-h2" tabindex="-1"><span class="sr-only">Step 2 of 4: </span>Sign in to Fernbank</h3>
    <p class="wz-p">You approve the link in the Fernbank app. Pennant never sees your password.</p>
    <p class="wz-meta">Secure Open Banking connection</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="bank-h3">
    <h3 class="wz-h" id="bank-h3" tabindex="-1"><span class="sr-only">Step 3 of 4: </span>Pick an account</h3>
    <p class="wz-p">We found two accounts. Payouts will go to the one you choose here.</p>
    <p class="wz-meta">Everyday account ending 4821</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="bank-h4">
    <h3 class="wz-h" id="bank-h4" tabindex="-1"><span class="sr-only">Step 4 of 4: </span>Confirm and link</h3>
    <p class="wz-p">Payouts arrive the next working day. You can change the account in Settings.</p>
    <p class="wz-meta">Fernbank Everyday ending 4821</p>
  </section>
  <section class="wz-panel wz-done" data-done hidden aria-labelledby="bank-h5">
    <span class="wz-badge"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>
    <h3 class="wz-h" id="bank-h5" tabindex="-1">Account linked</h3>
    <p class="wz-p">Your next payout, £1,240.00, goes to Everyday ending 4821 on Friday.</p>
    <button type="button" class="wz-btn ghost" data-restart>Add another account</button>
  </section>

  <div class="wz-appfoot" data-nav>
    <button type="button" class="wz-btn" data-next hidden>Continue</button>
    <button type="button" class="wz-btn" data-finish>Link account</button>
  </div>
</div></div>
CSS
/* Stepper styles: four compact wizards that share one script. Each has an <ol data-stepper> (dots, icons,
   a progress line, or a visually hidden list behind an app header), one <section data-step> per step and
   Back / Next buttons. The script sets data-state, aria-current="step" and the --wz-i / --wz-n / --wz-p
   custom properties the progress styles read, and moves focus to the new step's heading. */
.wz{--wz-accent:#0369a1;--wz-accent-dark:#075985;--wz-soft:#e0f2fe;--wz-done:#0f766e;--wz-done-soft:#ccfbf1;--wz-ink:#0c1a26;--wz-muted:#4a5b6b;--wz-line:#7d8d9d;--wz-track:#dbe4ec;
  background:#fff;border-radius:18px;padding:28px 30px;box-shadow:0 1px 2px rgba(12,26,38,.06),0 10px 30px -12px rgba(7,89,133,.22);color:var(--wz-ink)}
.wz [hidden]{display:none!important}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.wz-title{font-size:1.2rem;line-height:1.35;font-weight:700;letter-spacing:-.015em;margin:0}
.wz-sub{margin:2px 0 0;color:var(--wz-muted);font-size:.875rem}

/* step panels */
.wz-panel{display:grid;gap:8px;margin-top:24px;animation:wz-in .3s ease}
@keyframes wz-in{from{opacity:0;transform:translateY(6px)}}
.wz-h{margin:0;font-size:1.1rem;line-height:1.35;font-weight:700;letter-spacing:-.01em}
.wz-h:focus{outline:none}
.wz-kick{display:block;font-size:.7rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--wz-accent-dark);margin-bottom:2px}
.wz-p{margin:0;color:var(--wz-muted);font-size:.92rem}
.wz-meta{justify-self:start;margin:4px 0 0;padding:5px 10px;border-radius:8px;background:#f0f5f9;font-size:.82rem;font-weight:600;font-variant-numeric:tabular-nums}
.wz-badge{width:48px;height:48px;border-radius:14px;display:grid;place-items:center;background:var(--wz-soft);color:var(--wz-accent-dark);margin-bottom:6px}
.wz-badge svg,.wz-ico svg,.wz-iconbtn svg{width:24px;height:24px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.wz-done .wz-badge{background:var(--wz-done-soft);color:var(--wz-done)}
.wz-facts{margin:6px 0 0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
.wz-facts div{background:#f0f5f9;border-radius:10px;padding:10px 12px}
.wz-facts dt{font-size:.75rem;color:var(--wz-muted)}
.wz-facts dd{margin:0;font-weight:700;font-size:.95rem}

/* buttons */
.wz-nav{display:flex;gap:10px;margin-top:24px;padding-top:18px;border-top:1px solid #e3eaf0}
.wz-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;padding:0 20px;border-radius:10px;border:1.5px solid var(--wz-accent);background:var(--wz-accent);color:#fff;font:inherit;font-size:.95rem;font-weight:600;cursor:pointer;transition:background .2s}
.wz-btn:hover{background:var(--wz-accent-dark)}
.wz-btn:focus-visible,.wz-iconbtn:focus-visible{outline:none;box-shadow:0 0 0 3px #fff,0 0 0 5px var(--wz-accent)}
.wz-btn.ghost{background:#fff;color:var(--wz-accent-dark);border-color:var(--wz-line)}
.wz-btn.ghost:hover{background:#f0f9ff}
.wz-nav .wz-btn:not(.ghost){margin-left:auto}

/* A. dots: the current dot stretches into a pill, done dots turn teal */
.wz-tour .wz-panel{min-height:170px;align-content:start}
.wz-tour-nav{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:10px;margin-top:20px}
.wz-tour-nav [data-back]{grid-column:1;justify-self:start}
.wz-tour-nav [data-next],.wz-tour-nav [data-finish]{grid-column:3;grid-row:1;justify-self:end}
.wz-dots{grid-column:2;grid-row:1;list-style:none;margin:0;padding:0;display:flex;align-items:center;gap:7px}
.wz-dots li{width:9px;height:9px;border-radius:999px;background:var(--wz-line);transition:width .3s ease,background-color .3s}
.wz-dots [data-state="done"]{background:var(--wz-done)}
.wz-dots [data-state="current"]{width:28px;background:var(--wz-accent)}

/* B. icons in circles, joined by connectors, labels under them */
.wz-icons{list-style:none;margin:24px 0 0;padding:0;display:grid;grid-template-columns:repeat(5,1fr)}
.wz-icons li{position:relative;display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center;font-size:.78rem;line-height:1.3;color:var(--wz-muted)}
.wz-icons li + li::before{content:"";position:absolute;top:19px;left:calc(-50% + 26px);right:calc(50% + 26px);height:2px;border-radius:2px;background:var(--wz-line);transition:background .25s}
.wz-ico{width:40px;height:40px;border-radius:50%;display:grid;place-items:center;border:2px solid var(--wz-line);background:#fff;color:#5a6a7a;transition:background .25s,border-color .25s,box-shadow .25s}
.wz-ico svg{width:20px;height:20px;stroke-width:1.8}
.wz-icons [data-state="done"] .wz-ico{background:var(--wz-done);border-color:var(--wz-done);color:#fff}
.wz-icons [data-state="current"] .wz-ico{background:var(--wz-accent);border-color:var(--wz-accent);color:#fff;box-shadow:0 0 0 5px var(--wz-soft)}
.wz-icons [data-state="current"]{color:var(--wz-ink);font-weight:600}
.wz-icons [data-state="done"] + li::before,.wz-icons [data-state="current"]::before{background:var(--wz-done)}
@media (max-width:520px){.wz-ico{width:34px;height:34px}.wz-ico svg{width:17px;height:17px}
  .wz-icons li + li::before{top:16px;left:calc(-50% + 22px);right:calc(50% + 22px)}
  .wz-icons [data-state]:not([data-state="current"]) .wz-name{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}}

/* C. a thin track that fills to the current marker (width from --wz-p, set by the script) */
.wz-line{position:relative;list-style:none;margin:26px 0 0;padding:0;display:grid;grid-template-columns:repeat(var(--wz-n,4),1fr)}
.wz-line::before,.wz-line::after{content:"";position:absolute;top:6px;left:calc(50% / var(--wz-n,4));height:4px;border-radius:4px}
.wz-line::before{right:calc(50% / var(--wz-n,4));background:var(--wz-track)}
.wz-line::after{width:calc((100% - 100% / var(--wz-n,4)) * var(--wz-p,0));background:var(--wz-done);transition:width .4s ease}
.wz-line li{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;gap:10px;text-align:center;font-size:.8rem;line-height:1.3;color:var(--wz-muted)}
.wz-mark{width:16px;height:16px;border-radius:50%;border:2px solid var(--wz-line);background:#fff;transition:border-color .25s,background .25s,box-shadow .25s}
.wz-line [data-state="done"] .wz-mark{background:var(--wz-done);border-color:var(--wz-done)}
.wz-line [data-state="current"] .wz-mark{border:4px solid var(--wz-accent);box-shadow:0 0 0 4px var(--wz-soft)}
.wz-line [data-state="current"]{color:var(--wz-ink);font-weight:600}

/* D. app header: back arrow, "Step 2 of 4" over the step name and a thin bar; the real stepper is visually hidden */
.wz-app{max-width:400px;width:100%;margin:0 auto;padding:0;overflow:hidden}
.wz-appbar{display:grid;grid-template-columns:44px 1fr 44px;align-items:center;gap:6px;padding:10px 10px 12px}
.wz-appbar [data-back]{grid-column:1;grid-row:1}
.wz-iconbtn{width:44px;height:44px;border-radius:12px;display:grid;place-items:center;border:0;background:transparent;color:var(--wz-ink);cursor:pointer;transition:background .2s}
.wz-iconbtn:hover{background:#f0f5f9}
.wz-apptitle{grid-column:2;grid-row:1;margin:0;display:grid;text-align:center;line-height:1.3}
.wz-apptitle span{font-size:.75rem;color:var(--wz-muted)}
.wz-apptitle b{font-size:1rem}
.wz-meter{height:4px;background:var(--wz-track)}
.wz-meter span{display:block;height:100%;width:calc((var(--wz-i,0) + 1) / var(--wz-n,4) * 100%);background:var(--wz-accent);border-radius:0 4px 4px 0;transition:width .4s ease}
.wz-app .wz-panel{margin:0;padding:24px 22px 4px}
.wz-app .wz-done{padding-bottom:24px}
.wz-appfoot{padding:20px 22px 22px}
.wz-appfoot .wz-btn{width:100%}

.wz-done{justify-items:center;text-align:center}
.wz-done .wz-btn{margin-top:10px}
@media (prefers-reduced-motion:reduce){.wz *,.wz *::before,.wz *::after,.wz ::before,.wz ::after{transition:none!important;animation:none!important}}
@media (max-width:600px){.wz{padding:24px 18px}.wz-app{padding:0}.wz-app .wz-panel{padding:22px 18px 4px}.wz-appfoot{padding:18px 18px 20px}}
JavaScript
// Stepper wizard: shows one step at a time, marks the stepper with data-state and aria-current="step",
// sets --wz-i / --wz-n / --wz-p (0 to 1) for progress styles and moves focus to the new step's heading.
document.querySelectorAll('[data-wizard]').forEach(function (wz) {
  var panels = wz.querySelectorAll('[data-step]'), steps = wz.querySelectorAll('[data-stepper] > li'), n = panels.length;
  var back = wz.querySelector('[data-back]'), next = wz.querySelector('[data-next]'), finish = wz.querySelector('[data-finish]');
  var done = wz.querySelector('[data-done]'), navs = wz.querySelectorAll('[data-nav]'), at = 0;
  var count = wz.querySelector('[data-count]'), name = wz.querySelector('[data-name]');
  function mark(i) {
    var k = Math.min(i, n - 1);
    steps.forEach(function (s, j) {
      s.dataset.state = j < i ? 'done' : j === i ? 'current' : 'next';
      if (j === i) s.setAttribute('aria-current', 'step'); else s.removeAttribute('aria-current');
      s.querySelector('[data-sr]').textContent = j < i ? ', completed' : '';
    });
    wz.style.setProperty('--wz-i', k); wz.style.setProperty('--wz-n', n); wz.style.setProperty('--wz-p', n > 1 ? k / (n - 1) : 1);
    if (count) count.textContent = 'Step ' + (k + 1) + ' of ' + n;
    if (name) name.textContent = steps[k].firstChild.textContent.trim(); // the li's leading text is the step name
  }
  function go(i, focus) {
    at = i; mark(i);
    panels.forEach(function (p, j) { p.hidden = j !== i; });
    if (back) back.hidden = i === 0;
    next.hidden = i === n - 1;
    if (finish) finish.hidden = !next.hidden;
    if (focus) panels[i].querySelector('[tabindex="-1"]').focus();
  }
  if (back) back.addEventListener('click', function () { go(at - 1, true); });
  next.addEventListener('click', function () { go(at + 1, true); });
  // Last step: do the real thing here (navigate, save). The demo shows the done panel instead.
  if (finish && done) finish.addEventListener('click', function () {
    panels[at].hidden = true; finish.hidden = true; if (back) back.hidden = true; mark(n);
    navs.forEach(function (x) { x.hidden = true; });
    done.hidden = false; done.querySelector('[tabindex="-1"]').focus();
  });
  if (done) done.querySelector('[data-restart]').addEventListener('click', function () {
    done.hidden = true; navs.forEach(function (x) { x.hidden = false; }); go(0, true);
  });
  go(0);
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Dots, the current step stretches into a pill</span>
<div class="wz wz-tour card" data-wizard role="group" aria-labelledby="tour-title"><div class="card-body">
  <h2 class="wz-title fw-bold mb-0" id="tour-title">Welcome to Lumen Notes</h2>
  <p class="text-body-secondary mt-1 mb-0 small">Five things you'll use every day, in under a minute.</p>

  <section class="wz-panel" data-step aria-labelledby="tour-h1">
    <span class="wz-badge rounded-4"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M13 3 5 14h6l-1 7 8-11h-6z"/></svg></span>
    <h3 class="wz-h fw-bold mb-0" id="tour-h1" tabindex="-1"><span class="wz-kick">Step 1 of 5</span>Capture from anywhere</h3>
    <p class="wz-p text-body-secondary mb-0">Press Ctrl + Shift + N in any app to open a quick note. It waits in your Inbox until you file it.</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="tour-h2">
    <span class="wz-badge rounded-4"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3.5 7.5A1.5 1.5 0 0 1 5 6h4l2 2h8a1.5 1.5 0 0 1 1.5 1.5v8A1.5 1.5 0 0 1 19 19H5a1.5 1.5 0 0 1-1.5-1.5z"/></svg></span>
    <h3 class="wz-h fw-bold mb-0" id="tour-h2" tabindex="-1"><span class="wz-kick">Step 2 of 5</span>Sort with notebooks and tags</h3>
    <p class="wz-p text-body-secondary mb-0">Notebooks hold projects, tags cut across them. A note can live in one notebook and carry any number of tags.</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="tour-h3">
    <span class="wz-badge rounded-4"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M10 14a4 4 0 0 0 5.66 0l3-3a4 4 0 0 0-5.66-5.66l-1 1"/><path d="M14 10a4 4 0 0 0-5.66 0l-3 3a4 4 0 0 0 5.66 5.66l1-1"/></svg></span>
    <h3 class="wz-h fw-bold mb-0" id="tour-h3" tabindex="-1"><span class="wz-kick">Step 3 of 5</span>Link ideas together</h3>
    <p class="wz-p text-body-secondary mb-0">Type two square brackets to link another note. Every note lists the notes that point back to it.</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="tour-h4">
    <span class="wz-badge rounded-4"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="6"/><path d="m20 20-4.2-4.2"/></svg></span>
    <h3 class="wz-h fw-bold mb-0" id="tour-h4" tabindex="-1"><span class="wz-kick">Step 4 of 5</span>Find it in a second</h3>
    <p class="wz-p text-body-secondary mb-0">Search reads your notes, PDFs and the text inside photos, so a snapped whiteboard turns up too.</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="tour-h5">
    <span class="wz-badge rounded-4"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 11a8 8 0 0 0-14.3-4.9L4 8"/><path d="M4 4v4h4"/><path d="M4 13a8 8 0 0 0 14.3 4.9L20 16"/><path d="M20 20v-4h-4"/></svg></span>
    <h3 class="wz-h fw-bold mb-0" id="tour-h5" tabindex="-1"><span class="wz-kick">Step 5 of 5</span>Pick up on any device</h3>
    <p class="wz-p text-body-secondary mb-0">Notes sync to your phone, laptop and the web, and stay editable offline. Changes merge when you reconnect.</p>
  </section>
  <section class="wz-panel wz-done" data-done hidden aria-labelledby="tour-h6">
    <span class="wz-badge rounded-4"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>
    <h3 class="wz-h fw-bold mb-0" id="tour-h6" tabindex="-1">You're all set</h3>
    <p class="wz-p text-body-secondary mb-0">Your first notebook, Ideas, is ready and waiting.</p>
    <button type="button" class="btn btn-outline-secondary" data-restart>Replay the tour</button>
  </section>

  <div class="wz-tour-nav">
    <button type="button" class="btn btn-outline-secondary" data-back hidden>Back</button>
    <ol class="wz-dots" data-stepper aria-label="Progress">
      <li><span class="visually-hidden">Capture</span><span class="visually-hidden" data-sr></span></li>
      <li><span class="visually-hidden">Organise</span><span class="visually-hidden" data-sr></span></li>
      <li><span class="visually-hidden">Link</span><span class="visually-hidden" data-sr></span></li>
      <li><span class="visually-hidden">Search</span><span class="visually-hidden" data-sr></span></li>
      <li><span class="visually-hidden">Sync</span><span class="visually-hidden" data-sr></span></li>
    </ol>
    <button type="button" class="btn btn-primary" data-next hidden>Next</button>
    <button type="button" class="btn btn-primary" data-finish>Get started</button>
  </div>
</div></div></div>

<div><span class="lbl">Icons with labels under them</span>
<div class="wz card" data-wizard role="group" aria-labelledby="ord-title"><div class="card-body">
  <h2 class="wz-title fw-bold mb-0" id="ord-title">Order #LX-2291</h2>
  <p class="text-body-secondary mt-1 mb-0 small">Larkfield Home · 2 items · Tracked by Parcelway</p>
  <ol class="wz-icons" data-stepper aria-label="Progress">
    <li><span class="wz-ico"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 3.5h6v3H9z"/><path d="M9 5H6.5A1.5 1.5 0 0 0 5 6.5v13A1.5 1.5 0 0 0 6.5 21h11a1.5 1.5 0 0 0 1.5-1.5v-13A1.5 1.5 0 0 0 17.5 5H15"/><path d="m9 13.5 2 2 4-4"/></svg></span><span class="wz-name">Placed</span><span class="visually-hidden" data-sr></span></li>
    <li><span class="wz-ico"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20.5 7.5 12 3 3.5 7.5v9L12 21l8.5-4.5z"/><path d="m3.5 7.5 8.5 4.5 8.5-4.5M12 12v9"/></svg></span><span class="wz-name">Packed</span><span class="visually-hidden" data-sr></span></li>
    <li><span class="wz-ico"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.5 16.5v-10h11v10M13.5 9.5h4l3 3.5v3.5h-1.5M9 16.5h6"/><circle cx="6.5" cy="17" r="2"/><circle cx="17" cy="17" r="2"/></svg></span><span class="wz-name">Shipped</span><span class="visually-hidden" data-sr></span></li>
    <li><span class="wz-ico"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 21s-6.5-5.6-6.5-11a6.5 6.5 0 0 1 13 0c0 5.4-6.5 11-6.5 11z"/><circle cx="12" cy="10" r="2.3"/></svg></span><span class="wz-name">Out for delivery</span><span class="visually-hidden" data-sr></span></li>
    <li><span class="wz-ico"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 10.5 12 4l8 6.5V20H4z"/><path d="m9 14 2.2 2.2L15.5 12"/></svg></span><span class="wz-name">Delivered</span><span class="visually-hidden" data-sr></span></li>
  </ol>

  <section class="wz-panel" data-step aria-labelledby="ord-h1">
    <h3 class="wz-h fw-bold mb-0" id="ord-h1" tabindex="-1"><span class="wz-kick">Step 1 of 5</span>Order placed</h3>
    <p class="wz-p text-body-secondary mb-0">A Hekla stoneware mug set and an Oslo linen throw. Payment of €84.90 is confirmed.</p>
    <p class="wz-meta fw-semibold rounded-2 mb-0">Mon 13 Oct, 18:40 · Online store</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="ord-h2">
    <h3 class="wz-h fw-bold mb-0" id="ord-h2" tabindex="-1"><span class="wz-kick">Step 2 of 5</span>Packed and labelled</h3>
    <p class="wz-p text-body-secondary mb-0">Both items are in one 2.1 kg parcel, waiting for the Parcelway collection.</p>
    <p class="wz-meta fw-semibold rounded-2 mb-0">Tue 14 Oct, 09:12 · Rotterdam hub</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="ord-h3">
    <h3 class="wz-h fw-bold mb-0" id="ord-h3" tabindex="-1"><span class="wz-kick">Step 3 of 5</span>On its way</h3>
    <p class="wz-p text-body-secondary mb-0">Your parcel left the hub and is travelling to your local depot.</p>
    <p class="wz-meta fw-semibold rounded-2 mb-0">Tue 14 Oct, 16:05 · Rotterdam hub</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="ord-h4">
    <h3 class="wz-h fw-bold mb-0" id="ord-h4" tabindex="-1"><span class="wz-kick">Step 4 of 5</span>Out for delivery</h3>
    <p class="wz-p text-body-secondary mb-0">Sanne is driving today. You are stop 14 of 22, expected between 12:30 and 14:30.</p>
    <p class="wz-meta fw-semibold rounded-2 mb-0">Thu 16 Oct, 07:30 · Utrecht depot</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="ord-h5">
    <h3 class="wz-h fw-bold mb-0" id="ord-h5" tabindex="-1"><span class="wz-kick">Step 5 of 5</span>Delivered</h3>
    <p class="wz-p text-body-secondary mb-0">Left with your neighbour at number 16 and signed for by M. de Vries.</p>
    <p class="wz-meta fw-semibold rounded-2 mb-0">Thu 16 Oct, 13:48 · Utrecht</p>
  </section>

  <div class="wz-nav">
    <button type="button" class="btn btn-outline-secondary" data-back hidden>Back</button>
    <button type="button" class="btn btn-primary" data-next hidden>Next</button>
  </div>
</div></div></div>

<div><span class="lbl">Progress line with markers</span>
<div class="wz card" data-wizard role="group" aria-labelledby="mtg-title"><div class="card-body">
  <h2 class="wz-title fw-bold mb-0" id="mtg-title">Mortgage pre-approval</h2>
  <p class="text-body-secondary mt-1 mb-0 small">Halden Bank · Application HB-40718</p>
  <ol class="wz-line" data-stepper aria-label="Progress">
    <li><span class="wz-mark"></span>Income<span class="visually-hidden" data-sr></span></li>
    <li><span class="wz-mark"></span>Assets<span class="visually-hidden" data-sr></span></li>
    <li><span class="wz-mark"></span>Credit check<span class="visually-hidden" data-sr></span></li>
    <li><span class="wz-mark"></span>Offer<span class="visually-hidden" data-sr></span></li>
  </ol>

  <section class="wz-panel" data-step aria-labelledby="mtg-h1">
    <h3 class="wz-h fw-bold mb-0" id="mtg-h1" tabindex="-1"><span class="wz-kick">Step 1 of 4</span>Your income is verified</h3>
    <p class="wz-p text-body-secondary mb-0">We matched your last three payslips with your bank statements.</p>
    <dl class="wz-facts"><div><dt>Annual income</dt><dd>£64,200</dd></div><div><dt>Employer</dt><dd>Merrow Health Trust</dd></div></dl>
  </section>
  <section class="wz-panel" data-step aria-labelledby="mtg-h2">
    <h3 class="wz-h fw-bold mb-0" id="mtg-h2" tabindex="-1"><span class="wz-kick">Step 2 of 4</span>Your deposit is confirmed</h3>
    <p class="wz-p text-body-secondary mb-0">Savings held for more than three months count toward the deposit.</p>
    <dl class="wz-facts"><div><dt>Deposit</dt><dd>£48,000</dd></div><div><dt>Held in</dt><dd>Savings and ISA</dd></div></dl>
  </section>
  <section class="wz-panel" data-step aria-labelledby="mtg-h3">
    <h3 class="wz-h fw-bold mb-0" id="mtg-h3" tabindex="-1"><span class="wz-kick">Step 3 of 4</span>Credit check passed</h3>
    <p class="wz-p text-body-secondary mb-0">A soft search: other lenders can't see it and it doesn't affect your score.</p>
    <dl class="wz-facts"><div><dt>Result</dt><dd>Excellent</dd></div><div><dt>Checked</dt><dd>Wed 22 Oct</dd></div></dl>
  </section>
  <section class="wz-panel" data-step aria-labelledby="mtg-h4">
    <h3 class="wz-h fw-bold mb-0" id="mtg-h4" tabindex="-1"><span class="wz-kick">Step 4 of 4</span>You can borrow up to £285,000</h3>
    <p class="wz-p text-body-secondary mb-0">This agreement in principle is valid for 90 days. It is not a final mortgage offer.</p>
    <dl class="wz-facts"><div><dt>Rate</dt><dd>4.19% fixed, 5 years</dd></div><div><dt>Monthly</dt><dd>£1,392 over 30 years</dd></div></dl>
  </section>
  <section class="wz-panel wz-done" data-done hidden aria-labelledby="mtg-h5">
    <span class="wz-badge rounded-4"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>
    <h3 class="wz-h fw-bold mb-0" id="mtg-h5" tabindex="-1">Offer accepted</h3>
    <p class="wz-p text-body-secondary mb-0">A copy of agreement HB-40718 is in your inbox. An adviser will call within two working days.</p>
    <button type="button" class="btn btn-outline-secondary" data-restart>Start over</button>
  </section>

  <div class="wz-nav" data-nav>
    <button type="button" class="btn btn-outline-secondary" data-back hidden>Back</button>
    <button type="button" class="btn btn-primary" data-next hidden>Next</button>
    <button type="button" class="btn btn-primary" data-finish>Accept offer</button>
  </div>
</div></div></div>

<div><span class="lbl">Mobile header with a progress bar</span>
<div class="wz wz-app card" data-wizard role="group" aria-labelledby="bank-title">
  <h2 class="visually-hidden" id="bank-title">Add a bank account</h2>
  <div class="wz-appbar">
    <button type="button" class="btn wz-iconbtn" data-back hidden aria-label="Back"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19 12H5M11 18l-6-6 6-6"/></svg></button>
    <p class="wz-apptitle" aria-hidden="true"><span data-count>Step 1 of 4</span><b data-name>Choose bank</b></p>
  </div>
  <div class="progress wz-meter" aria-hidden="true"><div class="progress-bar"></div></div>
  <ol class="visually-hidden" data-stepper aria-label="Progress">
    <li>Choose bank<span data-sr></span></li>
    <li>Sign in<span data-sr></span></li>
    <li>Pick account<span data-sr></span></li>
    <li>Confirm<span data-sr></span></li>
  </ol>

  <section class="wz-panel" data-step aria-labelledby="bank-h1">
    <h3 class="wz-h fw-bold mb-0" id="bank-h1" tabindex="-1"><span class="visually-hidden">Step 1 of 4: </span>Choose your bank</h3>
    <p class="wz-p text-body-secondary mb-0">Pennant pays your invoices out to this account. We connect to 40 UK banks.</p>
    <p class="wz-meta fw-semibold rounded-2 mb-0">Selected: Fernbank</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="bank-h2">
    <h3 class="wz-h fw-bold mb-0" id="bank-h2" tabindex="-1"><span class="visually-hidden">Step 2 of 4: </span>Sign in to Fernbank</h3>
    <p class="wz-p text-body-secondary mb-0">You approve the link in the Fernbank app. Pennant never sees your password.</p>
    <p class="wz-meta fw-semibold rounded-2 mb-0">Secure Open Banking connection</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="bank-h3">
    <h3 class="wz-h fw-bold mb-0" id="bank-h3" tabindex="-1"><span class="visually-hidden">Step 3 of 4: </span>Pick an account</h3>
    <p class="wz-p text-body-secondary mb-0">We found two accounts. Payouts will go to the one you choose here.</p>
    <p class="wz-meta fw-semibold rounded-2 mb-0">Everyday account ending 4821</p>
  </section>
  <section class="wz-panel" data-step aria-labelledby="bank-h4">
    <h3 class="wz-h fw-bold mb-0" id="bank-h4" tabindex="-1"><span class="visually-hidden">Step 4 of 4: </span>Confirm and link</h3>
    <p class="wz-p text-body-secondary mb-0">Payouts arrive the next working day. You can change the account in Settings.</p>
    <p class="wz-meta fw-semibold rounded-2 mb-0">Fernbank Everyday ending 4821</p>
  </section>
  <section class="wz-panel wz-done" data-done hidden aria-labelledby="bank-h5">
    <span class="wz-badge rounded-4"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>
    <h3 class="wz-h fw-bold mb-0" id="bank-h5" tabindex="-1">Account linked</h3>
    <p class="wz-p text-body-secondary mb-0">Your next payout, £1,240.00, goes to Everyday ending 4821 on Friday.</p>
    <button type="button" class="btn btn-outline-secondary" data-restart>Add another account</button>
  </section>

  <div class="wz-appfoot" data-nav>
    <button type="button" class="btn btn-primary" data-next hidden>Continue</button>
    <button type="button" class="btn btn-primary" data-finish>Link account</button>
  </div>
</div></div>
CSS
/* Stepper styles on Bootstrap 5: .card, .btn, .progress and .visually-hidden, restyled through Bootstrap's CSS
   variables; the four stepper designs are the custom part. The script sets data-state, aria-current="step" and the
   --wz-i / --wz-n / --wz-p custom properties the progress styles read, and moves focus to the new step's heading. */
.wz{--wz-accent:#0369a1;--wz-accent-dark:#075985;--wz-soft:#e0f2fe;--wz-done:#0f766e;--wz-done-soft:#ccfbf1;--wz-muted:#4a5b6b;--wz-line:#7d8d9d;--wz-track:#dbe4ec;
  --bs-body-color:#0c1a26;--bs-secondary-color:#4a5b6b;--bs-card-color:#0c1a26;
  border:0;border-radius:18px;box-shadow:0 1px 2px rgba(12,26,38,.06),0 10px 30px -12px rgba(7,89,133,.22);color:#0c1a26}
.wz .card-body{padding:28px 30px}
.wz-title{font-size:1.2rem;line-height:1.35;letter-spacing:-.015em}
.wz .btn{--bs-btn-padding-x:20px;--bs-btn-font-weight:600;--bs-btn-border-width:1.5px;--bs-btn-border-radius:10px;min-height:44px}
.wz .btn-primary{--bs-btn-bg:var(--wz-accent);--bs-btn-border-color:var(--wz-accent);--bs-btn-hover-bg:var(--wz-accent-dark);--bs-btn-hover-border-color:var(--wz-accent-dark);--bs-btn-active-bg:var(--wz-accent-dark);--bs-btn-active-border-color:var(--wz-accent-dark)}
.wz .btn-outline-secondary{--bs-btn-color:var(--wz-accent-dark);--bs-btn-border-color:var(--wz-line);--bs-btn-bg:#fff;--bs-btn-hover-bg:#f0f9ff;--bs-btn-hover-color:var(--wz-accent-dark);--bs-btn-hover-border-color:var(--wz-line);--bs-btn-active-bg:#e0f2fe;--bs-btn-active-color:var(--wz-accent-dark)}
.wz .btn:focus-visible{outline:0;box-shadow:0 0 0 3px #fff,0 0 0 5px var(--wz-accent)}

/* step panels */
.wz-panel{display:grid;gap:8px;margin-top:24px;animation:wz-in .3s ease}
@keyframes wz-in{from{opacity:0;transform:translateY(6px)}}
.wz-h{font-size:1.1rem;line-height:1.35;letter-spacing:-.01em}
.wz-h:focus{outline:none}
.wz-kick{display:block;font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:var(--wz-accent-dark);margin-bottom:2px}
.wz-p{font-size:.92rem}
.wz-meta{justify-self:start;margin-top:4px;padding:5px 10px;font-size:.82rem;background:#f0f5f9;font-variant-numeric:tabular-nums}
.wz-badge{width:48px;height:48px;display:grid;place-items:center;background:var(--wz-soft);color:var(--wz-accent-dark);margin-bottom:6px}
.wz-badge svg,.wz-ico svg,.wz-iconbtn svg{width:24px;height:24px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.wz-done .wz-badge{background:var(--wz-done-soft);color:var(--wz-done)}
.wz-facts{margin:6px 0 0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
.wz-facts div{background:#f0f5f9;border-radius:10px;padding:10px 12px}
.wz-facts dt{font-size:.75rem;font-weight:400;color:var(--wz-muted)}
.wz-facts dd{margin:0;font-weight:700;font-size:.95rem}
.wz-nav{display:flex;gap:10px;margin-top:24px;padding-top:18px;border-top:1px solid #e3eaf0}
.wz-nav .btn-primary{margin-left:auto}

/* A. dots: the current dot stretches into a pill, done dots turn teal */
.wz-tour .wz-panel{min-height:170px;align-content:start}
.wz-tour-nav{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:10px;margin-top:20px}
.wz-tour-nav [data-back]{grid-column:1;justify-self:start}
.wz-tour-nav [data-next],.wz-tour-nav [data-finish]{grid-column:3;grid-row:1;justify-self:end}
.wz-dots{grid-column:2;grid-row:1;list-style:none;margin:0;padding:0;display:flex;align-items:center;gap:7px}
.wz-dots li{width:9px;height:9px;border-radius:999px;background:var(--wz-line);transition:width .3s ease,background-color .3s}
.wz-dots [data-state="done"]{background:var(--wz-done)}
.wz-dots [data-state="current"]{width:28px;background:var(--wz-accent)}

/* B. icons in circles, joined by connectors, labels under them */
.wz-icons{list-style:none;margin:24px 0 0;padding:0;display:grid;grid-template-columns:repeat(5,1fr)}
.wz-icons li{position:relative;display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center;font-size:.78rem;line-height:1.3;color:var(--wz-muted)}
.wz-icons li + li::before{content:"";position:absolute;top:19px;left:calc(-50% + 26px);right:calc(50% + 26px);height:2px;border-radius:2px;background:var(--wz-line);transition:background .25s}
.wz-ico{width:40px;height:40px;border-radius:50%;display:grid;place-items:center;border:2px solid var(--wz-line);background:#fff;color:#5a6a7a;transition:background .25s,border-color .25s,box-shadow .25s}
.wz-ico svg{width:20px;height:20px;stroke-width:1.8}
.wz-icons [data-state="done"] .wz-ico{background:var(--wz-done);border-color:var(--wz-done);color:#fff}
.wz-icons [data-state="current"] .wz-ico{background:var(--wz-accent);border-color:var(--wz-accent);color:#fff;box-shadow:0 0 0 5px var(--wz-soft)}
.wz-icons [data-state="current"]{color:#0c1a26;font-weight:600}
.wz-icons [data-state="done"] + li::before,.wz-icons [data-state="current"]::before{background:var(--wz-done)}
@media (max-width:520px){.wz-ico{width:34px;height:34px}.wz-ico svg{width:17px;height:17px}
  .wz-icons li + li::before{top:16px;left:calc(-50% + 22px);right:calc(50% + 22px)}
  .wz-icons [data-state]:not([data-state="current"]) .wz-name{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}}

/* C. a thin track that fills to the current marker (width from --wz-p, set by the script) */
.wz-line{position:relative;list-style:none;margin:26px 0 0;padding:0;display:grid;grid-template-columns:repeat(var(--wz-n,4),1fr)}
.wz-line::before,.wz-line::after{content:"";position:absolute;top:6px;left:calc(50% / var(--wz-n,4));height:4px;border-radius:4px}
.wz-line::before{right:calc(50% / var(--wz-n,4));background:var(--wz-track)}
.wz-line::after{width:calc((100% - 100% / var(--wz-n,4)) * var(--wz-p,0));background:var(--wz-done);transition:width .4s ease}
.wz-line li{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;gap:10px;text-align:center;font-size:.8rem;line-height:1.3;color:var(--wz-muted)}
.wz-mark{width:16px;height:16px;border-radius:50%;border:2px solid var(--wz-line);background:#fff;transition:border-color .25s,background .25s,box-shadow .25s}
.wz-line [data-state="done"] .wz-mark{background:var(--wz-done);border-color:var(--wz-done)}
.wz-line [data-state="current"] .wz-mark{border:4px solid var(--wz-accent);box-shadow:0 0 0 4px var(--wz-soft)}
.wz-line [data-state="current"]{color:#0c1a26;font-weight:600}

/* D. app header: back arrow, "Step 2 of 4" over the step name and a .progress bar; the real stepper is visually hidden */
.wz-app{max-width:400px;width:100%;margin:0 auto;overflow:hidden}
.wz-appbar{display:grid;grid-template-columns:44px 1fr 44px;align-items:center;gap:6px;padding:10px 10px 12px}
.wz-appbar [data-back]{grid-column:1;grid-row:1}
.wz .wz-iconbtn{--bs-btn-padding-x:0;--bs-btn-padding-y:0;--bs-btn-border-width:0;--bs-btn-border-radius:12px;--bs-btn-color:#0c1a26;--bs-btn-hover-color:#0c1a26;--bs-btn-hover-bg:#f0f5f9;--bs-btn-active-bg:#e3eaf0;width:44px;height:44px;display:grid;place-items:center}
.wz-apptitle{grid-column:2;grid-row:1;margin:0;display:grid;text-align:center;line-height:1.3}
.wz-apptitle span{font-size:.75rem;color:var(--wz-muted)}
.wz-apptitle b{font-size:1rem}
.wz-meter{--bs-progress-height:4px;--bs-progress-bg:var(--wz-track);--bs-progress-border-radius:0;--bs-progress-bar-bg:var(--wz-accent);--bs-progress-bar-transition:width .4s ease}
.wz-meter .progress-bar{width:calc((var(--wz-i,0) + 1) / var(--wz-n,4) * 100%);border-radius:0 4px 4px 0}
.wz-app .wz-panel{margin:0;padding:24px 22px 4px}
.wz-app .wz-done{padding-bottom:24px}
.wz-appfoot{padding:20px 22px 22px}
.wz-appfoot .btn{width:100%}

.wz-done{justify-items:center;text-align:center}
.wz-done .btn{margin-top:10px}
@media (prefers-reduced-motion:reduce){.wz *,.wz *::before,.wz *::after,.wz ::before,.wz ::after{transition:none!important;animation:none!important}}
@media (max-width:600px){.wz .card-body{padding:24px 18px}.wz-app .wz-panel{padding:22px 18px 4px}.wz-appfoot{padding:18px 18px 20px}}
JavaScript
// Stepper wizard: shows one step at a time, marks the stepper with data-state and aria-current="step",
// sets --wz-i / --wz-n / --wz-p (0 to 1) for progress styles and moves focus to the new step's heading.
document.querySelectorAll('[data-wizard]').forEach(function (wz) {
  var panels = wz.querySelectorAll('[data-step]'), steps = wz.querySelectorAll('[data-stepper] > li'), n = panels.length;
  var back = wz.querySelector('[data-back]'), next = wz.querySelector('[data-next]'), finish = wz.querySelector('[data-finish]');
  var done = wz.querySelector('[data-done]'), navs = wz.querySelectorAll('[data-nav]'), at = 0;
  var count = wz.querySelector('[data-count]'), name = wz.querySelector('[data-name]');
  function mark(i) {
    var k = Math.min(i, n - 1);
    steps.forEach(function (s, j) {
      s.dataset.state = j < i ? 'done' : j === i ? 'current' : 'next';
      if (j === i) s.setAttribute('aria-current', 'step'); else s.removeAttribute('aria-current');
      s.querySelector('[data-sr]').textContent = j < i ? ', completed' : '';
    });
    wz.style.setProperty('--wz-i', k); wz.style.setProperty('--wz-n', n); wz.style.setProperty('--wz-p', n > 1 ? k / (n - 1) : 1);
    if (count) count.textContent = 'Step ' + (k + 1) + ' of ' + n;
    if (name) name.textContent = steps[k].firstChild.textContent.trim(); // the li's leading text is the step name
  }
  function go(i, focus) {
    at = i; mark(i);
    panels.forEach(function (p, j) { p.hidden = j !== i; });
    if (back) back.hidden = i === 0;
    next.hidden = i === n - 1;
    if (finish) finish.hidden = !next.hidden;
    if (focus) panels[i].querySelector('[tabindex="-1"]').focus();
  }
  if (back) back.addEventListener('click', function () { go(at - 1, true); });
  next.addEventListener('click', function () { go(at + 1, true); });
  // Last step: do the real thing here (navigate, save). The demo shows the done panel instead.
  if (finish && done) finish.addEventListener('click', function () {
    panels[at].hidden = true; finish.hidden = true; if (back) back.hidden = true; mark(n);
    navs.forEach(function (x) { x.hidden = true; });
    done.hidden = false; done.querySelector('[tabindex="-1"]').focus();
  });
  if (done) done.querySelector('[data-restart]').addEventListener('click', function () {
    done.hidden = true; navs.forEach(function (x) { x.hidden = false; }); go(0, true);
  });
  go(0);
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-[#4a5b6b] uppercase">Dots, the current step stretches into a pill</span>
<div class="rounded-[18px] bg-white px-[30px] py-7 text-wz-ink shadow-[0_1px_2px_rgba(12,26,38,.06),0_10px_30px_-12px_rgba(7,89,133,.22)] max-[600px]:px-[18px] max-[600px]:py-6" data-wizard role="group" aria-labelledby="tour-title">
  <h2 class="text-[1.2rem] leading-[1.35] font-bold tracking-[-.015em]" id="tour-title">Welcome to Lumen Notes</h2>
  <p class="mt-0.5 text-[.875rem] text-wz-muted">Five things you'll use every day, in under a minute.</p>

  <section class="mt-6 min-h-[170px] content-start grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="tour-h1">
    <span class="mb-1.5 grid size-12 place-items-center rounded-[14px] bg-wz-soft text-wz-dark"><svg class="size-6 fill-none stroke-current stroke-2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="M13 3 5 14h6l-1 7 8-11h-6z"/></svg></span>
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="tour-h1" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 1 of 5</span>Capture from anywhere</h3>
    <p class="text-[.92rem] text-wz-muted">Press Ctrl + Shift + N in any app to open a quick note. It waits in your Inbox until you file it.</p>
  </section>
  <section class="mt-6 min-h-[170px] content-start grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="tour-h2">
    <span class="mb-1.5 grid size-12 place-items-center rounded-[14px] bg-wz-soft text-wz-dark"><svg class="size-6 fill-none stroke-current stroke-2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="M3.5 7.5A1.5 1.5 0 0 1 5 6h4l2 2h8a1.5 1.5 0 0 1 1.5 1.5v8A1.5 1.5 0 0 1 19 19H5a1.5 1.5 0 0 1-1.5-1.5z"/></svg></span>
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="tour-h2" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 2 of 5</span>Sort with notebooks and tags</h3>
    <p class="text-[.92rem] text-wz-muted">Notebooks hold projects, tags cut across them. A note can live in one notebook and carry any number of tags.</p>
  </section>
  <section class="mt-6 min-h-[170px] content-start grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="tour-h3">
    <span class="mb-1.5 grid size-12 place-items-center rounded-[14px] bg-wz-soft text-wz-dark"><svg class="size-6 fill-none stroke-current stroke-2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 14a4 4 0 0 0 5.66 0l3-3a4 4 0 0 0-5.66-5.66l-1 1"/><path d="M14 10a4 4 0 0 0-5.66 0l-3 3a4 4 0 0 0 5.66 5.66l1-1"/></svg></span>
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="tour-h3" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 3 of 5</span>Link ideas together</h3>
    <p class="text-[.92rem] text-wz-muted">Type two square brackets to link another note. Every note lists the notes that point back to it.</p>
  </section>
  <section class="mt-6 min-h-[170px] content-start grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="tour-h4">
    <span class="mb-1.5 grid size-12 place-items-center rounded-[14px] bg-wz-soft text-wz-dark"><svg class="size-6 fill-none stroke-current stroke-2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="6"/><path d="m20 20-4.2-4.2"/></svg></span>
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="tour-h4" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 4 of 5</span>Find it in a second</h3>
    <p class="text-[.92rem] text-wz-muted">Search reads your notes, PDFs and the text inside photos, so a snapped whiteboard turns up too.</p>
  </section>
  <section class="mt-6 min-h-[170px] content-start grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="tour-h5">
    <span class="mb-1.5 grid size-12 place-items-center rounded-[14px] bg-wz-soft text-wz-dark"><svg class="size-6 fill-none stroke-current stroke-2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 11a8 8 0 0 0-14.3-4.9L4 8"/><path d="M4 4v4h4"/><path d="M4 13a8 8 0 0 0 14.3 4.9L20 16"/><path d="M20 20v-4h-4"/></svg></span>
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="tour-h5" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 5 of 5</span>Pick up on any device</h3>
    <p class="text-[.92rem] text-wz-muted">Notes sync to your phone, laptop and the web, and stay editable offline. Changes merge when you reconnect.</p>
  </section>
  <section class="mt-6 min-h-[170px] content-start justify-items-center text-center grid animate-wz-in gap-2 motion-reduce:animate-none" data-done hidden aria-labelledby="tour-h6">
    <span class="mb-1.5 grid size-12 place-items-center rounded-[14px] bg-wz-done-soft text-wz-done"><svg class="size-6 fill-none stroke-current stroke-2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="tour-h6" tabindex="-1">You're all set</h3>
    <p class="text-[.92rem] text-wz-muted">Your first notebook, Ideas, is ready and waiting.</p>
    <button type="button" class="mt-2.5 inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-line bg-white text-wz-dark hover:bg-[#f0f9ff]" data-restart>Replay the tour</button>
  </section>

  <div class="mt-5 grid grid-cols-[1fr_auto_1fr] items-center gap-2.5">
    <button type="button" class="col-start-1 justify-self-start inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-line bg-white text-wz-dark hover:bg-[#f0f9ff]" data-back hidden>Back</button>
    <ol class="col-start-2 row-start-1 flex items-center gap-[7px]" data-stepper aria-label="Progress">
      <li class="h-[9px] w-[9px] rounded-full bg-wz-line transition-[width,background-color] duration-300 data-[state=current]:w-7 data-[state=current]:bg-wz-accent data-[state=done]:bg-wz-done motion-reduce:transition-none"><span class="sr-only">Capture</span><span class="sr-only" data-sr></span></li>
      <li class="h-[9px] w-[9px] rounded-full bg-wz-line transition-[width,background-color] duration-300 data-[state=current]:w-7 data-[state=current]:bg-wz-accent data-[state=done]:bg-wz-done motion-reduce:transition-none"><span class="sr-only">Organise</span><span class="sr-only" data-sr></span></li>
      <li class="h-[9px] w-[9px] rounded-full bg-wz-line transition-[width,background-color] duration-300 data-[state=current]:w-7 data-[state=current]:bg-wz-accent data-[state=done]:bg-wz-done motion-reduce:transition-none"><span class="sr-only">Link</span><span class="sr-only" data-sr></span></li>
      <li class="h-[9px] w-[9px] rounded-full bg-wz-line transition-[width,background-color] duration-300 data-[state=current]:w-7 data-[state=current]:bg-wz-accent data-[state=done]:bg-wz-done motion-reduce:transition-none"><span class="sr-only">Search</span><span class="sr-only" data-sr></span></li>
      <li class="h-[9px] w-[9px] rounded-full bg-wz-line transition-[width,background-color] duration-300 data-[state=current]:w-7 data-[state=current]:bg-wz-accent data-[state=done]:bg-wz-done motion-reduce:transition-none"><span class="sr-only">Sync</span><span class="sr-only" data-sr></span></li>
    </ol>
    <button type="button" class="col-start-3 row-start-1 justify-self-end inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-accent bg-wz-accent text-white hover:bg-wz-dark" data-next hidden>Next</button>
    <button type="button" class="col-start-3 row-start-1 justify-self-end inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-accent bg-wz-accent text-white hover:bg-wz-dark" data-finish>Get started</button>
  </div>
</div></div>

<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-[#4a5b6b] uppercase">Icons with labels under them</span>
<div class="rounded-[18px] bg-white px-[30px] py-7 text-wz-ink shadow-[0_1px_2px_rgba(12,26,38,.06),0_10px_30px_-12px_rgba(7,89,133,.22)] max-[600px]:px-[18px] max-[600px]:py-6" data-wizard role="group" aria-labelledby="ord-title">
  <h2 class="text-[1.2rem] leading-[1.35] font-bold tracking-[-.015em]" id="ord-title">Order #LX-2291</h2>
  <p class="mt-0.5 text-[.875rem] text-wz-muted">Larkfield Home · 2 items · Tracked by Parcelway</p>
  <ol class="mt-6 grid grid-cols-5" data-stepper aria-label="Progress">
    <li class="group relative flex flex-col items-center gap-2 text-center text-[.78rem] leading-[1.3] text-wz-muted data-[state=current]:font-semibold data-[state=current]:text-wz-ink not-first:before:absolute not-first:before:top-[19px] not-first:before:right-[calc(50%+26px)] not-first:before:left-[calc(-50%+26px)] not-first:before:h-0.5 not-first:before:rounded-sm not-first:before:bg-wz-line not-first:before:transition-colors not-first:data-[state=current]:before:bg-wz-done not-first:data-[state=done]:before:bg-wz-done not-first:motion-reduce:before:transition-none max-[520px]:not-first:before:top-4 max-[520px]:not-first:before:right-[calc(50%+22px)] max-[520px]:not-first:before:left-[calc(-50%+22px)]"><span class="grid size-10 place-items-center rounded-full border-2 border-wz-line bg-white text-[#5a6a7a] transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-wz-accent group-data-[state=current]:bg-wz-accent group-data-[state=current]:text-white group-data-[state=current]:shadow-[0_0_0_5px_var(--color-wz-soft)] group-data-[state=done]:border-wz-done group-data-[state=done]:bg-wz-done group-data-[state=done]:text-white motion-reduce:transition-none max-[520px]:size-[34px]"><svg class="size-5 fill-none stroke-current stroke-[1.8] max-[520px]:size-[17px]" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 3.5h6v3H9z"/><path d="M9 5H6.5A1.5 1.5 0 0 0 5 6.5v13A1.5 1.5 0 0 0 6.5 21h11a1.5 1.5 0 0 0 1.5-1.5v-13A1.5 1.5 0 0 0 17.5 5H15"/><path d="m9 13.5 2 2 4-4"/></svg></span><span class="max-[520px]:group-data-[state=done]:sr-only max-[520px]:group-data-[state=next]:sr-only">Placed</span><span class="sr-only" data-sr></span></li>
    <li class="group relative flex flex-col items-center gap-2 text-center text-[.78rem] leading-[1.3] text-wz-muted data-[state=current]:font-semibold data-[state=current]:text-wz-ink not-first:before:absolute not-first:before:top-[19px] not-first:before:right-[calc(50%+26px)] not-first:before:left-[calc(-50%+26px)] not-first:before:h-0.5 not-first:before:rounded-sm not-first:before:bg-wz-line not-first:before:transition-colors not-first:data-[state=current]:before:bg-wz-done not-first:data-[state=done]:before:bg-wz-done not-first:motion-reduce:before:transition-none max-[520px]:not-first:before:top-4 max-[520px]:not-first:before:right-[calc(50%+22px)] max-[520px]:not-first:before:left-[calc(-50%+22px)]"><span class="grid size-10 place-items-center rounded-full border-2 border-wz-line bg-white text-[#5a6a7a] transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-wz-accent group-data-[state=current]:bg-wz-accent group-data-[state=current]:text-white group-data-[state=current]:shadow-[0_0_0_5px_var(--color-wz-soft)] group-data-[state=done]:border-wz-done group-data-[state=done]:bg-wz-done group-data-[state=done]:text-white motion-reduce:transition-none max-[520px]:size-[34px]"><svg class="size-5 fill-none stroke-current stroke-[1.8] max-[520px]:size-[17px]" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="M20.5 7.5 12 3 3.5 7.5v9L12 21l8.5-4.5z"/><path d="m3.5 7.5 8.5 4.5 8.5-4.5M12 12v9"/></svg></span><span class="max-[520px]:group-data-[state=done]:sr-only max-[520px]:group-data-[state=next]:sr-only">Packed</span><span class="sr-only" data-sr></span></li>
    <li class="group relative flex flex-col items-center gap-2 text-center text-[.78rem] leading-[1.3] text-wz-muted data-[state=current]:font-semibold data-[state=current]:text-wz-ink not-first:before:absolute not-first:before:top-[19px] not-first:before:right-[calc(50%+26px)] not-first:before:left-[calc(-50%+26px)] not-first:before:h-0.5 not-first:before:rounded-sm not-first:before:bg-wz-line not-first:before:transition-colors not-first:data-[state=current]:before:bg-wz-done not-first:data-[state=done]:before:bg-wz-done not-first:motion-reduce:before:transition-none max-[520px]:not-first:before:top-4 max-[520px]:not-first:before:right-[calc(50%+22px)] max-[520px]:not-first:before:left-[calc(-50%+22px)]"><span class="grid size-10 place-items-center rounded-full border-2 border-wz-line bg-white text-[#5a6a7a] transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-wz-accent group-data-[state=current]:bg-wz-accent group-data-[state=current]:text-white group-data-[state=current]:shadow-[0_0_0_5px_var(--color-wz-soft)] group-data-[state=done]:border-wz-done group-data-[state=done]:bg-wz-done group-data-[state=done]:text-white motion-reduce:transition-none max-[520px]:size-[34px]"><svg class="size-5 fill-none stroke-current stroke-[1.8] max-[520px]:size-[17px]" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="M2.5 16.5v-10h11v10M13.5 9.5h4l3 3.5v3.5h-1.5M9 16.5h6"/><circle cx="6.5" cy="17" r="2"/><circle cx="17" cy="17" r="2"/></svg></span><span class="max-[520px]:group-data-[state=done]:sr-only max-[520px]:group-data-[state=next]:sr-only">Shipped</span><span class="sr-only" data-sr></span></li>
    <li class="group relative flex flex-col items-center gap-2 text-center text-[.78rem] leading-[1.3] text-wz-muted data-[state=current]:font-semibold data-[state=current]:text-wz-ink not-first:before:absolute not-first:before:top-[19px] not-first:before:right-[calc(50%+26px)] not-first:before:left-[calc(-50%+26px)] not-first:before:h-0.5 not-first:before:rounded-sm not-first:before:bg-wz-line not-first:before:transition-colors not-first:data-[state=current]:before:bg-wz-done not-first:data-[state=done]:before:bg-wz-done not-first:motion-reduce:before:transition-none max-[520px]:not-first:before:top-4 max-[520px]:not-first:before:right-[calc(50%+22px)] max-[520px]:not-first:before:left-[calc(-50%+22px)]"><span class="grid size-10 place-items-center rounded-full border-2 border-wz-line bg-white text-[#5a6a7a] transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-wz-accent group-data-[state=current]:bg-wz-accent group-data-[state=current]:text-white group-data-[state=current]:shadow-[0_0_0_5px_var(--color-wz-soft)] group-data-[state=done]:border-wz-done group-data-[state=done]:bg-wz-done group-data-[state=done]:text-white motion-reduce:transition-none max-[520px]:size-[34px]"><svg class="size-5 fill-none stroke-current stroke-[1.8] max-[520px]:size-[17px]" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 21s-6.5-5.6-6.5-11a6.5 6.5 0 0 1 13 0c0 5.4-6.5 11-6.5 11z"/><circle cx="12" cy="10" r="2.3"/></svg></span><span class="max-[520px]:group-data-[state=done]:sr-only max-[520px]:group-data-[state=next]:sr-only">Out for delivery</span><span class="sr-only" data-sr></span></li>
    <li class="group relative flex flex-col items-center gap-2 text-center text-[.78rem] leading-[1.3] text-wz-muted data-[state=current]:font-semibold data-[state=current]:text-wz-ink not-first:before:absolute not-first:before:top-[19px] not-first:before:right-[calc(50%+26px)] not-first:before:left-[calc(-50%+26px)] not-first:before:h-0.5 not-first:before:rounded-sm not-first:before:bg-wz-line not-first:before:transition-colors not-first:data-[state=current]:before:bg-wz-done not-first:data-[state=done]:before:bg-wz-done not-first:motion-reduce:before:transition-none max-[520px]:not-first:before:top-4 max-[520px]:not-first:before:right-[calc(50%+22px)] max-[520px]:not-first:before:left-[calc(-50%+22px)]"><span class="grid size-10 place-items-center rounded-full border-2 border-wz-line bg-white text-[#5a6a7a] transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-wz-accent group-data-[state=current]:bg-wz-accent group-data-[state=current]:text-white group-data-[state=current]:shadow-[0_0_0_5px_var(--color-wz-soft)] group-data-[state=done]:border-wz-done group-data-[state=done]:bg-wz-done group-data-[state=done]:text-white motion-reduce:transition-none max-[520px]:size-[34px]"><svg class="size-5 fill-none stroke-current stroke-[1.8] max-[520px]:size-[17px]" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 10.5 12 4l8 6.5V20H4z"/><path d="m9 14 2.2 2.2L15.5 12"/></svg></span><span class="max-[520px]:group-data-[state=done]:sr-only max-[520px]:group-data-[state=next]:sr-only">Delivered</span><span class="sr-only" data-sr></span></li>
  </ol>

  <section class="mt-6 grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="ord-h1">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="ord-h1" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 1 of 5</span>Order placed</h3>
    <p class="text-[.92rem] text-wz-muted">A Hekla stoneware mug set and an Oslo linen throw. Payment of €84.90 is confirmed.</p>
    <p class="mt-1 justify-self-start rounded-lg bg-[#f0f5f9] px-2.5 py-[5px] text-[.82rem] font-semibold tabular-nums">Mon 13 Oct, 18:40 · Online store</p>
  </section>
  <section class="mt-6 grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="ord-h2">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="ord-h2" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 2 of 5</span>Packed and labelled</h3>
    <p class="text-[.92rem] text-wz-muted">Both items are in one 2.1 kg parcel, waiting for the Parcelway collection.</p>
    <p class="mt-1 justify-self-start rounded-lg bg-[#f0f5f9] px-2.5 py-[5px] text-[.82rem] font-semibold tabular-nums">Tue 14 Oct, 09:12 · Rotterdam hub</p>
  </section>
  <section class="mt-6 grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="ord-h3">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="ord-h3" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 3 of 5</span>On its way</h3>
    <p class="text-[.92rem] text-wz-muted">Your parcel left the hub and is travelling to your local depot.</p>
    <p class="mt-1 justify-self-start rounded-lg bg-[#f0f5f9] px-2.5 py-[5px] text-[.82rem] font-semibold tabular-nums">Tue 14 Oct, 16:05 · Rotterdam hub</p>
  </section>
  <section class="mt-6 grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="ord-h4">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="ord-h4" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 4 of 5</span>Out for delivery</h3>
    <p class="text-[.92rem] text-wz-muted">Sanne is driving today. You are stop 14 of 22, expected between 12:30 and 14:30.</p>
    <p class="mt-1 justify-self-start rounded-lg bg-[#f0f5f9] px-2.5 py-[5px] text-[.82rem] font-semibold tabular-nums">Thu 16 Oct, 07:30 · Utrecht depot</p>
  </section>
  <section class="mt-6 grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="ord-h5">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="ord-h5" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 5 of 5</span>Delivered</h3>
    <p class="text-[.92rem] text-wz-muted">Left with your neighbour at number 16 and signed for by M. de Vries.</p>
    <p class="mt-1 justify-self-start rounded-lg bg-[#f0f5f9] px-2.5 py-[5px] text-[.82rem] font-semibold tabular-nums">Thu 16 Oct, 13:48 · Utrecht</p>
  </section>

  <div class="mt-6 flex gap-2.5 border-t border-[#e3eaf0] pt-[18px]">
    <button type="button" class="inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-line bg-white text-wz-dark hover:bg-[#f0f9ff]" data-back hidden>Back</button>
    <button type="button" class="ms-auto inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-accent bg-wz-accent text-white hover:bg-wz-dark" data-next hidden>Next</button>
  </div>
</div></div>

<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-[#4a5b6b] uppercase">Progress line with markers</span>
<div class="rounded-[18px] bg-white px-[30px] py-7 text-wz-ink shadow-[0_1px_2px_rgba(12,26,38,.06),0_10px_30px_-12px_rgba(7,89,133,.22)] max-[600px]:px-[18px] max-[600px]:py-6" data-wizard role="group" aria-labelledby="mtg-title">
  <h2 class="text-[1.2rem] leading-[1.35] font-bold tracking-[-.015em]" id="mtg-title">Mortgage pre-approval</h2>
  <p class="mt-0.5 text-[.875rem] text-wz-muted">Halden Bank · Application HB-40718</p>
  <ol class="relative mt-[26px] grid grid-cols-[repeat(var(--wz-n,4),1fr)] before:absolute before:top-1.5 before:right-[calc(50%/var(--wz-n,4))] before:left-[calc(50%/var(--wz-n,4))] before:h-1 before:rounded before:bg-wz-track after:absolute after:top-1.5 after:left-[calc(50%/var(--wz-n,4))] after:h-1 after:w-[calc((100%_-_100%/var(--wz-n,4))*var(--wz-p,0))] after:rounded after:bg-wz-done after:transition-[width] after:duration-400 motion-reduce:after:transition-none" data-stepper aria-label="Progress">
    <li class="group relative z-1 flex flex-col items-center gap-2.5 text-center text-[.8rem] leading-[1.3] text-wz-muted data-[state=current]:font-semibold data-[state=current]:text-wz-ink"><span class="size-4 rounded-full border-2 border-wz-line bg-white transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-4 group-data-[state=current]:border-wz-accent group-data-[state=current]:shadow-[0_0_0_4px_var(--color-wz-soft)] group-data-[state=done]:border-wz-done group-data-[state=done]:bg-wz-done motion-reduce:transition-none"></span>Income<span class="sr-only" data-sr></span></li>
    <li class="group relative z-1 flex flex-col items-center gap-2.5 text-center text-[.8rem] leading-[1.3] text-wz-muted data-[state=current]:font-semibold data-[state=current]:text-wz-ink"><span class="size-4 rounded-full border-2 border-wz-line bg-white transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-4 group-data-[state=current]:border-wz-accent group-data-[state=current]:shadow-[0_0_0_4px_var(--color-wz-soft)] group-data-[state=done]:border-wz-done group-data-[state=done]:bg-wz-done motion-reduce:transition-none"></span>Assets<span class="sr-only" data-sr></span></li>
    <li class="group relative z-1 flex flex-col items-center gap-2.5 text-center text-[.8rem] leading-[1.3] text-wz-muted data-[state=current]:font-semibold data-[state=current]:text-wz-ink"><span class="size-4 rounded-full border-2 border-wz-line bg-white transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-4 group-data-[state=current]:border-wz-accent group-data-[state=current]:shadow-[0_0_0_4px_var(--color-wz-soft)] group-data-[state=done]:border-wz-done group-data-[state=done]:bg-wz-done motion-reduce:transition-none"></span>Credit check<span class="sr-only" data-sr></span></li>
    <li class="group relative z-1 flex flex-col items-center gap-2.5 text-center text-[.8rem] leading-[1.3] text-wz-muted data-[state=current]:font-semibold data-[state=current]:text-wz-ink"><span class="size-4 rounded-full border-2 border-wz-line bg-white transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-4 group-data-[state=current]:border-wz-accent group-data-[state=current]:shadow-[0_0_0_4px_var(--color-wz-soft)] group-data-[state=done]:border-wz-done group-data-[state=done]:bg-wz-done motion-reduce:transition-none"></span>Offer<span class="sr-only" data-sr></span></li>
  </ol>

  <section class="mt-6 grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="mtg-h1">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="mtg-h1" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 1 of 4</span>Your income is verified</h3>
    <p class="text-[.92rem] text-wz-muted">We matched your last three payslips with your bank statements.</p>
    <dl class="mt-1.5 grid grid-cols-2 gap-2.5"><div class="rounded-[10px] bg-[#f0f5f9] px-3 py-2.5"><dt class="text-[.75rem] text-wz-muted">Annual income</dt><dd class="text-[.95rem] font-bold">£64,200</dd></div><div class="rounded-[10px] bg-[#f0f5f9] px-3 py-2.5"><dt class="text-[.75rem] text-wz-muted">Employer</dt><dd class="text-[.95rem] font-bold">Merrow Health Trust</dd></div></dl>
  </section>
  <section class="mt-6 grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="mtg-h2">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="mtg-h2" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 2 of 4</span>Your deposit is confirmed</h3>
    <p class="text-[.92rem] text-wz-muted">Savings held for more than three months count toward the deposit.</p>
    <dl class="mt-1.5 grid grid-cols-2 gap-2.5"><div class="rounded-[10px] bg-[#f0f5f9] px-3 py-2.5"><dt class="text-[.75rem] text-wz-muted">Deposit</dt><dd class="text-[.95rem] font-bold">£48,000</dd></div><div class="rounded-[10px] bg-[#f0f5f9] px-3 py-2.5"><dt class="text-[.75rem] text-wz-muted">Held in</dt><dd class="text-[.95rem] font-bold">Savings and ISA</dd></div></dl>
  </section>
  <section class="mt-6 grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="mtg-h3">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="mtg-h3" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 3 of 4</span>Credit check passed</h3>
    <p class="text-[.92rem] text-wz-muted">A soft search: other lenders can't see it and it doesn't affect your score.</p>
    <dl class="mt-1.5 grid grid-cols-2 gap-2.5"><div class="rounded-[10px] bg-[#f0f5f9] px-3 py-2.5"><dt class="text-[.75rem] text-wz-muted">Result</dt><dd class="text-[.95rem] font-bold">Excellent</dd></div><div class="rounded-[10px] bg-[#f0f5f9] px-3 py-2.5"><dt class="text-[.75rem] text-wz-muted">Checked</dt><dd class="text-[.95rem] font-bold">Wed 22 Oct</dd></div></dl>
  </section>
  <section class="mt-6 grid animate-wz-in gap-2 motion-reduce:animate-none" data-step aria-labelledby="mtg-h4">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="mtg-h4" tabindex="-1"><span class="mb-0.5 block text-[.7rem] font-bold tracking-[.1em] text-wz-dark uppercase">Step 4 of 4</span>You can borrow up to £285,000</h3>
    <p class="text-[.92rem] text-wz-muted">This agreement in principle is valid for 90 days. It is not a final mortgage offer.</p>
    <dl class="mt-1.5 grid grid-cols-2 gap-2.5"><div class="rounded-[10px] bg-[#f0f5f9] px-3 py-2.5"><dt class="text-[.75rem] text-wz-muted">Rate</dt><dd class="text-[.95rem] font-bold">4.19% fixed, 5 years</dd></div><div class="rounded-[10px] bg-[#f0f5f9] px-3 py-2.5"><dt class="text-[.75rem] text-wz-muted">Monthly</dt><dd class="text-[.95rem] font-bold">£1,392 over 30 years</dd></div></dl>
  </section>
  <section class="mt-6 justify-items-center text-center grid animate-wz-in gap-2 motion-reduce:animate-none" data-done hidden aria-labelledby="mtg-h5">
    <span class="mb-1.5 grid size-12 place-items-center rounded-[14px] bg-wz-done-soft text-wz-done"><svg class="size-6 fill-none stroke-current stroke-2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="mtg-h5" tabindex="-1">Offer accepted</h3>
    <p class="text-[.92rem] text-wz-muted">A copy of agreement HB-40718 is in your inbox. An adviser will call within two working days.</p>
    <button type="button" class="mt-2.5 inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-line bg-white text-wz-dark hover:bg-[#f0f9ff]" data-restart>Start over</button>
  </section>

  <div class="mt-6 flex gap-2.5 border-t border-[#e3eaf0] pt-[18px]" data-nav>
    <button type="button" class="inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-line bg-white text-wz-dark hover:bg-[#f0f9ff]" data-back hidden>Back</button>
    <button type="button" class="ms-auto inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-accent bg-wz-accent text-white hover:bg-wz-dark" data-next hidden>Next</button>
    <button type="button" class="ms-auto inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-accent bg-wz-accent text-white hover:bg-wz-dark" data-finish>Accept offer</button>
  </div>
</div></div>

<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-[#4a5b6b] uppercase">Mobile header with a progress bar</span>
<div class="mx-auto w-full max-w-[400px] overflow-hidden rounded-[18px] bg-white text-wz-ink shadow-[0_1px_2px_rgba(12,26,38,.06),0_10px_30px_-12px_rgba(7,89,133,.22)]" data-wizard role="group" aria-labelledby="bank-title">
  <h2 class="sr-only" id="bank-title">Add a bank account</h2>
  <div class="grid grid-cols-[44px_1fr_44px] items-center gap-1.5 px-2.5 pt-2.5 pb-3">
    <button type="button" class="col-start-1 row-start-1 grid size-11 cursor-pointer place-items-center rounded-xl text-wz-ink transition-colors hover:bg-[#f0f5f9] motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none" data-back hidden aria-label="Back"><svg class="size-6 fill-none stroke-current stroke-2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="M19 12H5M11 18l-6-6 6-6"/></svg></button>
    <p class="col-start-2 row-start-1 grid text-center leading-[1.3]" aria-hidden="true"><span class="text-[.75rem] text-wz-muted" data-count>Step 1 of 4</span><b class="text-base" data-name>Choose bank</b></p>
  </div>
  <div class="h-1 bg-wz-track" aria-hidden="true"><span class="block h-full w-[calc((var(--wz-i,0)_+_1)/var(--wz-n,4)*100%)] rounded-r bg-wz-accent transition-[width] duration-400 motion-reduce:transition-none"></span></div>
  <ol class="sr-only" data-stepper aria-label="Progress">
    <li>Choose bank<span data-sr></span></li>
    <li>Sign in<span data-sr></span></li>
    <li>Pick account<span data-sr></span></li>
    <li>Confirm<span data-sr></span></li>
  </ol>

  <section class="grid animate-wz-in gap-2 px-[22px] pt-6 pb-1 motion-reduce:animate-none max-[600px]:px-[18px] max-[600px]:pt-[22px]" data-step aria-labelledby="bank-h1">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="bank-h1" tabindex="-1"><span class="sr-only">Step 1 of 4: </span>Choose your bank</h3>
    <p class="text-[.92rem] text-wz-muted">Pennant pays your invoices out to this account. We connect to 40 UK banks.</p>
    <p class="mt-1 justify-self-start rounded-lg bg-[#f0f5f9] px-2.5 py-[5px] text-[.82rem] font-semibold tabular-nums">Selected: Fernbank</p>
  </section>
  <section class="grid animate-wz-in gap-2 px-[22px] pt-6 pb-1 motion-reduce:animate-none max-[600px]:px-[18px] max-[600px]:pt-[22px]" data-step aria-labelledby="bank-h2">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="bank-h2" tabindex="-1"><span class="sr-only">Step 2 of 4: </span>Sign in to Fernbank</h3>
    <p class="text-[.92rem] text-wz-muted">You approve the link in the Fernbank app. Pennant never sees your password.</p>
    <p class="mt-1 justify-self-start rounded-lg bg-[#f0f5f9] px-2.5 py-[5px] text-[.82rem] font-semibold tabular-nums">Secure Open Banking connection</p>
  </section>
  <section class="grid animate-wz-in gap-2 px-[22px] pt-6 pb-1 motion-reduce:animate-none max-[600px]:px-[18px] max-[600px]:pt-[22px]" data-step aria-labelledby="bank-h3">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="bank-h3" tabindex="-1"><span class="sr-only">Step 3 of 4: </span>Pick an account</h3>
    <p class="text-[.92rem] text-wz-muted">We found two accounts. Payouts will go to the one you choose here.</p>
    <p class="mt-1 justify-self-start rounded-lg bg-[#f0f5f9] px-2.5 py-[5px] text-[.82rem] font-semibold tabular-nums">Everyday account ending 4821</p>
  </section>
  <section class="grid animate-wz-in gap-2 px-[22px] pt-6 pb-1 motion-reduce:animate-none max-[600px]:px-[18px] max-[600px]:pt-[22px]" data-step aria-labelledby="bank-h4">
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="bank-h4" tabindex="-1"><span class="sr-only">Step 4 of 4: </span>Confirm and link</h3>
    <p class="text-[.92rem] text-wz-muted">Payouts arrive the next working day. You can change the account in Settings.</p>
    <p class="mt-1 justify-self-start rounded-lg bg-[#f0f5f9] px-2.5 py-[5px] text-[.82rem] font-semibold tabular-nums">Fernbank Everyday ending 4821</p>
  </section>
  <section class="grid animate-wz-in gap-2 px-[22px] pt-6 pb-1 motion-reduce:animate-none max-[600px]:px-[18px] max-[600px]:pt-[22px] justify-items-center pb-6 text-center max-[600px]:pb-6" data-done hidden aria-labelledby="bank-h5">
    <span class="mb-1.5 grid size-12 place-items-center rounded-[14px] bg-wz-done-soft text-wz-done"><svg class="size-6 fill-none stroke-current stroke-2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>
    <h3 class="text-[1.1rem] leading-[1.35] font-bold tracking-[-.01em] focus:outline-none" id="bank-h5" tabindex="-1">Account linked</h3>
    <p class="text-[.92rem] text-wz-muted">Your next payout, £1,240.00, goes to Everyday ending 4821 on Friday.</p>
    <button type="button" class="mt-2.5 inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-line bg-white text-wz-dark hover:bg-[#f0f9ff]" data-restart>Add another account</button>
  </section>

  <div class="px-[22px] pt-5 pb-[22px] max-[600px]:px-[18px] max-[600px]:pt-[18px] max-[600px]:pb-5" data-nav>
    <button type="button" class="w-full inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-accent bg-wz-accent text-white hover:bg-wz-dark" data-next hidden>Continue</button>
    <button type="button" class="w-full inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] px-5 text-[.95rem] font-semibold transition-colors motion-reduce:transition-none focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none border-wz-accent bg-wz-accent text-white hover:bg-wz-dark" data-finish>Link account</button>
  </div>
</div></div>
CSS
@theme {
  --color-wz-accent: #0369a1;
  --color-wz-dark: #075985;
  --color-wz-soft: #e0f2fe;
  --color-wz-done: #0f766e;
  --color-wz-done-soft: #ccfbf1;
  --color-wz-ink: #0c1a26;
  --color-wz-muted: #4a5b6b;
  --color-wz-line: #7d8d9d;
  --color-wz-track: #dbe4ec;
  --animate-wz-in: wz-in .3s ease;
  @keyframes wz-in { from { opacity: 0; transform: translateY(6px); } }
}
JavaScript
// Stepper wizard: shows one step at a time, marks the stepper with data-state and aria-current="step",
// sets --wz-i / --wz-n / --wz-p (0 to 1) for progress styles and moves focus to the new step's heading.
document.querySelectorAll('[data-wizard]').forEach(function (wz) {
  var panels = wz.querySelectorAll('[data-step]'), steps = wz.querySelectorAll('[data-stepper] > li'), n = panels.length;
  var back = wz.querySelector('[data-back]'), next = wz.querySelector('[data-next]'), finish = wz.querySelector('[data-finish]');
  var done = wz.querySelector('[data-done]'), navs = wz.querySelectorAll('[data-nav]'), at = 0;
  var count = wz.querySelector('[data-count]'), name = wz.querySelector('[data-name]');
  function mark(i) {
    var k = Math.min(i, n - 1);
    steps.forEach(function (s, j) {
      s.dataset.state = j < i ? 'done' : j === i ? 'current' : 'next';
      if (j === i) s.setAttribute('aria-current', 'step'); else s.removeAttribute('aria-current');
      s.querySelector('[data-sr]').textContent = j < i ? ', completed' : '';
    });
    wz.style.setProperty('--wz-i', k); wz.style.setProperty('--wz-n', n); wz.style.setProperty('--wz-p', n > 1 ? k / (n - 1) : 1);
    if (count) count.textContent = 'Step ' + (k + 1) + ' of ' + n;
    if (name) name.textContent = steps[k].firstChild.textContent.trim(); // the li's leading text is the step name
  }
  function go(i, focus) {
    at = i; mark(i);
    panels.forEach(function (p, j) { p.hidden = j !== i; });
    if (back) back.hidden = i === 0;
    next.hidden = i === n - 1;
    if (finish) finish.hidden = !next.hidden;
    if (focus) panels[i].querySelector('[tabindex="-1"]').focus();
  }
  if (back) back.addEventListener('click', function () { go(at - 1, true); });
  next.addEventListener('click', function () { go(at + 1, true); });
  // Last step: do the real thing here (navigate, save). The demo shows the done panel instead.
  if (finish && done) finish.addEventListener('click', function () {
    panels[at].hidden = true; finish.hidden = true; if (back) back.hidden = true; mark(n);
    navs.forEach(function (x) { x.hidden = true; });
    done.hidden = false; done.querySelector('[tabindex="-1"]').focus();
  });
  if (done) done.querySelector('[data-restart]').addEventListener('click', function () {
    done.hidden = true; navs.forEach(function (x) { x.hidden = false; }); go(0, true);
  });
  go(0);
});

About this set

Four compact stepper designs, each shown as a small working wizard with Back and Next buttons. Dots is a product tour for a notes app: five small dots where the current one stretches into a pill and finished ones turn teal, with the step names kept for screen readers only. Icons with labels under them is an order tracker with an icon in each circle, connector lines that fill as steps complete and a timestamp line in every panel; on narrow screens only the current label stays visible. Progress line with markers is a mortgage pre-approval with a thin track that fills to the current marker, its width driven by a custom property. Mobile header is an app-style bank account flow with a back arrow, a centred step count over the step name and a thin progress bar, while the real list of steps stays in the page for assistive technology. Use dots for short tours, icons for status and tracking, the line for longer checks with few steps, and the header on small screens. One short vanilla script drives all four: it shows one panel at a time, sets data-state and aria-current on the stepper, writes the progress custom properties and moves focus to each new heading. Without JavaScript every step is visible.

What’s included

  • Four stepper designs driven by one identical vanilla script
  • Progress width comes from --wz-p, --wz-i and --wz-n custom properties set by the script
  • Done states in teal, the current step in sky blue, upcoming steps in grey
  • Finish button and success panel with a restart action on three of the four wizards
  • Labels collapse to the current step at phone width, with no horizontal scrolling
  • All steps show stacked when JavaScript is off

Accessibility

Each stepper is an ordered list labelled Progress, with aria-current="step" on the current item and a visually hidden ", completed" on finished ones. Each step heading starts with its own "Step 2 of 4" text and receives focus on Back and Next, so screen readers announce where you are. In the mobile header the visible step count and progress bar are aria-hidden and the back arrow has an aria-label, so progress is announced once. Buttons have a visible focus ring and transitions are turned off under prefers-reduced-motion.

Customise it

Change the --wz-accent, --wz-done, --wz-soft and --wz-line custom properties on .wz (plain CSS and Bootstrap) or the --color-wz-* values in the Tailwind @theme block. Bootstrap buttons and the progress bar are restyled through their --bs-btn-* and --bs-progress-* variables.