DEMO
CODE
<div class="demo-stage stp-stage">
<div class="stp-box">
<ol class="stp-list" id="stp-list">
<li class="stp-item"><span class="stp-dot">1</span><span class="stp-name">入力</span></li>
<li class="stp-item"><span class="stp-dot">2</span><span class="stp-name">確認</span></li>
<li class="stp-item"><span class="stp-dot">3</span><span class="stp-name">支払い</span></li>
<li class="stp-item"><span class="stp-dot">4</span><span class="stp-name">完了</span></li>
</ol>
<div class="stp-nav">
<button type="button" class="stp-btn" id="stp-back">戻る</button>
<button type="button" class="stp-btn stp-next" id="stp-next">次へ</button>
</div>
</div>
</div>
.stp-stage { display:flex; align-items:center; justify-content:center; padding:2rem; background:#0B0B10; min-height:280px; }
.stp-box { width:100%; max-width:420px; }
.stp-list { list-style:none; margin:0 0 1.6rem; padding:0; display:grid; grid-template-columns:repeat(4,1fr); }
.stp-item { position:relative; display:flex; flex-direction:column; align-items:center; gap:.45rem; }
.stp-item + .stp-item::before { content:''; position:absolute; top:17px; right:50%; width:100%; height:2px; background:rgba(255,255,255,.12); }
.stp-item + .stp-item::after { content:''; position:absolute; top:17px; right:50%; width:100%; height:2px; background:#E91B89; transform:scaleX(0); transform-origin:left; transition: transform .4s cubic-bezier(.22,1,.36,1); }
.stp-item.is-done::after, .stp-item.is-current::after { transform:scaleX(1); }
.stp-dot { position:relative; z-index:1; width:36px; height:36px; border-radius:50%; display:flex; align-items:center; justify-content:center;
background:#15151B; border:2px solid rgba(255,255,255,.18); color:#8A8A96; font-family:'JetBrains Mono',monospace; font-size:.85rem;
transition: background .3s ease, border-color .3s ease, color .3s ease, transform .3s cubic-bezier(.34,1.56,.64,1); }
.stp-item.is-current .stp-dot { border-color:#E91B89; color:#fff; transform:scale(1.1); }
.stp-item.is-done .stp-dot { background:#E91B89; border-color:#E91B89; color:#fff; }
.stp-name { font-size:.74rem; color:#8A8A96; }
.stp-item.is-current .stp-name { color:#fff; }
.stp-nav { display:flex; justify-content:space-between; }
.stp-btn { min-width:88px; min-height:44px; padding:0 1rem; border-radius:9px; border:1px solid rgba(255,255,255,.18); background:transparent; color:#C9C9D1; font-size:.85rem; cursor:pointer; }
.stp-next { background:#E91B89; border-color:#E91B89; color:#fff; }
.stp-btn:focus-visible { outline:2px solid #fff; outline-offset:2px; }
.stp-btn[aria-disabled="true"] { opacity:.45; cursor:not-allowed; }
@media (prefers-reduced-motion: reduce) {
.stp-item + .stp-item::after, .stp-dot { transition-duration:.01ms; }
}
(function(){
var list = document.getElementById('stp-list');
var back = document.getElementById('stp-back');
var next = document.getElementById('stp-next');
if (!list || !back || !next) return;
var items = list.querySelectorAll('.stp-item');
var cur = 0;
function render(){
items.forEach(function(li, i){
li.classList.toggle('is-done', i < cur);
li.classList.toggle('is-current', i === cur);
if (i === cur) li.setAttribute('aria-current', 'step'); else li.removeAttribute('aria-current');
li.querySelector('.stp-dot').textContent = i < cur ? '✓' : String(i + 1);
});
back.setAttribute('aria-disabled', cur === 0 ? 'true' : 'false');
next.textContent = cur === items.length - 1 ? '最初から' : '次へ';
}
next.addEventListener('click', function(){ cur = cur === items.length - 1 ? 0 : cur + 1; render(); });
back.addEventListener('click', function(){ if (cur > 0) { cur--; render(); } });
render();
})();
AI PROMPT
入力フォームや購入手続きで、いま何段目にいるかを示すステップ表示(Stepper)を作って。ol と li で4段(入力・確認・支払い・完了)を grid で等間隔に並べ、各段は番号入りの丸と名前を縦に置く。段の間の線は2本重ねる:下は灰色の線、上はアクセント色の線を transform: scaleX(0→1)(transform-origin: left)で伸ばす(width を動かさない)。終わった段は丸を塗りつぶして中身を「✓」にし、現在の段は枠をアクセント色にして scale(1.1) で少し大きくする(cubic-bezier(.34,1.56,.64,1) で弾ませる)。現在の段の li には aria-current="step" を付けて読み上げに位置を伝える。「戻る」「次へ」ボタンで行き来し、最初の段では「戻る」を aria-disabled="true" にして押しても何もしない(disabled 属性にしないのは、フォーカスが外れて位置を見失わないようにするため)。最後の段では「次へ」を「最初から」に変える。ボタンの高さは44px以上。@media (prefers-reduced-motion: reduce) で transition を無効化する。 Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift
AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する
BOOKS · PR · AMAZON
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
学びを止めない · AMAZONサブスク
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
会員登録や購入手続きのように、いくつかの画面に分かれた作業で「全部で何段あり、今どこにいるか」を示す部品です。先の長さが見えると、途中でやめてしまう人を減らす効果が期待でき、フォームの最上部によく置かれます。
当サイトの Progress Bar は割合で進み具合を示す横棒、Segmented Control は選択肢を切り替える部品です。本ページは段階の数と現在位置を示す別テーマで、終わった段・今の段・これからの段を見分けられるようにします。
色の違いだけで現在位置を示すと、色の見分けにくい人や読み上げの利用者には伝わりません。aria-current で位置を伝え、終わった段にはチェックマークという形の違いも付けると、誰にとっても分かりやすい表示になります。
aria-current="step" は何を伝えますか?
複数の段のうち、どれが今の段かを支援技術に伝える属性です。スクリーンリーダーでは「現在のステップ」のように読み上げられ、見た目の色や大きさの違いが分からない利用者にも位置が伝わります。今の段だけに付け、ほかの段からは取り除いてください。
最初の段で「戻る」を disabled にしないのはなぜですか?
disabled にしたボタンはフォーカスを受け取れなくなるため、キーボードで操作中にフォーカスがどこかへ飛び、利用者が位置を見失うことがあります。aria-disabled="true" にして見た目を薄くし、押しても何も起きないようにすると、フォーカスは保ったまま「今は戻れない」ことを伝えられます。
コードは無料で使えますか?
はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。