ステッパーとは

複数の手順に分かれた入力を、今どの段階にいるかを丸数字と線で示しながら進める部品。

概要

会員登録や購入手続きのように複数の手順に分かれたフォームで、今どの段階にいるかを丸数字と線でつないで示す部品です。完了した手順・現在の手順・まだ着手していない手順を色や塗りつぶしで区別し、全体で何ステップあるうちの何番目にいるかを一目で伝えます。「次へ」「戻る」ボタンと組み合わせて、1 画面ずつ入力を進める使い方が一般的です。

別名

表記ゆれ:ステップフォーム、マルチステップフォーム、進捗ステップ、stepper

「マルチステップフォーム」はフォーム全体の作り方を指す言葉で、「ステッパー」はその中で進捗を示す表示部品そのものを指します。

AI への伝え方

「3 ステップの会員登録フォームをステッパー付きで作って」のように、ステップ数と各段階で何を入力させるかを伝えます。次へ・戻るボタンで移動できるようにしたいか、各ステップの丸数字をクリックしても移動できるようにしたいかも伝えると挙動がぶれません。

似たパーツとの違い

「プログレスバー」は連続した割合(パーセント)を示すのに対し、ステッパーは区切られた手順のどこにいるかを段階で示す点が異なります。 「ウィザード」はステッパーを含むフォーム全体の進め方(画面遷移の仕組み)を指す、より広い言葉です。

デモ

  1. お客様情報
  2. お支払い方法
  3. 確認

現在のステップの入力内容がここに表示されます(デモでは省略)。

コード

HTML
<div class="stepper-demo">
  <ol class="stepper-demo__steps" data-stepper-steps>
    <li class="stepper-demo__step stepper-demo__step--done" data-stepper-step="1">
      <span class="stepper-demo__circle" aria-hidden="true">✓</span>
      <span class="stepper-demo__label">お客様情報</span>
    </li>
    <li class="stepper-demo__step stepper-demo__step--current" data-stepper-step="2" aria-current="step">
      <span class="stepper-demo__circle" aria-hidden="true">2</span>
      <span class="stepper-demo__label">お支払い方法</span>
    </li>
    <li class="stepper-demo__step" data-stepper-step="3">
      <span class="stepper-demo__circle" aria-hidden="true">3</span>
      <span class="stepper-demo__label">確認</span>
    </li>
  </ol>

  <div class="stepper-demo__body">
    <p>現在のステップの入力内容がここに表示されます(デモでは省略)。</p>
  </div>

  <div class="stepper-demo__actions">
    <button class="stepper-demo__button" type="button" data-stepper-back>戻る</button>
    <button class="stepper-demo__button stepper-demo__button--primary" type="button" data-stepper-next>次へ</button>
  </div>
</div>
CSS
.stepper-demo {
  font-family: sans-serif;
}

.stepper-demo__steps {
  display: flex;
  list-style: none;
  margin: 0 0 20px;
  padding: 0;
}

.stepper-demo__step {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
  font-size: 12px;
  color: #666;
}

.stepper-demo__step::before {
  content: "";
  position: absolute;
  top: 15px;
  left: -50%;
  width: 100%;
  height: 2px;
  background: #e6e6e6;
  z-index: 0;
}

.stepper-demo__step:first-child::before {
  display: none;
}

.stepper-demo__step--done::before {
  background: #0017c1;
}

.stepper-demo__circle {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: #e6e6e6;
  color: #666;
  font-size: 13px;
  font-weight: 700;
}

.stepper-demo__step--done .stepper-demo__circle {
  background: #0017c1;
  color: #fff;
}

.stepper-demo__step--current .stepper-demo__circle {
  background: #fff;
  border: 2px solid #0017c1;
  color: #0017c1;
}

.stepper-demo__step--current .stepper-demo__label {
  color: #0017c1;
  font-weight: 700;
}

.stepper-demo__body {
  padding: 16px;
  margin-bottom: 16px;
  border: 1px solid #e6e6e6;
  border-radius: 8px;
  background: #fff;
  font-size: 13px;
  color: #333;
}

.stepper-demo__body p {
  margin: 0;
}

.stepper-demo__actions {
  display: flex;
  gap: 10px;
}

.stepper-demo__button {
  padding: 8px 20px;
  border: 1px solid #0017c1;
  border-radius: 8px;
  background: #fff;
  color: #0017c1;
  font-size: 13px;
  cursor: pointer;
}

.stepper-demo__button:hover {
  background: #e6e6e6;
}

.stepper-demo__button:focus-visible {
  outline: 2px solid #0017c1;
  outline-offset: 2px;
}

.stepper-demo__button--primary {
  background: #0017c1;
  color: #fff;
}

.stepper-demo__button--primary:hover {
  background: #000f8c;
}

.stepper-demo__button:disabled {
  border-color: #b3b3b3;
  background: #f2f2f2;
  color: #b3b3b3;
  cursor: not-allowed;
}
JS
const steps = document.querySelectorAll("[data-stepper-step]");
const backButton = document.querySelector("[data-stepper-back]");
const nextButton = document.querySelector("[data-stepper-next]");
let current = 2;

function render() {
  steps.forEach((step) => {
    const num = Number(step.dataset.stepperStep);
    step.classList.remove("stepper-demo__step--done", "stepper-demo__step--current");
    step.removeAttribute("aria-current");
    if (num < current) {
      step.classList.add("stepper-demo__step--done");
      step.querySelector(".stepper-demo__circle").textContent = "✓";
    } else if (num === current) {
      step.classList.add("stepper-demo__step--current");
      step.setAttribute("aria-current", "step");
      step.querySelector(".stepper-demo__circle").textContent = String(num);
    } else {
      step.querySelector(".stepper-demo__circle").textContent = String(num);
    }
  });
  backButton.disabled = current === 1;
  nextButton.disabled = current === steps.length;
}

backButton.addEventListener("click", () => {
  if (current > 1) {
    current -= 1;
    render();
  }
});

nextButton.addEventListener("click", () => {
  if (current < steps.length) {
    current += 1;
    render();
  }
});

render();