States
Data Attribute Example
data-state="completed"
Completed
2
Next
data-state="active"
1
Active
2
Next
data-state="upcoming"
1
Upcoming
2
Upcoming
data-state="error"
Done
2
Error
Variants
Class Example
.mdst-stepper
Account
2
Profile
3
Review
4
Confirm
.mdst-stepper--vertical
Account
2
Profile
3
Review
4
Confirm
.mdst-stepper--compact
Account
2
Profile
3
Review
4
Confirm
Examples

Checkout flow — step 2 active

Cart
2
Shipping
3
Payment
4
Done

Error on step 2

Cart
!
Shipping
3
Payment
4
Done

Vertical — step 3 active

Select plan
Create account
3
Enter payment
4
Confirmation
Interactive Demo

This demo uses vanilla JS to toggle data-state attributes. The JavaScript below is not part of modest-ui — it’s here to show how the CSS responds to state changes. Bring whatever JS you want.

1
Account
2
Profile
3
Review
4
Confirm
Demo source
const steps = document.querySelectorAll("#demo-stepper .mdst-stepper-step");
let current = 0;

function update() {
  steps.forEach((step, i) => {
    if (i < current) {
      step.dataset.state = "completed";
      step.querySelector(".mdst-stepper-indicator").textContent = "\u2713";
    } else if (i === current) {
      step.dataset.state = "active";
      step.querySelector(".mdst-stepper-indicator").textContent = String(i + 1);
    } else {
      step.dataset.state = "upcoming";
      step.querySelector(".mdst-stepper-indicator").textContent = String(i + 1);
    }
  });
  backBtn.disabled = current === 0;
  nextBtn.disabled = current >= steps.length;
}

nextBtn.addEventListener("click", () => { if (current < steps.length) { current++; update(); } });
backBtn.addEventListener("click", () => { if (current > 0) { current--; update(); } });
resetBtn.addEventListener("click", () => { current = 0; update(); });
Data Attribute Contract
<!-- Set data-state on each .mdst-stepper-step -->

data-state="completed"  → Step is done (filled indicator, solid connector)
data-state="active"     → Current step (bold indicator and label)
data-state="upcoming"   → Not yet reached (muted indicator and label)
data-state="error"      → Step has an error (error-colored indicator and label)

<!-- Your JS updates these attributes as the user progresses -->
step.dataset.state = "completed";
step.dataset.state = "active";
step.dataset.state = "error";
Usage
<!-- Horizontal stepper -->
<div class="mdst-stepper">
  <div class="mdst-stepper-step" data-state="completed">
    <div class="mdst-stepper-indicator">&#10003;</div>
    <div class="mdst-stepper-label">Account</div>
  </div>
  <div class="mdst-stepper-step" data-state="active">
    <div class="mdst-stepper-indicator">2</div>
    <div class="mdst-stepper-label">Profile</div>
  </div>
  <div class="mdst-stepper-step" data-state="upcoming">
    <div class="mdst-stepper-indicator">3</div>
    <div class="mdst-stepper-label">Review</div>
  </div>
</div>

<!-- Vertical variant -->
<div class="mdst-stepper mdst-stepper--vertical">
  <div class="mdst-stepper-step" data-state="completed">
    <div class="mdst-stepper-indicator">&#10003;</div>
    <div class="mdst-stepper-label">Step 1</div>
  </div>
  <div class="mdst-stepper-step" data-state="active">
    <div class="mdst-stepper-indicator">2</div>
    <div class="mdst-stepper-label">Step 2</div>
  </div>
</div>

<!-- Compact variant -->
<div class="mdst-stepper mdst-stepper--compact">
  ...
</div>

<!-- Error state -->
<div class="mdst-stepper-step" data-state="error">
  <div class="mdst-stepper-indicator">!</div>
  <div class="mdst-stepper-label">Failed</div>
</div>