Skip to main content
xriptxr

Steps

Info:Navigation
16

A horizontal step indicator for a linear flow: done, current, and upcoming at a glance.

stepper wizard progress steps checkout onboarding flow

Live demo

live · @xtyle/astro

Steps

Checkout

  1. Cart
  2. Shipping
  3. Payment
  4. Review

Nearly done

  1. Account
  2. Profile
  3. Preferences
  4. Confirm

Nothing done yet

  1. Brief
  2. Draft
  3. Review

Steps with real content

  1. Upload Success:done
  2. Transcode Success:done
  3. Publish now

Zero-JS

The Astro binding splits the authored <li>s against current on the server, so the markers, the connectors, and the done / current / upcoming states are all resolved at build time. static keeps it that way: the stepper below never asks for the runtime and stays a complete, fully-styled indicator with zero JavaScript. Reacting to a changed current is what hydration adds on top.

  1. Authored
  2. Rendered
  3. Shipped
  4. Hydrated

Steps shows where a user is in a linear process: a checkout, an onboarding wizard, a multi-part form. Author a semantic ordered list of <li> steps and it splits them by the current index: everything before it is done (a filled marker with a check), the one at it is current (an outlined marker, flagged with aria-current), and everything after is upcoming (a muted, numbered marker).

A connector track fills in behind the markers up to the current step. The marker and the connector are real nodes rendered by the component's fill, not glyphs painted onto the author's markup, so a mod can put an icon in the marker, number the steps in roman, or draw the connector as a dashed arrow — while each step's own content is relocated into the marker's label region untouched, and the rendered list stays a semantic <ol> screen readers hear in order.

When to use

How this component composes with the rest of the set.

Drive current from the same state that gates a wizard's Next button, so the marker moves as the user advances.
Keep labels to a word or two; the row divides the width evenly, so long labels wrap under their marker.
For a form, pair it with a heading per step and swap the panel below as current changes.
The marker and the connector are the fill's own nodes: a mod filling component.steps can swap the check for an icon, number the steps in roman, or draw the connector as a dashed arrow, without the app changing a line.

Props

2 props, straight from the manifest.

PropTypeDefaultBindingsDescription
current number 0
html svelte astro
The zero-based index of the current step. Steps before it render as done (checked), the step at it as current (`aria-current="step"`), and steps after it as upcoming.
static boolean false
astro
Astro only: emit the server-rendered indicator but never load the runtime to hydrate it. The markers, connectors, and the done / current / upcoming split against `current` are resolved at build time, so a static stepper is complete; hydration only adds reacting to a changed `current` or a changed list. The Svelte and raw-element paths always upgrade, so they carry no equivalent.

Appearance

States

done

.xtyle-steps__step--done

A completed step: an accent-filled marker whose glyph is a check, and a filled connector into it.

current

.xtyle-steps__step--current

The active step: an accent-outlined marker and an emphasized label, carrying aria-current.

upcoming

.xtyle-steps__step--upcoming

A step not yet reached: a muted, numbered marker on the unfilled track.

Anatomy

The named parts that make up the component, with their selectors.

list

.xtyle-steps__list

The rendered ordered list, laid out as an even horizontal row of steps.

step

.xtyle-steps__step

One step: its marker over its label, joined to the previous step by the connector track. Carries any attributes the author put on the source <li>.

--space-2 --text-sm --fg-1 --weight-medium --fg-0

marker

.xtyle-steps__marker

The circular step marker: an ordinal when upcoming, a check when done, an accent outline when current. A real node, so a mod can replace the glyph with an icon or a different numbering.

--bg-1 --fg-2 --line --border-thick --radius-full --weight-semibold --text-sm --accent --accent-fg --accent-text --bg-0

connector

.xtyle-steps__connector

The track joining a step back to the one before it — accent-filled up to the current step, --line beyond it. The first step has none.

--line --accent --border-thick

label

.xtyle-steps__label

The region each step's authored content is relocated into, beneath its marker.

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 16/16 consumed tokens produced default register: 305 tokens

Live coverage check against the xtyle-default register (derive(xtyleDefault, { anchors })coverComponent(manifest, register)). Every token this component consumes must be a key the algorithm produces.

--accent --accent-fg --accent-text --bg-0 --bg-1 --border-thick --fg-0 --fg-1 --fg-2 --font-sans --line --radius-full --space-2 --text-sm --weight-medium --weight-semibold

Slots

default
html svelte astro

The ordered list. Provide an <ol> (or <ul>) whose <li> children are the steps, in order. Each step's content is relocated into the rendered marker's label region, and any attributes on the source <li> ride along onto the rendered step.

Accessibility

It renders a semantic ordered list, so assistive tech announces the steps in order; the current step carries aria-current="step" so it is announced as the active one.
State is not conveyed by color alone: a done step shows a check glyph and the current step is outlined and emphasized, so a color-deficient user still reads progress.
The marker and connector nodes are decorative and carry aria-hidden, so the step's own content is the only thing announced.

Code

Checkout flow

Four steps with the second active; the first is done and the rest are upcoming.

<xtyle-steps current="1">
	<ol>
		<li>Cart</li>
		<li>Shipping</li>
		<li>Payment</li>
		<li>Review</li>
	</ol>
</xtyle-steps>
<script lang="ts">
	import { Steps } from "@xtyle/svelte";

	let current = $state(1);
	const labels = ["Cart", "Shipping", "Payment", "Review"];
</script>

<Steps {current}>
	<ol>
		{#each labels as label (label)}<li>{label}</li>{/each}
	</ol>
</Steps>
---
import Steps from "@xtyle/astro/Steps.astro";
---

<Steps current={1}>
	<ol>
		<li>Cart</li>
		<li>Shipping</li>
		<li>Payment</li>
		<li>Review</li>
	</ol>
</Steps>