Open demo

CSS code

HTML
<div><span class="lbl">Numbered stepper, labels below</span>
<form class="wz" data-wizard aria-labelledby="ws-title">
  <h2 class="wz-title" id="ws-title">Create your workspace</h2>
  <p class="wz-sub">Four quick steps. You can change all of this later in Settings.</p>
  <ol class="wz-steps" data-stepper aria-label="Progress">
    <li><span class="wz-dot"><b>1</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>Workspace<span class="sr-only" data-sr></span></li>
    <li><span class="wz-dot"><b>2</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>Team<span class="sr-only" data-sr></span></li>
    <li><span class="wz-dot"><b>3</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>Plan<span class="sr-only" data-sr></span></li>
    <li><span class="wz-dot"><b>4</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>Review<span class="sr-only" data-sr></span></li>
  </ol>

  <section class="wz-panel" data-step aria-labelledby="ws-h1">
    <h3 class="wz-h" id="ws-h1" tabindex="-1"><span class="wz-kick">Step 1 of 4</span>Name your workspace</h3>
    <p class="wz-lead">This is where your team's boards and reports live.</p>
    <div class="wz-field"><label for="ws-name">Workspace name</label>
      <input class="wz-input" id="ws-name" name="workspace" required autocomplete="organization" value="Harbor Lane Studio"></div>
    <div class="wz-field"><label for="ws-url">Workspace address</label>
      <div class="wz-affix"><span>tallyboard.app/</span><input class="wz-input" id="ws-url" name="address" required pattern="[a-z0-9\-]+" aria-describedby="ws-url-hint" value="harbor-lane"></div>
      <span class="wz-hint" id="ws-url-hint">Lowercase letters, numbers and hyphens.</span></div>
  </section>

  <section class="wz-panel" data-step aria-labelledby="ws-h2">
    <h3 class="wz-h" id="ws-h2" tabindex="-1"><span class="wz-kick">Step 2 of 4</span>Invite your team</h3>
    <p class="wz-lead">They get an email with a link to join. Leave it empty to skip.</p>
    <div class="wz-field"><label for="ws-emails">Email addresses</label>
      <input class="wz-input" id="ws-emails" name="invites" type="email" multiple aria-describedby="ws-emails-hint" placeholder="[email protected], [email protected]">
      <span class="wz-hint" id="ws-emails-hint">Separate addresses with commas.</span></div>
    <div class="wz-field"><label for="ws-role">They join as</label>
      <select class="wz-input" id="ws-role" name="role"><option>Editor</option><option>Viewer</option><option>Admin</option></select></div>
  </section>

  <section class="wz-panel" data-step aria-labelledby="ws-h3">
    <h3 class="wz-h" id="ws-h3" tabindex="-1"><span class="wz-kick">Step 3 of 4</span>Choose a plan</h3>
    <fieldset class="wz-field"><legend class="wz-legend">Plan</legend>
      <div class="wz-tiles">
        <label class="wz-tile"><input type="radio" name="plan" value="Starter" required><b>Starter</b><span>Free · up to 3 people</span></label>
        <label class="wz-tile"><input type="radio" name="plan" value="Team" checked><b>Team</b><span>$12 per seat a month</span></label>
        <label class="wz-tile"><input type="radio" name="plan" value="Business"><b>Business</b><span>$24 per seat a month</span></label>
      </div></fieldset>
  </section>

  <section class="wz-panel" data-step aria-labelledby="ws-h4">
    <h3 class="wz-h" id="ws-h4" tabindex="-1"><span class="wz-kick">Step 4 of 4</span>Review and create</h3>
    <dl class="wz-review">
      <div><dt>Workspace</dt><dd data-out="workspace">Harbor Lane Studio</dd></div>
      <div><dt>Address</dt><dd>tallyboard.app/<span data-out="address">harbor-lane</span></dd></div>
      <div><dt>Invites</dt><dd data-out="invites">None</dd></div>
      <div><dt>Plan</dt><dd data-out="plan">Team</dd></div>
    </dl>
  </section>

  <section class="wz-panel wz-done" data-done hidden aria-labelledby="ws-h5">
    <span class="wz-dot"><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="ws-h5" tabindex="-1">Your workspace is ready</h3>
    <p class="wz-lead" style="margin:0">We sent the invites. Your 14-day Team trial starts today.</p>
    <button type="button" class="wz-btn ghost" data-restart>Start over</button>
  </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>Continue</button>
    <button type="submit" class="wz-btn">Create workspace</button>
  </div>
</form></div>

<div><span class="lbl">Compact bar, labels beside numbers</span>
<form class="wz" data-wizard aria-labelledby="job-title">
  <h2 class="wz-title" id="job-title">Post a job</h2>
  <p class="wz-sub">Northwind Robotics · Careers</p>
  <ol class="wz-bar" data-stepper aria-label="Progress">
    <li><span class="wz-dot"><b>1</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span><span class="wz-name">Role</span><span class="sr-only" data-sr></span></li>
    <li><span class="wz-dot"><b>2</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span><span class="wz-name">Details</span><span class="sr-only" data-sr></span></li>
    <li><span class="wz-dot"><b>3</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span><span class="wz-name">Publish</span><span class="sr-only" data-sr></span></li>
  </ol>

  <section class="wz-panel" data-step aria-labelledby="job-h1">
    <h3 class="wz-h" id="job-h1" tabindex="-1"><span class="wz-kick">Step 1 of 3</span>What's the role?</h3>
    <div class="wz-row">
      <div class="wz-field"><label for="job-name">Job title</label><input class="wz-input" id="job-name" name="title" required value="Firmware Engineer"></div>
      <div class="wz-field"><label for="job-team">Team</label>
        <select class="wz-input" id="job-team" name="team"><option>Embedded systems</option><option>Perception</option><option>Manufacturing</option></select></div>
    </div>
    <fieldset class="wz-field"><legend class="wz-legend">Employment type</legend>
      <div class="wz-seg">
        <label><input type="radio" name="type" value="Full-time" checked>Full-time</label>
        <label><input type="radio" name="type" value="Part-time">Part-time</label>
        <label><input type="radio" name="type" value="Contract">Contract</label>
      </div></fieldset>
  </section>

  <section class="wz-panel" data-step aria-labelledby="job-h2">
    <h3 class="wz-h" id="job-h2" tabindex="-1"><span class="wz-kick">Step 2 of 3</span>Location and pay</h3>
    <div class="wz-row">
      <div class="wz-field"><label for="job-loc">Location</label><input class="wz-input" id="job-loc" name="location" required value="Pittsburgh, PA · Hybrid"></div>
      <div class="wz-field"><label for="job-min">Salary from (USD)</label><input class="wz-input" id="job-min" name="min" type="number" min="0" step="1000" value="128000"></div>
      <div class="wz-field"><label for="job-max">Salary to (USD)</label><input class="wz-input" id="job-max" name="max" type="number" min="0" step="1000" value="156000"></div>
    </div>
    <div class="wz-field"><label for="job-desc">Short description</label>
      <textarea class="wz-input" id="job-desc" name="description" required minlength="40">You will write and test the motor-control firmware for our warehouse picking arms, from bring-up to field updates.</textarea></div>
  </section>

  <section class="wz-panel" data-step aria-labelledby="job-h3">
    <h3 class="wz-h" id="job-h3" tabindex="-1"><span class="wz-kick">Step 3 of 3</span>Where should it appear?</h3>
    <fieldset class="wz-field"><legend class="wz-legend">Post to</legend>
      <div class="wz-seg">
        <label><input type="checkbox" name="boards" value="Careers page" checked>Careers page</label>
        <label><input type="checkbox" name="boards" value="LinkedIn" checked>LinkedIn</label>
        <label><input type="checkbox" name="boards" value="Robotics job boards">Robotics job boards</label>
      </div></fieldset>
    <div class="wz-field" style="max-width:260px"><label for="job-close">Closing date</label><input class="wz-input" id="job-close" name="closes" type="date" required value="2026-11-30"></div>
  </section>

  <section class="wz-panel wz-done" data-done hidden aria-labelledby="job-h4">
    <span class="wz-dot"><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="job-h4" tabindex="-1">Job posted</h3>
    <p class="wz-lead" style="margin:0">It goes live on your careers page in about a minute.</p>
    <button type="button" class="wz-btn ghost" data-restart>Post another job</button>
  </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>Continue</button>
    <button type="submit" class="wz-btn">Publish job</button>
  </div>
</form></div>
CSS
/* Horizontal wizard: an <ol> stepper, one <section data-step> per step and a shared Back / Continue bar.
   The script hides every step but the current one, sets data-state and aria-current="step" on the stepper
   and moves focus to the new step's heading. Without JavaScript every step shows and the form submits normally. */
