ステップインジケータ
stimeo--step-indicator
手続きのどこまで来たかを示す表示。読むためのもので、押すためのものではありません。
申し込みのような手続きで、いまどの段階かを示します。読むための表示なので、フォーカスも受けず押せません。現在地は読み上げにも伝わります。各段階が済んだのか、いまここなのか、これからなのかは CSS から読めるので、色や印は自由に付けられます。全体のどのくらいまで来たかも割合として渡すので、進捗の線を引けます。段階を進めるのは外側の操作の役目です。押して行き来できるものが要るときは、ステッパーを使ってください。
実行中
- カート
- 配送
- 支払い
- 確認
キーボード操作
このコンポーネント自体はキーボード操作を持ちません。
<%# Markup for the step-indicator (read-only progress) demo.
The library holds the current step number, sets data-state on each li, sets
aria-current="step" on the current li, and updates the progress CSS variable
(--stimeo--step-indicator-ratio). The steps themselves aren't interactive, so the
current step is updated via the external step:set event (fired by demo.js from the
prev/next buttons). %>
<div class="step-indicator-demo">
<%# --steps lets the CSS put the track between the first and last dot centres:
the library's ratio is "how far along the range", and the range runs from
centre to centre, not edge to edge. %>
<ol
class="step-indicator"
style="--steps: 4"
data-controller="stimeo--step-indicator"
aria-label="<%= t("components.step_indicator.demo.label") %>"
data-stimeo--step-indicator-current-value="1"
data-action="step:set->stimeo--step-indicator#setCurrent">
<% %w[cart shipping payment confirm].each_with_index do |step, index| %>
<li
class="step-indicator__step"
data-stimeo--step-indicator-target="step"
<%= "data-state=\"complete\"".html_safe if index < 1 %>
<%= "data-state=\"current\" aria-current=\"step\"".html_safe if index == 1 %>
<%= "data-state=\"upcoming\"".html_safe if index > 1 %>>
<span class="step-indicator__dot"></span>
<span class="step-indicator__label"><%= t(
"components.step_indicator.demo.steps.#{step}"
) %></span>
</li>
<% end %>
</ol>
<div class="step-indicator__controls">
<button type="button" class="demo-trigger" data-step-indicator-prev>
<%= t("components.step_indicator.demo.back") %>
</button>
<button type="button" class="demo-trigger" data-step-indicator-next>
<%= t("components.step_indicator.demo.advance") %>
</button>
</div>
</div>
/*
* Presentation-only styles for the step-indicator demo.
* Complete / current / upcoming are built by reacting to each li's data-state. The
* progress bar uses --stimeo--step-indicator-ratio (0–1, updated by the library) as its width.
*
* The ratio measures how far along the *range* the current step is, and the range runs
* from the first dot's centre to the last one's — so the track is inset by half a step
* (--steps is the count the template renders), not by half a dot. Insetting by the dot
* instead leaves the fill short of the first dot and past the last one.
*/
.step-indicator {
display: flex;
gap: 0;
margin: 0;
padding: 0;
list-style: none;
position: relative;
}
.step-indicator::before,
.step-indicator::after {
content: "";
position: absolute;
top: 0.5rem;
left: calc(100% / var(--steps, 2) / 2);
right: calc(100% / var(--steps, 2) / 2);
height: 2px;
background: var(--surface-subtle);
}
.step-indicator::after {
right: auto;
width: calc((100% - 100% / var(--steps, 2)) * var(--stimeo--step-indicator-ratio, 0));
background: var(--accent, var(--color-primary));
transition: width 0.2s ease;
}
.step-indicator__step {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.4rem;
font-size: 0.8125rem;
color: var(--color-text-subtle);
}
.step-indicator__dot {
position: relative;
z-index: 1;
width: 1rem;
height: 1rem;
border-radius: 50%;
background: var(--border-strong);
}
.step-indicator__step[data-state="complete"] .step-indicator__dot,
.step-indicator__step[data-state="current"] .step-indicator__dot {
background: var(--accent, var(--color-primary));
}
.step-indicator__step[data-state="current"] {
color: var(--fg, var(--color-text));
font-weight: 600;
}
.step-indicator__controls {
display: flex;
gap: 0.5rem;
margin-top: 1.25rem;
}
// step-indicator external-update demo (consumer-side JS).
//
// The core controller (stimeo--step-indicator) is read-only: it only holds the current
// step and syncs data-state / aria-current / the progress CSS variable (no interaction).
// Changing the current step is done externally via the step:set event by contract, so
// here the prev/next buttons read the current value and fire step:set with a clamped new index.
document.querySelectorAll('[data-controller~="stimeo--step-indicator"]').forEach((list) => {
const root = list.closest('.step-indicator-demo') ?? list.parentElement;
if (!root) return;
const total = list.querySelectorAll('[data-stimeo--step-indicator-target="step"]').length;
const attr = 'data-stimeo--step-indicator-current-value';
const move = (delta) => {
const current = Number(list.getAttribute(attr) ?? 0);
const next = Math.min(total - 1, Math.max(0, current + delta));
list.dispatchEvent(new CustomEvent('step:set', { detail: { current: next } }));
};
root.querySelector('[data-step-indicator-prev]')?.addEventListener('click', () => move(-1));
root.querySelector('[data-step-indicator-next]')?.addEventListener('click', () => move(1));
});
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--step-indicator"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
step
必須
|
各ステップ要素。data-state(complete/current/upcoming)と aria-current が付与される。 |
data-stimeo--step-indicator-target="step" |
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
current
|
現在ステップの0始まりインデックス。ステップ数に丸められる(既定 0)。 | data-stimeo--step-indicator-current-value |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
setCurrent
|
detail.current(0始まり・丸め)で現在ステップを更新し change を発火する。 | stimeo--step-indicator#setCurrent |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
change
|
現在ステップが変わったときに発火。detail.current / detail.total を伴う。 | stimeo--step-indicator:change |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
data-state |
ステップ(li) | 現在番号から導出した "complete" / "current" / "upcoming"。 |
aria-current |
現在ステップ(li) | 現在ステップにのみ "step"。 |
--stimeo--step-indicator-ratio |
コントローラ要素 | 全体の進捗割合(0–1)を CSS へ。 |