.wz{--wz-accent:#4f46e5;--wz-accent-dark:#3730a3;--wz-soft:#e0e7ff;--wz-ink:#111827;--wz-muted:#555c70;--wz-line:#8b91a7;--wz-field:#7c8398;
  background:#fff;border-radius:18px;padding:30px 32px;box-shadow:0 1px 2px rgba(17,24,39,.06),0 10px 30px -12px rgba(49,46,129,.18);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.3rem;font-weight:700;letter-spacing:-.015em;margin:0}
.wz-sub{margin:4px 0 0;color:var(--wz-muted);font-size:.92rem}

/* stepper: numbered dots with labels under them, joined by a line that fills as steps complete */
.wz-steps{list-style:none;margin:26px 0 0;padding:0;display:grid;grid-template-columns:repeat(var(--wz-n,4),1fr)}
.wz-steps li{position:relative;display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center;font-size:.8rem;color:var(--wz-muted)}
.wz-steps li + li::before{content:"";position:absolute;top:17px;left:calc(-50% + 26px);right:calc(50% + 26px);height:2px;border-radius:2px;background:var(--wz-line);transition:background .25s}
.wz-dot{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;border:2px solid var(--wz-line);background:#fff;font-weight:700;font-size:.9rem;color:#4b5263;transition:background .25s,border-color .25s,box-shadow .25s}
.wz-dot svg{display:none;width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.wz-steps [data-state="current"]{color:var(--wz-ink);font-weight:600}
.wz-steps [data-state="current"] .wz-dot{border-color:var(--wz-accent);color:var(--wz-accent-dark);box-shadow:0 0 0 5px var(--wz-soft)}
.wz-steps [data-state="done"] .wz-dot{border-color:var(--wz-accent);background:var(--wz-accent);color:#fff}
.wz-steps [data-state="done"] .wz-dot b{display:none}
.wz-steps [data-state="done"] .wz-dot svg{display:block}
.wz-steps [data-state="done"] + li::before,.wz-steps [data-state="current"]::before{background:var(--wz-accent)}

/* compact bar: number and label side by side; narrow screens keep only the current label */
.wz-bar{list-style:none;margin:22px 0 0;padding:5px;display:flex;gap:4px;background:#f1f2f9;border-radius:14px}
.wz-bar li{flex:1;display:flex;align-items:center;justify-content:center;gap:9px;padding:9px 10px;border-radius:10px;font-size:.88rem;color:var(--wz-muted);transition:background .25s,box-shadow .25s}
.wz-bar .wz-dot{width:26px;height:26px;border-width:2px;font-size:.78rem;flex:none}
.wz-bar .wz-dot svg{width:14px;height:14px}
.wz-bar [data-state="current"]{background:#fff;color:var(--wz-ink);font-weight:600;box-shadow:0 1px 3px rgba(17,24,39,.12)}
.wz-bar [data-state="current"] .wz-dot{border-color:var(--wz-accent);background:var(--wz-accent);color:#fff}
.wz-bar [data-state="done"] .wz-dot{border-color:var(--wz-accent);background:var(--wz-soft);color:var(--wz-accent-dark)}
.wz-bar [data-state="done"] .wz-dot b{display:none}
.wz-bar [data-state="done"] .wz-dot svg{display:block}
@media (max-width:520px){.wz-bar li{flex:none}.wz-bar [data-state="current"]{flex:1}
  .wz-bar [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}}

/* step panels */
.wz-panel{margin-top:28px;display:grid;gap:18px;animation:wz-in .3s ease}
@keyframes wz-in{from{opacity:0;transform:translateY(6px)}}
.wz-h{margin:0;font-size:1.15rem;font-weight:700;letter-spacing:-.01em}
.wz-h:focus{outline:none}
.wz-kick{display:block;font-size:.72rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--wz-accent-dark);margin-bottom:2px}
.wz-lead{margin:-10px 0 0;color:var(--wz-muted);font-size:.92rem}
.wz-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px}
.wz-field{display:grid;gap:6px;min-width:0}
.wz-field > label,.wz-legend{font-size:.88rem;font-weight:600}
.wz-hint{font-size:.8rem;color:var(--wz-muted)}
.wz-input{width:100%;font:inherit;font-size:.95rem;color:var(--wz-ink);background:#fff;border:1.5px solid var(--wz-field);border-radius:10px;padding:10px 12px;min-height:44px}
textarea.wz-input{min-height:96px;resize:vertical}
.wz-input:focus-visible{outline:none;border-color:var(--wz-accent);box-shadow:0 0 0 4px var(--wz-soft)}
.wz-affix{display:flex;align-items:stretch;border:1.5px solid var(--wz-field);border-radius:10px;overflow:hidden}
.wz-affix span{display:flex;align-items:center;padding:0 12px;background:#f4f5fa;color:#4b5263;font-size:.9rem;border-right:1.5px solid var(--wz-field)}
.wz-affix .wz-input{border:0;border-radius:0}
.wz-affix:has(:focus-visible){border-color:var(--wz-accent);box-shadow:0 0 0 4px var(--wz-soft)}
.wz-affix .wz-input:focus-visible{box-shadow:none}
fieldset.wz-field{border:0;margin:0;padding:0}
.wz-legend{padding:0;margin-bottom:6px}

/* plan tiles: a radio inside each label, the tile outline follows :has(:checked) */
.wz-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}
.wz-tile{position:relative;display:grid;gap:2px;padding:14px 14px 14px 44px;border:1.5px solid var(--wz-field);border-radius:12px;cursor:pointer;transition:border-color .2s,background .2s}
.wz-tile input{position:absolute;left:14px;top:16px;width:18px;height:18px;margin:0;accent-color:var(--wz-accent)}
.wz-tile input:focus-visible{outline:2px solid var(--wz-accent);outline-offset:3px}
.wz-tile b{font-size:.95rem}
.wz-tile span{font-size:.82rem;color:var(--wz-muted)}
.wz-tile:has(:checked){border-color:var(--wz-accent);background:#f5f6ff}
.wz-seg{display:flex;flex-wrap:wrap;gap:8px}
.wz-seg label{position:relative;display:flex;align-items:center;gap:8px;padding:9px 14px;border:1.5px solid var(--wz-field);border-radius:999px;font-size:.9rem;cursor:pointer}
.wz-seg input{width:16px;height:16px;margin:0;accent-color:var(--wz-accent)}
.wz-seg input:focus-visible{outline:2px solid var(--wz-accent);outline-offset:2px}
.wz-seg label:has(:checked){border-color:var(--wz-accent);background:#f5f6ff;color:var(--wz-accent-dark);font-weight:600}

/* review list */
.wz-review{margin:0;display:grid;border:1px solid #e3e5ef;border-radius:12px;overflow:hidden}
.wz-review div{display:flex;justify-content:space-between;gap:16px;padding:11px 14px;font-size:.92rem}
.wz-review div + div{border-top:1px solid #e3e5ef}
.wz-review dt{color:var(--wz-muted)}
.wz-review dd{margin:0;font-weight:600;text-align:right;overflow-wrap:anywhere}

/* Back / Continue bar */
.wz-nav{display:flex;gap:10px;margin-top:28px;padding-top:20px;border-top:1px solid #e8eaf2}
.wz-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;padding:0 22px;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,box-shadow .2s}
.wz-btn:hover{background:var(--wz-accent-dark)}
.wz-btn: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:#f5f6ff}
.wz-nav .wz-btn:not(.ghost){margin-left:auto}
.wz-done{text-align:center;justify-items:center;padding:12px 0 4px}
.wz-done .wz-dot{width:56px;height:56px;border:0;background:var(--wz-soft);color:var(--wz-accent-dark)}
.wz-done .wz-dot svg{display:block;width:26px;height:26px}
@media (prefers-reduced-motion:reduce){.wz *,.wz *::before{transition:none!important;animation:none!important}}
@media (max-width:600px){.wz{padding:24px 18px}.wz-steps li{font-size:.72rem}}
JavaScript
// Form wizard: shows one step at a time, marks the stepper with data-state and aria-current="step",
// checks the step's fields before moving on 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');
  var back = wz.querySelector('[data-back]'), next = wz.querySelector('[data-next]');
  var submit = wz.querySelector('[type="submit"]'), done = wz.querySelector('[data-done]'), at = 0;
  function mark(i) {
    steps.forEach(function (s, n) {
      s.dataset.state = n < i ? 'done' : n === i ? 'current' : 'next';
      if (n === i) s.setAttribute('aria-current', 'step'); else s.removeAttribute('aria-current');
      s.querySelector('[data-sr]').textContent = n < i ? ', completed' : '';
    });
  }
  function go(i, focus) {
    at = i; mark(i);
    panels.forEach(function (p, n) { p.hidden = n !== i; });
    back.hidden = i === 0; next.hidden = i === panels.length - 1; submit.hidden = !next.hidden;
    wz.querySelectorAll('[data-out]').forEach(function (o) {
      var f = wz.elements[o.dataset.out], v = f && (f.length && !f.options ? [].filter.call(f, function (x) { return x.checked; }).map(function (x) { return x.value; }).join(', ') : f.value);
      o.textContent = v || 'None';
    });
    if (focus) panels[i].querySelector('[tabindex="-1"]').focus();
  }
  function valid() {
    var bad = [].find.call(panels[at].querySelectorAll('input, select, textarea'), function (f) { return !f.checkValidity(); });
    if (bad) bad.reportValidity();
    return !bad;
  }
  wz.noValidate = true;
  back.addEventListener('click', function () { go(at - 1, true); });
  next.addEventListener('click', function () { if (valid()) go(at + 1, true); });
  wz.addEventListener('submit', function (e) {
    e.preventDefault();
    if (!valid()) return;
    if (at < panels.length - 1) return go(at + 1, true);
    // Last step: send the form here (fetch or wz.submit()). The demo shows the done panel instead.
    panels[at].hidden = true; back.parentNode.hidden = true; mark(steps.length);
    done.hidden = false; done.querySelector('[tabindex="-1"]').focus();
  });
  if (done) done.querySelector('[data-restart]').addEventListener('click', function () {
    wz.reset(); done.hidden = true; back.parentNode.hidden = false; go(0, true);
  });
  go(0);
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Numbered stepper, labels below</span>
<form class="wz card" data-wizard aria-labelledby="ws-title"><div class="card-body">
  <h2 class="wz-title fw-bold mb-0" id="ws-title">Create your workspace</h2>
  <p class="wz-sub text-body-secondary mt-1 mb-0">Four quick steps. You can change all of this later in Settings.</p>
  <ol class="wz-steps" data-stepper aria-label="Progress">
    <li><span class="wz-dot"><b>1</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>Workspace<span class="visually-hidden" data-sr></span></li>
    <li><span class="wz-dot"><b>2</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>Team<span class="visually-hidden" data-sr></span></li>
    <li><span class="wz-dot"><b>3</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>Plan<span class="visually-hidden" data-sr></span></li>
    <li><span class="wz-dot"><b>4</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>Review<span class="visually-hidden" data-sr></span></li>
  </ol>

  <section class="wz-panel" data-step aria-labelledby="ws-h1">
    <h3 class="wz-h fw-bold mb-0" id="ws-h1" tabindex="-1"><span class="wz-kick">Step 1 of 4</span>Name your workspace</h3>
    <p class="wz-lead">This is where your team's boards and reports live.</p>
    <div><label class="form-label" for="ws-name">Workspace name</label>
      <input class="form-control" id="ws-name" name="workspace" required autocomplete="organization" value="Harbor Lane Studio"></div>
    <div><label class="form-label" for="ws-url">Workspace address</label>
      <div class="input-group"><span class="input-group-text">tallyboard.app/</span><input class="form-control" id="ws-url" name="address" required pattern="[a-z0-9\-]+" aria-describedby="ws-url-hint" value="harbor-lane"></div>
      <div class="form-text mt-1" id="ws-url-hint">Lowercase letters, numbers and hyphens.</div></div>
  </section>

  <section class="wz-panel" data-step aria-labelledby="ws-h2">
    <h3 class="wz-h fw-bold mb-0" id="ws-h2" tabindex="-1"><span class="wz-kick">Step 2 of 4</span>Invite your team</h3>
    <p class="wz-lead">They get an email with a link to join. Leave it empty to skip.</p>
    <div><label class="form-label" for="ws-emails">Email addresses</label>
      <input class="form-control" id="ws-emails" name="invites" type="email" multiple aria-describedby="ws-emails-hint" placeholder="[email protected], [email protected]">
      <div class="form-text mt-1" id="ws-emails-hint">Separate addresses with commas.</div></div>
    <div><label class="form-label" for="ws-role">They join as</label>
      <select class="form-select" id="ws-role" name="role"><option>Editor</option><option>Viewer</option><option>Admin</option></select></div>
  </section>

  <section class="wz-panel" data-step aria-labelledby="ws-h3">
    <h3 class="wz-h fw-bold mb-0" id="ws-h3" tabindex="-1"><span class="wz-kick">Step 3 of 4</span>Choose a plan</h3>
    <fieldset><legend>Plan</legend>
      <div class="row g-2">
        <div class="col-sm-4"><label class="wz-tile h-100"><input class="form-check-input" type="radio" name="plan" value="Starter" required><b>Starter</b><span>Free · up to 3 people</span></label></div>
        <div class="col-sm-4"><label class="wz-tile h-100"><input class="form-check-input" type="radio" name="plan" value="Team" checked><b>Team</b><span>$12 per seat a month</span></label></div>
        <div class="col-sm-4"><label class="wz-tile h-100"><input class="form-check-input" type="radio" name="plan" value="Business"><b>Business</b><span>$24 per seat a month</span></label></div>
      </div></fieldset>
  </section>

  <section class="wz-panel" data-step aria-labelledby="ws-h4">
    <h3 class="wz-h fw-bold mb-0" id="ws-h4" tabindex="-1"><span class="wz-kick">Step 4 of 4</span>Review and create</h3>
    <dl class="list-group mb-0">
      <div class="list-group-item d-flex justify-content-between gap-3"><dt>Workspace</dt><dd data-out="workspace">Harbor Lane Studio</dd></div>
      <div class="list-group-item d-flex justify-content-between gap-3"><dt>Address</dt><dd>tallyboard.app/<span data-out="address">harbor-lane</span></dd></div>
      <div class="list-group-item d-flex justify-content-between gap-3"><dt>Invites</dt><dd data-out="invites">None</dd></div>
      <div class="list-group-item d-flex justify-content-between gap-3"><dt>Plan</dt><dd data-out="plan">Team</dd></div>
    </dl>
  </section>

  <section class="wz-panel wz-done" data-done hidden aria-labelledby="ws-h5">
    <span class="wz-dot"><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="ws-h5" tabindex="-1">Your workspace is ready</h3>
    <p class="text-body-secondary small mb-0">We sent the invites. Your 14-day Team trial starts today.</p>
    <button type="button" class="btn btn-outline-secondary" data-restart>Start over</button>
  </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>Continue</button>
    <button type="submit" class="btn btn-primary">Create workspace</button>
  </div>
</div></form></div>

<div><span class="lbl">Compact bar, labels beside numbers</span>
<form class="wz card" data-wizard aria-labelledby="job-title"><div class="card-body">
  <h2 class="wz-title fw-bold mb-0" id="job-title">Post a job</h2>
  <p class="wz-sub text-body-secondary mt-1 mb-0">Northwind Robotics · Careers</p>
  <ol class="wz-bar" data-stepper aria-label="Progress">
    <li><span class="wz-dot"><b>1</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span><span class="wz-name">Role</span><span class="visually-hidden" data-sr></span></li>
    <li><span class="wz-dot"><b>2</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span><span class="wz-name">Details</span><span class="visually-hidden" data-sr></span></li>
    <li><span class="wz-dot"><b>3</b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span><span class="wz-name">Publish</span><span class="visually-hidden" data-sr></span></li>
  </ol>

  <section class="wz-panel" data-step aria-labelledby="job-h1">
    <h3 class="wz-h fw-bold mb-0" id="job-h1" tabindex="-1"><span class="wz-kick">Step 1 of 3</span>What's the role?</h3>
    <div class="row g-3">
      <div class="col-sm-6"><label class="form-label" for="job-name">Job title</label><input class="form-control" id="job-name" name="title" required value="Firmware Engineer"></div>
      <div class="col-sm-6"><label class="form-label" for="job-team">Team</label>
        <select class="form-select" id="job-team" name="team"><option>Embedded systems</option><option>Perception</option><option>Manufacturing</option></select></div>
    </div>
    <fieldset><legend>Employment type</legend>
      <div class="wz-seg d-flex flex-wrap gap-2">
        <label><input class="form-check-input" type="radio" name="type" value="Full-time" checked>Full-time</label>
        <label><input class="form-check-input" type="radio" name="type" value="Part-time">Part-time</label>
        <label><input class="form-check-input" type="radio" name="type" value="Contract">Contract</label>
      </div></fieldset>
  </section>

  <section class="wz-panel" data-step aria-labelledby="job-h2">
    <h3 class="wz-h fw-bold mb-0" id="job-h2" tabindex="-1"><span class="wz-kick">Step 2 of 3</span>Location and pay</h3>
    <div class="row g-3">
      <div class="col-md-4"><label class="form-label" for="job-loc">Location</label><input class="form-control" id="job-loc" name="location" required value="Pittsburgh, PA · Hybrid"></div>
      <div class="col-sm-6 col-md-4"><label class="form-label" for="job-min">Salary from (USD)</label><input class="form-control" id="job-min" name="min" type="number" min="0" step="1000" value="128000"></div>
      <div class="col-sm-6 col-md-4"><label class="form-label" for="job-max">Salary to (USD)</label><input class="form-control" id="job-max" name="max" type="number" min="0" step="1000" value="156000"></div>
    </div>
    <div><label class="form-label" for="job-desc">Short description</label>
      <textarea class="form-control" id="job-desc" name="description" rows="3" required minlength="40">You will write and test the motor-control firmware for our warehouse picking arms, from bring-up to field updates.</textarea></div>
  </section>

  <section class="wz-panel" data-step aria-labelledby="job-h3">
    <h3 class="wz-h fw-bold mb-0" id="job-h3" tabindex="-1"><span class="wz-kick">Step 3 of 3</span>Where should it appear?</h3>
    <fieldset><legend>Post to</legend>
      <div class="wz-seg d-flex flex-wrap gap-2">
        <label><input class="form-check-input" type="checkbox" name="boards" value="Careers page" checked>Careers page</label>
        <label><input class="form-check-input" type="checkbox" name="boards" value="LinkedIn" checked>LinkedIn</label>
        <label><input class="form-check-input" type="checkbox" name="boards" value="Robotics job boards">Robotics job boards</label>
      </div></fieldset>
    <div style="max-width:260px"><label class="form-label" for="job-close">Closing date</label><input class="form-control" id="job-close" name="closes" type="date" required value="2026-11-30"></div>
  </section>

  <section class="wz-panel wz-done" data-done hidden aria-labelledby="job-h4">
    <span class="wz-dot"><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="job-h4" tabindex="-1">Job posted</h3>
    <p class="text-body-secondary small mb-0">It goes live on your careers page in about a minute.</p>
    <button type="button" class="btn btn-outline-secondary" data-restart>Post another job</button>
  </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>Continue</button>
    <button type="submit" class="btn btn-primary">Publish job</button>
  </div>
</div></form></div>
CSS
/* Horizontal wizard on Bootstrap 5: .card, .form-control, .form-select, .input-group, .form-check-input,
   .list-group and .btn, restyled through Bootstrap's CSS variables. The stepper is the only custom part.
   The script hides every step but the current one, sets data-state and aria-current="step" on the stepper
   and moves focus to the new step's heading. Without JavaScript every step shows and the form submits normally. */
.wz{--wz-accent:#4f46e5;--wz-accent-dark:#3730a3;--wz-soft:#e0e7ff;--wz-muted:#555c70;--wz-line:#8b91a7;
  --bs-border-color:#7c8398;--bs-border-radius:10px;--bs-body-color:#111827;--bs-secondary-color:#555c70;
  border:0;border-radius:18px;box-shadow:0 1px 2px rgba(17,24,39,.06),0 10px 30px -12px rgba(49,46,129,.18)}
.wz .card-body{padding:30px 32px}
.wz .form-control,.wz .form-select{border-width:1.5px;min-height:44px;padding:10px 12px;font-size:.95rem}
.wz h2,.wz h3{line-height:1.5}
.wz .form-select{padding-right:36px}
.wz .form-control:focus,.wz .form-select:focus{border-color:var(--wz-accent);box-shadow:0 0 0 4px var(--wz-soft)}
.wz .input-group-text{border-width:1.5px;background:#f4f5fa;color:#4b5263;font-size:.9rem}
.wz .form-label,.wz legend{font-size:.88rem;font-weight:600;margin-bottom:6px}
.wz .form-text{font-size:.8rem;color:var(--wz-muted);margin-top:0}
.wz .form-check-input{border-width:1.5px;border-color:var(--wz-line)}
.wz .form-check-input:checked{background-color:var(--wz-accent);border-color:var(--wz-accent)}
.wz .form-check-input:focus{border-color:var(--wz-accent);box-shadow:0 0 0 3px var(--wz-soft)}
.wz .form-check-input:focus-visible{outline:2px solid var(--wz-accent);outline-offset:2px}
.wz .btn{--bs-btn-padding-x:22px;--bs-btn-font-size:.95rem;--bs-btn-font-weight:600;--bs-btn-border-width:1.5px;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-hover-bg:#f5f6ff;--bs-btn-hover-color:var(--wz-accent-dark);--bs-btn-hover-border-color:var(--wz-line);--bs-btn-active-bg:#eef0ff;--bs-btn-active-color:var(--wz-accent-dark);--bs-btn-bg:#fff}
.wz .btn:focus-visible{box-shadow:0 0 0 3px #fff,0 0 0 5px var(--wz-accent);outline:0}
.wz .list-group{--bs-list-group-border-color:#e3e5ef;--bs-list-group-border-radius:12px;font-size:.92rem}
.wz .list-group dt{font-weight:400;color:var(--wz-muted)}
.wz .list-group dd{margin:0;font-weight:600;text-align:right;overflow-wrap:anywhere}
.wz-title{font-size:1.3rem;letter-spacing:-.015em}
.wz-sub{font-size:.92rem}

/* stepper: numbered dots with labels under them, joined by a line that fills as steps complete */
.wz-steps{list-style:none;margin:26px 0 0;padding:0;display:grid;grid-template-columns:repeat(4,1fr)}
.wz-steps li{position:relative;display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center;font-size:.8rem;color:var(--wz-muted)}
.wz-steps li + li::before{content:"";position:absolute;top:17px;left:calc(-50% + 26px);right:calc(50% + 26px);height:2px;border-radius:2px;background:var(--wz-line);transition:background .25s}
.wz-dot{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;border:2px solid var(--wz-line);background:#fff;font-weight:700;font-size:.9rem;color:#4b5263;transition:background .25s,border-color .25s,box-shadow .25s}
.wz-dot svg{display:none;width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.wz-steps [data-state="current"]{color:#111827;font-weight:600}
.wz-steps [data-state="current"] .wz-dot{border-color:var(--wz-accent);color:var(--wz-accent-dark);box-shadow:0 0 0 5px var(--wz-soft)}
.wz-steps [data-state="done"] .wz-dot{border-color:var(--wz-accent);background:var(--wz-accent);color:#fff}
.wz [data-state="done"] .wz-dot b{display:none}
.wz [data-state="done"] .wz-dot svg{display:block}
.wz-steps [data-state="done"] + li::before,.wz-steps [data-state="current"]::before{background:var(--wz-accent)}

/* compact bar: number and label side by side; narrow screens keep only the current label */
.wz-bar{list-style:none;margin:22px 0 0;padding:5px;display:flex;gap:4px;background:#f1f2f9;border-radius:14px}
.wz-bar li{flex:1;display:flex;align-items:center;justify-content:center;gap:9px;padding:9px 10px;border-radius:10px;font-size:.88rem;color:var(--wz-muted);transition:background .25s,box-shadow .25s}
.wz-bar .wz-dot{width:26px;height:26px;font-size:.78rem;flex:none}
.wz-bar .wz-dot svg{width:14px;height:14px}
.wz-bar [data-state="current"]{background:#fff;color:#111827;font-weight:600;box-shadow:0 1px 3px rgba(17,24,39,.12)}
.wz-bar [data-state="current"] .wz-dot{border-color:var(--wz-accent);background:var(--wz-accent);color:#fff}
.wz-bar [data-state="done"] .wz-dot{border-color:var(--wz-accent);background:var(--wz-soft);color:var(--wz-accent-dark)}
@media (max-width:520px){.wz-bar li{flex:none}.wz-bar [data-state="current"]{flex:1}
  .wz-bar [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}}

/* step panels */
.wz-panel{margin-top:28px;display:grid;gap:18px;animation:wz-in .3s ease}
@keyframes wz-in{from{opacity:0;transform:translateY(6px)}}
.wz-h{font-size:1.15rem;letter-spacing:-.01em}
.wz-h:focus{outline:none}
.wz-kick{display:block;font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;color:var(--wz-accent-dark);margin-bottom:2px}
.wz-lead{margin:-10px 0 0;color:var(--wz-muted);font-size:.92rem}

/* plan tiles and pills: a .form-check-input inside each label, the outline follows :has(:checked) */
.wz-tile{position:relative;display:grid;gap:2px;padding:14px 14px 14px 44px;border:1.5px solid var(--bs-border-color);border-radius:12px;cursor:pointer;transition:border-color .2s,background .2s}
.wz-tile .form-check-input{position:absolute;left:14px;top:16px;width:18px;height:18px;margin:0}
.wz-tile b{font-size:.95rem}
.wz-tile span{font-size:.82rem;color:var(--wz-muted)}
.wz-tile:has(:checked){border-color:var(--wz-accent);background:#f5f6ff}
.wz-seg label{display:flex;align-items:center;gap:8px;padding:9px 14px;border:1.5px solid var(--bs-border-color);border-radius:999px;font-size:.9rem;cursor:pointer}
.wz-seg .form-check-input{width:16px;height:16px;margin:0}
.wz-seg label:has(:checked){border-color:var(--wz-accent);background:#f5f6ff;color:var(--wz-accent-dark);font-weight:600}
.wz-nav{display:flex;gap:8px;margin-top:28px;padding-top:20px;border-top:1px solid #e8eaf2}
.wz-nav .btn-primary{margin-left:auto}
.wz-done{text-align:center;justify-items:center;padding:12px 0 4px}
.wz-done .wz-dot{width:56px;height:56px;border:0;background:var(--wz-soft);color:var(--wz-accent-dark)}
.wz-done .wz-dot svg{display:block;width:26px;height:26px}
@media (prefers-reduced-motion:reduce){.wz *,.wz *::before{transition:none!important;animation:none!important}}
@media (max-width:600px){.wz .card-body{padding:24px 18px}.wz-steps li{font-size:.72rem}}
JavaScript
// Form wizard: shows one step at a time, marks the stepper with data-state and aria-current="step",
// checks the step's fields before moving on 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');
  var back = wz.querySelector('[data-back]'), next = wz.querySelector('[data-next]');
  var submit = wz.querySelector('[type="submit"]'), done = wz.querySelector('[data-done]'), at = 0;
  function mark(i) {
    steps.forEach(function (s, n) {
      s.dataset.state = n < i ? 'done' : n === i ? 'current' : 'next';
      if (n === i) s.setAttribute('aria-current', 'step'); else s.removeAttribute('aria-current');
      s.querySelector('[data-sr]').textContent = n < i ? ', completed' : '';
    });
  }
  function go(i, focus) {
    at = i; mark(i);
    panels.forEach(function (p, n) { p.hidden = n !== i; });
    back.hidden = i === 0; next.hidden = i === panels.length - 1; submit.hidden = !next.hidden;
    wz.querySelectorAll('[data-out]').forEach(function (o) {
      var f = wz.elements[o.dataset.out], v = f && (f.length && !f.options ? [].filter.call(f, function (x) { return x.checked; }).map(function (x) { return x.value; }).join(', ') : f.value);
      o.textContent = v || 'None';
    });
    if (focus) panels[i].querySelector('[tabindex="-1"]').focus();
  }
  function valid() {
    var bad = [].find.call(panels[at].querySelectorAll('input, select, textarea'), function (f) { return !f.checkValidity(); });
    if (bad) bad.reportValidity();
    return !bad;
  }
  wz.noValidate = true;
  back.addEventListener('click', function () { go(at - 1, true); });
  next.addEventListener('click', function () { if (valid()) go(at + 1, true); });
  wz.addEventListener('submit', function (e) {
    e.preventDefault();
    if (!valid()) return;
    if (at < panels.length - 1) return go(at + 1, true);
    // Last step: send the form here (fetch or wz.submit()). The demo shows the done panel instead.
    panels[at].hidden = true; back.parentNode.hidden = true; mark(steps.length);
    done.hidden = false; done.querySelector('[tabindex="-1"]').focus();
  });
  if (done) done.querySelector('[data-restart]').addEventListener('click', function () {
    wz.reset(); done.hidden = true; back.parentNode.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-[#555c70] uppercase">Numbered stepper, labels below</span>
<form class="rounded-[18px] bg-white px-8 py-[30px] shadow-[0_1px_2px_rgba(17,24,39,.06),0_10px_30px_-12px_rgba(49,46,129,.18)] max-[600px]:px-[18px] max-[600px]:py-6" data-wizard aria-labelledby="ws-title">
  <h2 class="text-[1.3rem] font-bold tracking-[-.015em]" id="ws-title">Create your workspace</h2>
  <p class="mt-1 text-[.92rem] text-wz-muted">Four quick steps. You can change all of this later in Settings.</p>
  <ol class="mt-[26px] grid grid-cols-4" data-stepper aria-label="Progress">
    <li class="group relative flex flex-col items-center gap-2 text-center text-[.8rem] text-wz-muted not-first:before:absolute not-first:before:top-[17px] 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 data-[state=current]:font-semibold data-[state=current]:text-[#111827] not-first:data-[state=current]:before:bg-wz-accent not-first:data-[state=done]:before:bg-wz-accent not-first:motion-reduce:before:transition-none max-[600px]:text-[.72rem]"><span class="grid size-9 place-items-center rounded-full border-2 border-wz-line bg-white text-[.9rem] font-bold text-[#4b5263] transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-wz-accent group-data-[state=current]:text-wz-dark group-data-[state=current]:shadow-[0_0_0_5px_var(--color-wz-soft)] group-data-[state=done]:border-wz-accent group-data-[state=done]:bg-wz-accent group-data-[state=done]:text-white motion-reduce:transition-none"><b class="group-data-[state=done]:hidden">1</b><svg class="hidden size-[18px] fill-none stroke-current stroke-3 group-data-[state=done]:block" 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>Workspace<span class="sr-only" data-sr></span></li>
    <li class="group relative flex flex-col items-center gap-2 text-center text-[.8rem] text-wz-muted not-first:before:absolute not-first:before:top-[17px] 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 data-[state=current]:font-semibold data-[state=current]:text-[#111827] not-first:data-[state=current]:before:bg-wz-accent not-first:data-[state=done]:before:bg-wz-accent not-first:motion-reduce:before:transition-none max-[600px]:text-[.72rem]"><span class="grid size-9 place-items-center rounded-full border-2 border-wz-line bg-white text-[.9rem] font-bold text-[#4b5263] transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-wz-accent group-data-[state=current]:text-wz-dark group-data-[state=current]:shadow-[0_0_0_5px_var(--color-wz-soft)] group-data-[state=done]:border-wz-accent group-data-[state=done]:bg-wz-accent group-data-[state=done]:text-white motion-reduce:transition-none"><b class="group-data-[state=done]:hidden">2</b><svg class="hidden size-[18px] fill-none stroke-current stroke-3 group-data-[state=done]:block" 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>Team<span class="sr-only" data-sr></span></li>
    <li class="group relative flex flex-col items-center gap-2 text-center text-[.8rem] text-wz-muted not-first:before:absolute not-first:before:top-[17px] 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 data-[state=current]:font-semibold data-[state=current]:text-[#111827] not-first:data-[state=current]:before:bg-wz-accent not-first:data-[state=done]:before:bg-wz-accent not-first:motion-reduce:before:transition-none max-[600px]:text-[.72rem]"><span class="grid size-9 place-items-center rounded-full border-2 border-wz-line bg-white text-[.9rem] font-bold text-[#4b5263] transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-wz-accent group-data-[state=current]:text-wz-dark group-data-[state=current]:shadow-[0_0_0_5px_var(--color-wz-soft)] group-data-[state=done]:border-wz-accent group-data-[state=done]:bg-wz-accent group-data-[state=done]:text-white motion-reduce:transition-none"><b class="group-data-[state=done]:hidden">3</b><svg class="hidden size-[18px] fill-none stroke-current stroke-3 group-data-[state=done]:block" 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>Plan<span class="sr-only" data-sr></span></li>
    <li class="group relative flex flex-col items-center gap-2 text-center text-[.8rem] text-wz-muted not-first:before:absolute not-first:before:top-[17px] 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 data-[state=current]:font-semibold data-[state=current]:text-[#111827] not-first:data-[state=current]:before:bg-wz-accent not-first:data-[state=done]:before:bg-wz-accent not-first:motion-reduce:before:transition-none max-[600px]:text-[.72rem]"><span class="grid size-9 place-items-center rounded-full border-2 border-wz-line bg-white text-[.9rem] font-bold text-[#4b5263] transition-[background-color,border-color,box-shadow] duration-250 group-data-[state=current]:border-wz-accent group-data-[state=current]:text-wz-dark group-data-[state=current]:shadow-[0_0_0_5px_var(--color-wz-soft)] group-data-[state=done]:border-wz-accent group-data-[state=done]:bg-wz-accent group-data-[state=done]:text-white motion-reduce:transition-none"><b class="group-data-[state=done]:hidden">4</b><svg class="hidden size-[18px] fill-none stroke-current stroke-3 group-data-[state=done]:block" 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>Review<span class="sr-only" data-sr></span></li>
  </ol>

  <section class="mt-7 grid animate-wz-in gap-[18px] motion-reduce:animate-none" data-step aria-labelledby="ws-h1">
    <h3 class="text-[1.15rem] font-bold tracking-[-.01em] focus:outline-none" id="ws-h1" tabindex="-1"><span class="mb-0.5 block text-[.72rem] tracking-[.1em] text-wz-dark uppercase">Step 1 of 4</span>Name your workspace</h3>
    <p class="-mt-2.5 text-[.92rem] text-wz-muted">This is where your team's boards and reports live.</p>
    <div class="grid gap-1.5"><label class="text-[.88rem] font-semibold" for="ws-name">Workspace name</label>
      <input class="min-h-11 w-full rounded-[10px] border-[1.5px] border-wz-field bg-white px-3 py-2.5 text-[.95rem] focus-visible:border-wz-accent focus-visible:shadow-[0_0_0_4px_var(--color-wz-soft)] focus-visible:outline-none" id="ws-name" name="workspace" required autocomplete="organization" value="Harbor Lane Studio"></div>
    <div class="grid gap-1.5"><label class="text-[.88rem] font-semibold" for="ws-url">Workspace address</label>
      <div class="flex overflow-hidden rounded-[10px] border-[1.5px] border-wz-field has-[:focus-visible]:border-wz-accent has-[:focus-visible]:shadow-[0_0_0_4px_var(--color-wz-soft)]"><span class="flex items-center border-r-[1.5px] border-wz-field bg-[#f4f5fa] px-3 text-[.9rem] text-[#4b5263]">tallyboard.app/</span><input class="min-h-11 w-full min-w-0 bg-white px-3 py-2.5 text-[.95rem] focus-visible:outline-none" id="ws-url" name="address" required pattern="[a-z0-9\-]+" aria-describedby="ws-url-hint" value="harbor-lane"></div>
      <span class="text-[.8rem] text-wz-muted" id="ws-url-hint">Lowercase letters, numbers and hyphens.</span></div>
  </section>

  <section class="mt-7 grid animate-wz-in gap-[18px] motion-reduce:animate-none" data-step aria-labelledby="ws-h2">
    <h3 class="text-[1.15rem] font-bold tracking-[-.01em] focus:outline-none" id="ws-h2" tabindex="-1"><span class="mb-0.5 block text-[.72rem] tracking-[.1em] text-wz-dark uppercase">Step 2 of 4</span>Invite your team</h3>
    <p class="-mt-2.5 text-[.92rem] text-wz-muted">They get an email with a link to join. Leave it empty to skip.</p>
    <div class="grid gap-1.5"><label class="text-[.88rem] font-semibold" for="ws-emails">Email addresses</label>
      <input class="min-h-11 w-full rounded-[10px] border-[1.5px] border-wz-field bg-white px-3 py-2.5 text-[.95rem] placeholder:text-[#6b7185] focus-visible:border-wz-accent focus-visible:shadow-[0_0_0_4px_var(--color-wz-soft)] focus-visible:outline-none" id="ws-emails" name="invites" type="email" multiple aria-describedby="ws-emails-hint" placeholder="[email protected], [email protected]">
      <span class="text-[.8rem] text-wz-muted" id="ws-emails-hint">Separate addresses with commas.</span></div>
    <div class="grid gap-1.5"><label class="text-[.88rem] font-semibold" for="ws-role">They join as</label>
      <select class="min-h-11 w-full rounded-[10px] border-[1.5px] border-wz-field bg-white px-3 py-2.5 text-[.95rem] focus-visible:border-wz-accent focus-visible:shadow-[0_0_0_4px_var(--color-wz-soft)] focus-visible:outline-none" id="ws-role" name="role"><option>Editor</option><option>Viewer</option><option>Admin</option></select></div>
  </section>

  <section class="mt-7 grid animate-wz-in gap-[18px] motion-reduce:animate-none" data-step aria-labelledby="ws-h3">
    <h3 class="text-[1.15rem] font-bold tracking-[-.01em] focus:outline-none" id="ws-h3" tabindex="-1"><span class="mb-0.5 block text-[.72rem] tracking-[.1em] text-wz-dark uppercase">Step 3 of 4</span>Choose a plan</h3>
    <fieldset class="grid gap-1.5"><legend class="mb-1.5 text-[.88rem] font-semibold">Plan</legend>
      <div class="grid grid-cols-[repeat(auto-fit,minmax(160px,1fr))] gap-3">
        <label class="relative grid cursor-pointer gap-0.5 rounded-xl border-[1.5px] border-wz-field py-3.5 pr-3.5 pl-11 transition-colors has-checked:border-wz-accent has-checked:bg-[#f5f6ff] motion-reduce:transition-none"><input class="absolute top-4 left-3.5 size-[18px] accent-wz-accent focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-wz-accent" type="radio" name="plan" value="Starter" required><b class="text-[.95rem]">Starter</b><span class="text-[.82rem] text-wz-muted">Free · up to 3 people</span></label>
        <label class="relative grid cursor-pointer gap-0.5 rounded-xl border-[1.5px] border-wz-field py-3.5 pr-3.5 pl-11 transition-colors has-checked:border-wz-accent has-checked:bg-[#f5f6ff] motion-reduce:transition-none"><input class="absolute top-4 left-3.5 size-[18px] accent-wz-accent focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-wz-accent" type="radio" name="plan" value="Team" checked><b class="text-[.95rem]">Team</b><span class="text-[.82rem] text-wz-muted">$12 per seat a month</span></label>
        <label class="relative grid cursor-pointer gap-0.5 rounded-xl border-[1.5px] border-wz-field py-3.5 pr-3.5 pl-11 transition-colors has-checked:border-wz-accent has-checked:bg-[#f5f6ff] motion-reduce:transition-none"><input class="absolute top-4 left-3.5 size-[18px] accent-wz-accent focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-wz-accent" type="radio" name="plan" value="Business"><b class="text-[.95rem]">Business</b><span class="text-[.82rem] text-wz-muted">$24 per seat a month</span></label>
      </div></fieldset>
  </section>

  <section class="mt-7 grid animate-wz-in gap-[18px] motion-reduce:animate-none" data-step aria-labelledby="ws-h4">
    <h3 class="text-[1.15rem] font-bold tracking-[-.01em] focus:outline-none" id="ws-h4" tabindex="-1"><span class="mb-0.5 block text-[.72rem] tracking-[.1em] text-wz-dark uppercase">Step 4 of 4</span>Review and create</h3>
    <dl class="grid divide-y divide-[#e3e5ef] overflow-hidden rounded-xl border border-[#e3e5ef] text-[.92rem]">
      <div class="flex justify-between gap-4 px-3.5 py-[11px]"><dt class="text-wz-muted">Workspace</dt><dd class="text-right font-semibold wrap-anywhere" data-out="workspace">Harbor Lane Studio</dd></div>
      <div class="flex justify-between gap-4 px-3.5 py-[11px]"><dt class="text-wz-muted">Address</dt><dd class="text-right font-semibold wrap-anywhere">tallyboard.app/<span data-out="address">harbor-lane</span></dd></div>
      <div class="flex justify-between gap-4 px-3.5 py-[11px]"><dt class="text-wz-muted">Invites</dt><dd class="text-right font-semibold wrap-anywhere" data-out="invites">None</dd></div>
      <div class="flex justify-between gap-4 px-3.5 py-[11px]"><dt class="text-wz-muted">Plan</dt><dd class="text-right font-semibold wrap-anywhere" data-out="plan">Team</dd></div>
    </dl>
  </section>

  <section class="mt-7 grid animate-wz-in justify-items-center gap-[18px] pt-3 pb-1 text-center motion-reduce:animate-none" data-done hidden aria-labelledby="ws-h5">
    <span class="grid size-14 place-items-center rounded-full bg-wz-soft text-wz-dark"><svg class="size-[26px] fill-none stroke-current stroke-3" 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.15rem] font-bold tracking-[-.01em] focus:outline-none" id="ws-h5" tabindex="-1">Your workspace is ready</h3>
    <p class="text-[.92rem] text-wz-muted">We sent the invites. Your 14-day Team trial starts today.</p>
    <button type="button" class="inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] border-wz-line bg-white px-[22px] text-[.95rem] font-semibold text-wz-dark transition-colors hover:bg-[#f5f6ff] focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none motion-reduce:transition-none" data-restart>Start over</button>
  </section>

  <div class="mt-7 flex gap-2.5 border-t border-[#e8eaf2] pt-5">
    <button type="button" class="inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] border-wz-line bg-white px-[22px] text-[.95rem] font-semibold text-wz-dark transition-colors hover:bg-[#f5f6ff] focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none motion-reduce:transition-none" 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] border-wz-accent bg-wz-accent px-[22px] text-[.95rem] font-semibold text-white transition-colors hover:bg-wz-dark focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none motion-reduce:transition-none" data-next hidden>Continue</button>
    <button type="submit" class="ms-auto inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] border-wz-accent bg-wz-accent px-[22px] text-[.95rem] font-semibold text-white transition-colors hover:bg-wz-dark focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none motion-reduce:transition-none">Create workspace</button>
  </div>
</form></div>

<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-[#555c70] uppercase">Compact bar, labels beside numbers</span>
<form class="rounded-[18px] bg-white px-8 py-[30px] shadow-[0_1px_2px_rgba(17,24,39,.06),0_10px_30px_-12px_rgba(49,46,129,.18)] max-[600px]:px-[18px] max-[600px]:py-6" data-wizard aria-labelledby="job-title">
  <h2 class="text-[1.3rem] font-bold tracking-[-.015em]" id="job-title">Post a job</h2>
  <p class="mt-1 text-[.92rem] text-wz-muted">Northwind Robotics · Careers</p>
  <ol class="mt-[22px] flex gap-1 rounded-[14px] bg-[#f1f2f9] p-[5px]" data-stepper aria-label="Progress">
    <li class="group flex flex-1 items-center justify-center gap-[9px] rounded-[10px] px-2.5 py-[9px] text-[.88rem] text-wz-muted transition-[background-color,box-shadow] duration-250 data-[state=current]:bg-white data-[state=current]:font-semibold data-[state=current]:text-[#111827] data-[state=current]:shadow-[0_1px_3px_rgba(17,24,39,.12)] motion-reduce:transition-none max-[520px]:flex-none max-[520px]:data-[state=current]:flex-1"><span class="grid size-[26px] flex-none place-items-center rounded-full border-2 border-wz-line bg-white text-[.78rem] font-bold text-[#4b5263] group-data-[state=current]:border-wz-accent group-data-[state=current]:bg-wz-accent group-data-[state=current]:text-white group-data-[state=done]:border-wz-accent group-data-[state=done]:bg-wz-soft group-data-[state=done]:text-wz-dark"><b class="group-data-[state=done]:hidden">1</b><svg class="hidden size-3.5 fill-none stroke-current stroke-3 group-data-[state=done]:block" 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><span class="max-[520px]:group-data-[state=done]:sr-only max-[520px]:group-data-[state=next]:sr-only">Role</span><span class="sr-only" data-sr></span></li>
    <li class="group flex flex-1 items-center justify-center gap-[9px] rounded-[10px] px-2.5 py-[9px] text-[.88rem] text-wz-muted transition-[background-color,box-shadow] duration-250 data-[state=current]:bg-white data-[state=current]:font-semibold data-[state=current]:text-[#111827] data-[state=current]:shadow-[0_1px_3px_rgba(17,24,39,.12)] motion-reduce:transition-none max-[520px]:flex-none max-[520px]:data-[state=current]:flex-1"><span class="grid size-[26px] flex-none place-items-center rounded-full border-2 border-wz-line bg-white text-[.78rem] font-bold text-[#4b5263] group-data-[state=current]:border-wz-accent group-data-[state=current]:bg-wz-accent group-data-[state=current]:text-white group-data-[state=done]:border-wz-accent group-data-[state=done]:bg-wz-soft group-data-[state=done]:text-wz-dark"><b class="group-data-[state=done]:hidden">2</b><svg class="hidden size-3.5 fill-none stroke-current stroke-3 group-data-[state=done]:block" 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><span class="max-[520px]:group-data-[state=done]:sr-only max-[520px]:group-data-[state=next]:sr-only">Details</span><span class="sr-only" data-sr></span></li>
    <li class="group flex flex-1 items-center justify-center gap-[9px] rounded-[10px] px-2.5 py-[9px] text-[.88rem] text-wz-muted transition-[background-color,box-shadow] duration-250 data-[state=current]:bg-white data-[state=current]:font-semibold data-[state=current]:text-[#111827] data-[state=current]:shadow-[0_1px_3px_rgba(17,24,39,.12)] motion-reduce:transition-none max-[520px]:flex-none max-[520px]:data-[state=current]:flex-1"><span class="grid size-[26px] flex-none place-items-center rounded-full border-2 border-wz-line bg-white text-[.78rem] font-bold text-[#4b5263] group-data-[state=current]:border-wz-accent group-data-[state=current]:bg-wz-accent group-data-[state=current]:text-white group-data-[state=done]:border-wz-accent group-data-[state=done]:bg-wz-soft group-data-[state=done]:text-wz-dark"><b class="group-data-[state=done]:hidden">3</b><svg class="hidden size-3.5 fill-none stroke-current stroke-3 group-data-[state=done]:block" 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><span class="max-[520px]:group-data-[state=done]:sr-only max-[520px]:group-data-[state=next]:sr-only">Publish</span><span class="sr-only" data-sr></span></li>
  </ol>

  <section class="mt-7 grid animate-wz-in gap-[18px] motion-reduce:animate-none" data-step aria-labelledby="job-h1">
    <h3 class="text-[1.15rem] font-bold tracking-[-.01em] focus:outline-none" id="job-h1" tabindex="-1"><span class="mb-0.5 block text-[.72rem] tracking-[.1em] text-wz-dark uppercase">Step 1 of 3</span>What's the role?</h3>
    <div class="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-4">
      <div class="grid min-w-0 gap-1.5"><label class="text-[.88rem] font-semibold" for="job-name">Job title</label><input class="min-h-11 w-full rounded-[10px] border-[1.5px] border-wz-field bg-white px-3 py-2.5 text-[.95rem] focus-visible:border-wz-accent focus-visible:shadow-[0_0_0_4px_var(--color-wz-soft)] focus-visible:outline-none" id="job-name" name="title" required value="Firmware Engineer"></div>
      <div class="grid min-w-0 gap-1.5"><label class="text-[.88rem] font-semibold" for="job-team">Team</label>
        <select class="min-h-11 w-full rounded-[10px] border-[1.5px] border-wz-field bg-white px-3 py-2.5 text-[.95rem] focus-visible:border-wz-accent focus-visible:shadow-[0_0_0_4px_var(--color-wz-soft)] focus-visible:outline-none" id="job-team" name="team"><option>Embedded systems</option><option>Perception</option><option>Manufacturing</option></select></div>
    </div>
    <fieldset class="grid gap-1.5"><legend class="mb-1.5 text-[.88rem] font-semibold">Employment type</legend>
      <div class="flex flex-wrap gap-2">
        <label class="flex cursor-pointer items-center gap-2 rounded-full border-[1.5px] border-wz-field px-3.5 py-[9px] text-[.9rem] has-checked:border-wz-accent has-checked:bg-[#f5f6ff] has-checked:font-semibold has-checked:text-wz-dark"><input class="size-4 accent-wz-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-wz-accent" type="radio" name="type" value="Full-time" checked>Full-time</label>
        <label class="flex cursor-pointer items-center gap-2 rounded-full border-[1.5px] border-wz-field px-3.5 py-[9px] text-[.9rem] has-checked:border-wz-accent has-checked:bg-[#f5f6ff] has-checked:font-semibold has-checked:text-wz-dark"><input class="size-4 accent-wz-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-wz-accent" type="radio" name="type" value="Part-time">Part-time</label>
        <label class="flex cursor-pointer items-center gap-2 rounded-full border-[1.5px] border-wz-field px-3.5 py-[9px] text-[.9rem] has-checked:border-wz-accent has-checked:bg-[#f5f6ff] has-checked:font-semibold has-checked:text-wz-dark"><input class="size-4 accent-wz-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-wz-accent" type="radio" name="type" value="Contract">Contract</label>
      </div></fieldset>
  </section>

  <section class="mt-7 grid animate-wz-in gap-[18px] motion-reduce:animate-none" data-step aria-labelledby="job-h2">
    <h3 class="text-[1.15rem] font-bold tracking-[-.01em] focus:outline-none" id="job-h2" tabindex="-1"><span class="mb-0.5 block text-[.72rem] tracking-[.1em] text-wz-dark uppercase">Step 2 of 3</span>Location and pay</h3>
    <div class="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-4">
      <div class="grid min-w-0 gap-1.5"><label class="text-[.88rem] font-semibold" for="job-loc">Location</label><input class="min-h-11 w-full rounded-[10px] border-[1.5px] border-wz-field bg-white px-3 py-2.5 text-[.95rem] focus-visible:border-wz-accent focus-visible:shadow-[0_0_0_4px_var(--color-wz-soft)] focus-visible:outline-none" id="job-loc" name="location" required value="Pittsburgh, PA · Hybrid"></div>
      <div class="grid min-w-0 gap-1.5"><label class="text-[.88rem] font-semibold" for="job-min">Salary from (USD)</label><input class="min-h-11 w-full rounded-[10px] border-[1.5px] border-wz-field bg-white px-3 py-2.5 text-[.95rem] focus-visible:border-wz-accent focus-visible:shadow-[0_0_0_4px_var(--color-wz-soft)] focus-visible:outline-none" id="job-min" name="min" type="number" min="0" step="1000" value="128000"></div>
      <div class="grid min-w-0 gap-1.5"><label class="text-[.88rem] font-semibold" for="job-max">Salary to (USD)</label><input class="min-h-11 w-full rounded-[10px] border-[1.5px] border-wz-field bg-white px-3 py-2.5 text-[.95rem] focus-visible:border-wz-accent focus-visible:shadow-[0_0_0_4px_var(--color-wz-soft)] focus-visible:outline-none" id="job-max" name="max" type="number" min="0" step="1000" value="156000"></div>
    </div>
    <div class="grid gap-1.5"><label class="text-[.88rem] font-semibold" for="job-desc">Short description</label>
      <textarea class="min-h-24 w-full resize-y rounded-[10px] border-[1.5px] border-wz-field bg-white px-3 py-2.5 text-[.95rem] focus-visible:border-wz-accent focus-visible:shadow-[0_0_0_4px_var(--color-wz-soft)] focus-visible:outline-none" id="job-desc" name="description" required minlength="40">You will write and test the motor-control firmware for our warehouse picking arms, from bring-up to field updates.</textarea></div>
  </section>

  <section class="mt-7 grid animate-wz-in gap-[18px] motion-reduce:animate-none" data-step aria-labelledby="job-h3">
    <h3 class="text-[1.15rem] font-bold tracking-[-.01em] focus:outline-none" id="job-h3" tabindex="-1"><span class="mb-0.5 block text-[.72rem] tracking-[.1em] text-wz-dark uppercase">Step 3 of 3</span>Where should it appear?</h3>
    <fieldset class="grid gap-1.5"><legend class="mb-1.5 text-[.88rem] font-semibold">Post to</legend>
      <div class="flex flex-wrap gap-2">
        <label class="flex cursor-pointer items-center gap-2 rounded-full border-[1.5px] border-wz-field px-3.5 py-[9px] text-[.9rem] has-checked:border-wz-accent has-checked:bg-[#f5f6ff] has-checked:font-semibold has-checked:text-wz-dark"><input class="size-4 accent-wz-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-wz-accent" type="checkbox" name="boards" value="Careers page" checked>Careers page</label>
        <label class="flex cursor-pointer items-center gap-2 rounded-full border-[1.5px] border-wz-field px-3.5 py-[9px] text-[.9rem] has-checked:border-wz-accent has-checked:bg-[#f5f6ff] has-checked:font-semibold has-checked:text-wz-dark"><input class="size-4 accent-wz-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-wz-accent" type="checkbox" name="boards" value="LinkedIn" checked>LinkedIn</label>
        <label class="flex cursor-pointer items-center gap-2 rounded-full border-[1.5px] border-wz-field px-3.5 py-[9px] text-[.9rem] has-checked:border-wz-accent has-checked:bg-[#f5f6ff] has-checked:font-semibold has-checked:text-wz-dark"><input class="size-4 accent-wz-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-wz-accent" type="checkbox" name="boards" value="Robotics job boards">Robotics job boards</label>
      </div></fieldset>
    <div class="grid max-w-[260px] gap-1.5"><label class="text-[.88rem] font-semibold" for="job-close">Closing date</label><input class="min-h-11 w-full rounded-[10px] border-[1.5px] border-wz-field bg-white px-3 py-2.5 text-[.95rem] focus-visible:border-wz-accent focus-visible:shadow-[0_0_0_4px_var(--color-wz-soft)] focus-visible:outline-none" id="job-close" name="closes" type="date" required value="2026-11-30"></div>
  </section>

  <section class="mt-7 grid animate-wz-in justify-items-center gap-[18px] pt-3 pb-1 text-center motion-reduce:animate-none" data-done hidden aria-labelledby="job-h4">
    <span class="grid size-14 place-items-center rounded-full bg-wz-soft text-wz-dark"><svg class="size-[26px] fill-none stroke-current stroke-3" 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.15rem] font-bold tracking-[-.01em] focus:outline-none" id="job-h4" tabindex="-1">Job posted</h3>
    <p class="text-[.92rem] text-wz-muted">It goes live on your careers page in about a minute.</p>
    <button type="button" class="inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] border-wz-line bg-white px-[22px] text-[.95rem] font-semibold text-wz-dark transition-colors hover:bg-[#f5f6ff] focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none motion-reduce:transition-none" data-restart>Post another job</button>
  </section>

  <div class="mt-7 flex gap-2.5 border-t border-[#e8eaf2] pt-5">
    <button type="button" class="inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] border-wz-line bg-white px-[22px] text-[.95rem] font-semibold text-wz-dark transition-colors hover:bg-[#f5f6ff] focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none motion-reduce:transition-none" 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] border-wz-accent bg-wz-accent px-[22px] text-[.95rem] font-semibold text-white transition-colors hover:bg-wz-dark focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none motion-reduce:transition-none" data-next hidden>Continue</button>
    <button type="submit" class="ms-auto inline-flex min-h-11 cursor-pointer items-center justify-center rounded-[10px] border-[1.5px] border-wz-accent bg-wz-accent px-[22px] text-[.95rem] font-semibold text-white transition-colors hover:bg-wz-dark focus-visible:shadow-[0_0_0_3px_#fff,0_0_0_5px_var(--color-wz-accent)] focus-visible:outline-none motion-reduce:transition-none">Publish job</button>
  </div>
</form></div>
CSS
@theme {
  --color-wz-accent: #4f46e5;
  --color-wz-dark: #3730a3;
  --color-wz-soft: #e0e7ff;
  --color-wz-muted: #555c70;
  --color-wz-line: #8b91a7;
  --color-wz-field: #7c8398;
  --animate-wz-in: wz-in .3s ease;
  @keyframes wz-in { from { opacity: 0; transform: translateY(6px); } }
}
JavaScript
// Form wizard: shows one step at a time, marks the stepper with data-state and aria-current="step",
// checks the step's fields before moving on 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');
  var back = wz.querySelector('[data-back]'), next = wz.querySelector('[data-next]');
  var submit = wz.querySelector('[type="submit"]'), done = wz.querySelector('[data-done]'), at = 0;
  function mark(i) {
    steps.forEach(function (s, n) {
      s.dataset.state = n < i ? 'done' : n === i ? 'current' : 'next';
      if (n === i) s.setAttribute('aria-current', 'step'); else s.removeAttribute('aria-current');
      s.querySelector('[data-sr]').textContent = n < i ? ', completed' : '';
    });
  }
  function go(i, focus) {
    at = i; mark(i);
    panels.forEach(function (p, n) { p.hidden = n !== i; });
    back.hidden = i === 0; next.hidden = i === panels.length - 1; submit.hidden = !next.hidden;
    wz.querySelectorAll('[data-out]').forEach(function (o) {
      var f = wz.elements[o.dataset.out], v = f && (f.length && !f.options ? [].filter.call(f, function (x) { return x.checked; }).map(function (x) { return x.value; }).join(', ') : f.value);
      o.textContent = v || 'None';
    });
    if (focus) panels[i].querySelector('[tabindex="-1"]').focus();
  }
  function valid() {
    var bad = [].find.call(panels[at].querySelectorAll('input, select, textarea'), function (f) { return !f.checkValidity(); });
    if (bad) bad.reportValidity();
    return !bad;
  }
  wz.noValidate = true;
  back.addEventListener('click', function () { go(at - 1, true); });
  next.addEventListener('click', function () { if (valid()) go(at + 1, true); });
  wz.addEventListener('submit', function (e) {
    e.preventDefault();
    if (!valid()) return;
    if (at < panels.length - 1) return go(at + 1, true);
    // Last step: send the form here (fetch or wz.submit()). The demo shows the done panel instead.
    panels[at].hidden = true; back.parentNode.hidden = true; mark(steps.length);
    done.hidden = false; done.querySelector('[tabindex="-1"]').focus();
  });
  if (done) done.querySelector('[data-restart]').addEventListener('click', function () {
    wz.reset(); done.hidden = true; back.parentNode.hidden = false; go(0, true);
  });
  go(0);
});

About this set

Two multi-step forms with the stepper across the top, the pattern most wizards start from. Numbered stepper, labels below, is a four-step workspace setup: name and address, team invites, a plan picked from radio tiles and a review list that repeats what was entered. Numbered dots sit on a line that fills in the accent colour as steps are completed, and finished steps swap their number for a tick. Compact bar, labels beside numbers, is a three-step job posting form where the stepper is a segmented bar and the current step is a raised white pill; below 520 pixels the other steps shrink to their number so the bar never overflows. Each step is a section with its own heading, and one Back and Continue bar serves the whole form. About forty lines of vanilla JavaScript show one step at a time, check the current step’s fields with the browser’s own constraint validation before moving on, set aria-current=”step” and a data-state on the stepper, move focus to the new step’s heading, fill the review slots and show a done panel. Without JavaScript every step shows in order and the form submits normally.

What’s included

  • Numbered stepper with a connector line that fills as you go
  • Compact segmented bar that keeps only the current label on phones
  • Per-step validation with the browser's constraint validation
  • Review step filled from the form through data-out slots
  • Focus moves to each new step heading, which starts with Step 2 of 4
  • Every step visible and submittable without JavaScript

Accessibility

The stepper is an ordered list labelled Progress; the current item has aria-current="step" and finished items gain a visually hidden ", completed". Each step is a section named by its heading, and focus moves to that heading on Back and Continue so screen readers announce "Step 2 of 4, Invite your team". Fields, radio tiles and buttons show visible focus rings, field borders reach 3:1, and the step animation and colour transitions stop under prefers-reduced-motion.

Customise it

Change --wz-accent, --wz-accent-dark, --wz-soft and --wz-line on .wz in the plain CSS and Bootstrap versions; Bootstrap buttons and fields read them through --bs-btn-* and --bs-border-color. In Tailwind edit the --color-wz-* theme colours.