Timeline
Sequenced event displays — a free-form timeline and a stepper that covers both a numbered install/checkout sequence and a plain status stepper.
Stepper — A step-by-step layout with connector lines and complete/active/upcoming states, vertical or horizontal, numbered or a plain dot marker, for install guides, onboarding, checkout progress, fulfilment tracking and event timelines.
Preview
-
CompletedInstall the package
Add the dependency to your project.
-
2Configure the registry
Point the CLI at your registry endpoint.
-
3Add your first component
Run ui:add to copy editable Blade in.
-
CompletedCart
-
2Shipping
-
3Payment
-
CompletedOrder placed
Your order has been received and confirmed.
-
Preparing shipment
Your items are being packed and labeled.
-
Out for delivery
A courier will bring your package to your door.
<div class="grid w-full gap-10 lg:grid-cols-2">
<x-ui.stepper
:steps="[
['title' => 'Install the package', 'description' => 'Add the dependency to your project.', 'status' => 'completed'],
['title' => 'Configure the registry', 'description' => 'Point the CLI at your registry endpoint.', 'status' => 'active'],
['title' => 'Add your first component', 'description' => 'Run ui:add to copy editable Blade in.', 'status' => 'default'],
]"
/>
<x-ui.stepper orientation="horizontal" class="self-start">
<x-ui.stepper.item step="1" title="Cart" status="completed" />
<x-ui.stepper.item step="2" title="Shipping" status="active" />
<x-ui.stepper.item step="3" title="Payment" status="default" :last="true" />
</x-ui.stepper>
{{-- numbered=false: a plain dot marker for a status timeline (the look
formerly shipped as the separate timeline-steps component). --}}
<x-ui.stepper :numbered="false" class="max-w-md">
<x-ui.stepper.item status="completed" title="Order placed" description="Your order has been received and confirmed." />
<x-ui.stepper.item status="active" title="Preparing shipment" description="Your items are being packed and labeled." />
<x-ui.stepper.item status="default" title="Out for delivery" description="A courier will bring your package to your door." :last="true" />
</x-ui.stepper>
</div>
Orientation options
Align options
Tone options
Installation
php artisan ui:add stepper
Registry contract
php artisan ui:add stepper
writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.
-
resources/views/components/ui/stepper.blade.php -
resources/views/components/ui/stepper/item.blade.php
- Registry dependencies
- None — installs on its own.
- Packages
-
composer: jml/brok:^0.2
Use with AI
A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.
# Brok UI: Stepper (`stepper`)
A step-by-step layout with connector lines and complete/active/upcoming states, vertical or horizontal, numbered or a plain dot marker, for install guides, onboarding, checkout progress, fulfilment tracking and event timelines.
Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.
## Install
```bash
php artisan ui:add stepper
```
## Usage
```blade
<div class="grid w-full gap-10 lg:grid-cols-2">
<x-ui.stepper
:steps="[
['title' => 'Install the package', 'description' => 'Add the dependency to your project.', 'status' => 'completed'],
['title' => 'Configure the registry', 'description' => 'Point the CLI at your registry endpoint.', 'status' => 'active'],
['title' => 'Add your first component', 'description' => 'Run ui:add to copy editable Blade in.', 'status' => 'default'],
]"
/>
<x-ui.stepper orientation="horizontal" class="self-start">
<x-ui.stepper.item step="1" title="Cart" status="completed" />
<x-ui.stepper.item step="2" title="Shipping" status="active" />
<x-ui.stepper.item step="3" title="Payment" status="default" :last="true" />
</x-ui.stepper>
{{-- numbered=false: a plain dot marker for a status timeline (the look
formerly shipped as the separate timeline-steps component). --}}
<x-ui.stepper :numbered="false" class="max-w-md">
<x-ui.stepper.item status="completed" title="Order placed" description="Your order has been received and confirmed." />
<x-ui.stepper.item status="active" title="Preparing shipment" description="Your items are being packed and labeled." />
<x-ui.stepper.item status="default" title="Out for delivery" description="A courier will bring your package to your door." :last="true" />
</x-ui.stepper>
</div>
```
## Props
- `orientation` (vertical|horizontal, default `vertical`) — Rail direction. Vertical stacks marker over connector; horizontal lays the steps across the row.
- `align` (start|center, default `start`) — Horizontal only. `center` puts each marker in the middle of its own column with the label beneath it and draws the rail marker-centre to marker-centre — the checkout/fulfilment shape.
- `steps` (array, default `[]`) — Step entries: ['step','title','description','status','icon']. `icon` takes an Htmlable (e.g. new HtmlString('<svg …>')) shown in the marker for non-completed steps; a completed step always shows the check. Omit `steps` to compose <x-ui.stepper.item> children by hand.
- `numbered` (bool, default `true`) — false hides the step number (and the icon fallback slot) and leaves a plain dot marker — the look formerly shipped as the separate timeline-steps component, now this component's own event-timeline mode. A completed step always shows its check regardless.
- `bind` (string|null, default `null`) — An Alpine expression for the current 1-based step (`step`). Every item then follows it live: marker, connector, check and aria-current update as the value changes, so a wizard needs no second rail.
- `tone` (primary|neutral, default `primary`) — Colour of the current and completed markers and of the done connectors: primary uses the brand primary; neutral uses the foreground colour, so a product that keeps primary for its one main action is not diluted by progress marks. Upcoming steps stay muted in both.
- `step` (mixed|null, default `null`) — Declared by @props in the registry Blade source.
- `title` (mixed|null, default `null`) — Declared by @props in the registry Blade source.
- `description` (mixed|null, default `null`) — Declared by @props in the registry Blade source.
- `status` (string, default `default`) — Declared by @props in the registry Blade source.
- `icon` (mixed|null, default `null`) — Declared by @props in the registry Blade source.
- `last` (bool, default `false`) — Declared by @props in the registry Blade source.
## Use when
- Use to show real progress, ordered stages, or process status when visibility helps completion.
- Showing where someone is in a fixed, ordered sequence — an install guide, an onboarding flow, a checkout.
- Reporting fulfilment progress across named stages: orientation="horizontal" align="center" with a per-step icon.
## Avoid when
- Do not add progress UI to tiny tasks or to flows whose steps are not meaningfully sequential.
- Use for ordered stages with clear progression; start complex flows as focused steps and merge only when research supports it.
- The entries are dated events rather than a sequence to finish — use <x-ui.timeline>.
- There is one continuous quantity to report — use <x-ui.progress>.
## Anti-patterns
- Decorative or inaccurate progress
- Steppers for nonsequential exploration
## Rules
- Use the `<brok:stepper>` tag (or `<x-ui.stepper>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.
## Links
- Docs: https://brokui.dev/docs/components/stepper
- Registry JSON (files, props, contract): https://brokui.dev/r/open/stepper.json
Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.
Examples
Horizontal
@php
$icon = fn (string $path) => new \Illuminate\Support\HtmlString(
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="size-4" aria-hidden="true"><path d="'.$path.'" /></svg>'
);
@endphp
<div class="flex w-full flex-col gap-10">
<x-ui.stepper
orientation="horizontal"
align="center"
:steps="[
['title' => __('Cart'), 'status' => 'completed'],
['title' => __('Shipping'), 'status' => 'completed'],
['title' => __('Payment'), 'status' => 'active'],
['title' => __('Review'), 'status' => 'default'],
]"
/>
<x-ui.stepper
orientation="horizontal"
align="center"
:steps="[
['title' => __('Ordered'), 'status' => 'completed', 'icon' => $icon('M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4zM3 6h18M16 10a4 4 0 0 1-8 0')],
['title' => __('Packed'), 'status' => 'completed', 'icon' => $icon('M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z')],
['title' => __('Shipped'), 'status' => 'active', 'icon' => $icon('M10 17h4V5H2v12h3m15 0h2v-3.34a4 4 0 0 0-1.17-2.83L19 9h-5v8h1')],
['title' => __('Delivered'), 'status' => 'default', 'icon' => $icon('M20 6 9 17l-5-5')],
]"
/>
</div>
{{-- tone="primary" (the default) draws the current and completed marks in the
brand primary; tone="neutral" uses the foreground colour, for a product
that keeps primary for its one main action. --}}
<div class="flex w-full flex-col gap-10">
<x-ui.stepper
orientation="horizontal"
align="center"
:steps="[
['title' => __('Account'), 'status' => 'completed'],
['title' => __('Workspace'), 'status' => 'active'],
['title' => __('Invite team'), 'status' => 'default'],
]"
/>
<x-ui.stepper
tone="neutral"
orientation="horizontal"
align="center"
:steps="[
['title' => __('Account'), 'status' => 'completed'],
['title' => __('Workspace'), 'status' => 'active'],
['title' => __('Invite team'), 'status' => 'default'],
]"
/>
</div>
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| orientation | vertical | horizontal | vertical | Rail direction. Vertical stacks marker over connector; horizontal lays the steps across the row. |
| align | start | center | start | Horizontal only. `center` puts each marker in the middle of its own column with the label beneath it and draws the rail marker-centre to marker-centre — the checkout/fulfilment shape. |
| steps | array | [] | Step entries: ['step','title','description','status','icon']. `icon` takes an Htmlable (e.g. new HtmlString('<svg …>')) shown in the marker for non-completed steps; a completed step always shows the check. Omit `steps` to compose <x-ui.stepper.item> children by hand. |
| numbered | bool | true | false hides the step number (and the icon fallback slot) and leaves a plain dot marker — the look formerly shipped as the separate timeline-steps component, now this component's own event-timeline mode. A completed step always shows its check regardless. |
| bind | string | null | null | An Alpine expression for the current 1-based step (`step`). Every item then follows it live: marker, connector, check and aria-current update as the value changes, so a wizard needs no second rail. |
| tone | primary | neutral | primary | Colour of the current and completed markers and of the done connectors: primary uses the brand primary; neutral uses the foreground colour, so a product that keeps primary for its one main action is not diluted by progress marks. Upcoming steps stay muted in both. |
| step | mixed | null | null | Declared by @props in the registry Blade source. |
| title | mixed | null | null | Declared by @props in the registry Blade source. |
| description | mixed | null | null | Declared by @props in the registry Blade source. |
| status | string | default | Declared by @props in the registry Blade source. |
| icon | mixed | null | null | Declared by @props in the registry Blade source. |
| last | bool | false | Declared by @props in the registry Blade source. |
Slots
default— Primary Blade slot rendered by the component.x-ui.stepper.item— Installed subcomponent from the registry item.
Data slots
Stable hooks for CSS overrides and browser tests.
Behavior
- With `bind`, the server-rendered statuses are the first paint and the Alpine expression drives them afterwards through object-form class bindings, so exactly one state's classes stay on each marker.
- Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.
Guidance
Communicate genuine progress or ordered stages.
Use when
- Use to show real progress, ordered stages, or process status when visibility helps completion.
- Showing where someone is in a fixed, ordered sequence — an install guide, an onboarding flow, a checkout.
- Reporting fulfilment progress across named stages: orientation="horizontal" align="center" with a per-step icon.
Avoid when
- Do not add progress UI to tiny tasks or to flows whose steps are not meaningfully sequential.
- Use for ordered stages with clear progression; start complex flows as focused steps and merge only when research supports it.
- The entries are dated events rather than a sequence to finish — use <x-ui.timeline>.
- There is one continuous quantity to report — use <x-ui.progress>.
Use instead
- Simple completion text for tiny tasks
Anti-patterns
- Decorative or inaccurate progress
- Steppers for nonsequential exploration
- Anatomy
- Theming hooks
Accessibility
- Keyboard
- managed
- Focus
none
- Renders an ordered list, so assistive technology announces the step count and position; the active step carries aria-current="step", also when it follows a bound value.
- Connectors and the complete check are decorative (aria-hidden); a completed step also says "Completed" in visually hidden text, so the state never depends on the icon or colour alone.
- The active-step pulse runs only under prefers-reduced-motion: no-preference.
- Semantic HTML and a stable
data-slotattribute for styling and scripting hooks. - Focus-visible rings use the
ringtoken, so keyboard focus is always visible. - Disabled and invalid states are conveyed to assistive tech, not by color alone.
- Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
- Labels go through
__()and layout uses logical properties (ms-*,text-start), so it mirrors underdir="rtl"— flip the preview to RTL to confirm. - Dark mode uses the same semantic tokens under the
darkclass; high contrast follows forced-color system tokens.
Livewire
Add a stable wire:key when Livewire can reorder this interactive component.
<div wire:key="stepper-{{ $record->id }}">
<div class="grid w-full gap-10 lg:grid-cols-2">
<x-ui.stepper
:steps="[
['title' => 'Install the package', 'description' => 'Add the dependency to your project.', 'status' => 'completed'],
['title' => 'Configure the registry', 'description' => 'Point the CLI at your registry endpoint.', 'status' => 'active'],
['title' => 'Add your first component', 'description' => 'Run ui:add to copy editable Blade in.', 'status' => 'default'],
]"
/>
<x-ui.stepper orientation="horizontal" class="self-start">
<x-ui.stepper.item step="1" title="Cart" status="completed" />
<x-ui.stepper.item step="2" title="Shipping" status="active" />
<x-ui.stepper.item step="3" title="Payment" status="default" :last="true" />
</x-ui.stepper>
{{-- numbered=false: a plain dot marker for a status timeline (the look
formerly shipped as the separate timeline-steps component). --}}
<x-ui.stepper :numbered="false" class="max-w-md">
<x-ui.stepper.item status="completed" title="Order placed" description="Your order has been received and confirmed." />
<x-ui.stepper.item status="active" title="Preparing shipment" description="Your items are being packed and labeled." />
<x-ui.stepper.item status="default" title="Out for delivery" description="A courier will bring your package to your door." :last="true" />
</x-ui.stepper>
</div>
</div>
Source
The exact, editable files ui:add writes
into your app. Previews render this same code; there are no preview-only components.
@props([
'orientation' => 'vertical',
'align' => 'start',
'steps' => [],
// false hides the step number/icon fallback and leaves a plain dot marker —
// the timeline-steps look, now a mode of this component instead of a
// second one. A completed step still shows its check either way.
'numbered' => true,
// An Alpine expression for the current 1-based step (`step`): every item
// then follows it live — marker, connector, check and aria-current — so a
// wizard never needs a second, hand-rolled rail beside the server one.
'bind' => null,
// Colour of the current and completed marks. primary (default) uses the
// brand primary; neutral uses the foreground colour, so a product that
// keeps primary for its one main action is not diluted by progress marks.
'tone' => 'primary',
])
@php
// Accept a string, a backed enum or a Stringable for `tone`.
$styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
$tone = $styles['normalizeVariant']($tone);
$tone = in_array($tone, ['primary', 'neutral'], true) ? $tone : 'primary';
$orientation = in_array($orientation, ['vertical', 'horizontal'], true) ? $orientation : 'vertical';
$numbered = filter_var($numbered, FILTER_VALIDATE_BOOLEAN);
// `align` only bites horizontally. `center` is the checkout/fulfilment shape:
// each marker centred in its own column with the label beneath it and the
// connector drawn marker-centre to marker-centre behind them.
$align = in_array($align, ['start', 'center'], true) ? $align : 'start';
// Array mode: render <x-ui.stepper.item> for each entry, auto-numbering and
// flagging the last item so it omits its trailing connector. Slot mode lets
// callers compose items by hand (with explicit `step`/`status`/nested content).
$base = 'relative '.($orientation === 'horizontal' ? 'flex w-full' : 'flex flex-col');
@endphp
{{-- v1.1.0: CSS-only progress reveal. Server-rendered steps animate a one-shot
reveal on load — completed connectors fill in (scaleX/Y on a pseudo-overlay,
transform-only, no layout thrash) and the active marker pulses in with a
ring. Purely decorative; fully gated behind prefers-reduced-motion. No JS. --}}
<style>
@media (prefers-reduced-motion: no-preference) {
@keyframes ui-stepper-fill-x {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
@keyframes ui-stepper-fill-y {
from { transform: scaleY(0); }
to { transform: scaleY(1); }
}
@keyframes ui-stepper-pulse-in {
0% { transform: scale(0.85); box-shadow: 0 0 0 0 color-mix(in oklab, var(--ui-stepper-ring) calc(0.45 * 100%), transparent); }
60% { transform: scale(1); }
100% { transform: scale(1); box-shadow: 0 0 0 0.375rem color-mix(in oklab, var(--ui-stepper-ring) calc(0 * 100%), transparent); }
}
/* The pulse ring takes the tone's colour. */
[data-slot="stepper"] {
--ui-stepper-ring: var(--primary-color, hsl(var(--primary)));
}
[data-slot="stepper"][data-tone="neutral"] {
--ui-stepper-ring: var(--foreground-color, hsl(var(--foreground)));
}
/* Animate completed connectors growing toward the next step. The overlay
inherits the connector's own border, so colour stays token-driven. */
[data-slot="stepper"]:not([data-align="center"]) [data-status="completed"] [data-slot="stepper-connector"] {
position: relative;
}
[data-slot="stepper"] [data-status="completed"] [data-slot="stepper-connector"]::after {
content: "";
position: absolute;
inset: -1px;
border: inherit;
border-color: inherit;
animation: ui-stepper-fill-x 0.5s ease-out both;
}
[data-slot="stepper"][data-orientation="vertical"] [data-status="completed"] [data-slot="stepper-connector"]::after {
transform-origin: top;
animation-name: ui-stepper-fill-y;
}
[data-slot="stepper"][data-orientation="horizontal"] [data-status="completed"] [data-slot="stepper-connector"]::after {
transform-origin: inline-start;
}
/* Active marker pulses in with a fading ring in the tone's colour. */
[data-slot="stepper"] [data-status="active"] [data-slot="stepper-marker"] {
animation: ui-stepper-pulse-in 0.6s ease-out both;
}
}
</style>
<ol
data-slot="stepper"
data-orientation="{{ $orientation }}"
data-align="{{ $align }}"
data-tone="{{ $tone }}"
@if ($bind !== null) data-bound="true" @endif
{{ $attributes->merge(['class' => $base]) }}
>
@if (! empty($steps))
@foreach ($steps as $i => $step)
<x-ui.stepper.item
:step="$step['step'] ?? $i + 1"
:title="$step['title'] ?? null"
:description="$step['description'] ?? null"
:status="$step['status'] ?? 'default'"
:icon="$step['icon'] ?? null"
:orientation="$orientation"
:align="$align"
:numbered="$numbered"
:bind="$bind"
:tone="$tone"
:last="$loop->last"
/>
@endforeach
@else
{{ $slot }}
@endif
</ol>
@props([
'step' => null,
'title' => null,
'description' => null,
'status' => 'default',
'icon' => null,
'last' => false,
'orientation' => 'vertical',
'align' => 'start',
'numbered' => true,
'bind' => null,
'tone' => 'primary',
])
@aware(['orientation' => 'vertical', 'align' => 'start', 'numbered' => true, 'bind' => null, 'tone' => 'primary'])
@php
// timeline-steps' vocabulary (upcoming|current|complete) is accepted as a
// backward-compatible alias for this component's own (default|active|completed).
$status = ['upcoming' => 'default', 'current' => 'active', 'complete' => 'completed'][$status] ?? $status;
$status = in_array($status, ['default', 'active', 'completed'], true) ? $status : 'default';
$orientation = in_array($orientation, ['vertical', 'horizontal'], true) ? $orientation : 'vertical';
$numbered = filter_var($numbered, FILTER_VALIDATE_BOOLEAN);
$align = in_array($align, ['start', 'center'], true) ? $align : 'start';
$isLast = filter_var($last, FILTER_VALIDATE_BOOLEAN);
// Centred columns only exist horizontally; a vertical rail is always start-aligned.
$isCentered = $orientation === 'horizontal' && $align === 'center';
// Accept a string, a backed enum or a Stringable for `tone`.
$styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
$tone = $styles['normalizeVariant']($tone);
// Full class strings per tone so Tailwind's JIT can see every utility.
$tones = [
'primary' => [
'completed' => 'border-primary bg-primary text-primary-foreground',
'active' => 'border-primary bg-background text-primary',
'connector' => 'border-primary',
],
'neutral' => [
'completed' => 'border-foreground bg-foreground text-background',
'active' => 'border-foreground bg-background text-foreground',
'connector' => 'border-foreground',
],
];
$tone = isset($tones[$tone]) ? $tone : 'primary';
$markerStates = [
'completed' => $tones[$tone]['completed'],
'active' => $tones[$tone]['active'],
'default' => 'border-border bg-background text-muted-foreground',
];
$markerClasses = 'relative z-10 flex size-8 shrink-0 items-center justify-center rounded-full border text-sm font-medium transition-colors motion-reduce:transition-none '.$markerStates[$status];
// Completed steps draw a "done" connector toward the next step.
$doneConnector = $tones[$tone]['connector'];
$connectorColor = $status === 'completed' ? $doneConnector : 'border-border';
$connectorBase = 'transition-colors motion-reduce:transition-none '.$connectorColor;
// Live mode: the stepper's `bind` names the current step in Alpine scope;
// the status is derived from it per item and the same class maps apply.
$live = $bind !== null && $step !== null;
$liveStatus = $live ? "(({$bind}) > {$step} ? 'completed' : (({$bind}) === {$step} ? 'active' : 'default'))" : null;
// Object-form class bindings, so the state a class set belongs to is the
// only thing that keeps it on the element (a falsy entry removes the class
// even when the server rendered it).
$liveMarkerClass = $live ? sprintf(
"{ '%s': (%s) > %d, '%s': (%s) === %d, '%s': (%s) < %d }",
$markerStates['completed'], $bind, $step,
$markerStates['active'], $bind, $step,
$markerStates['default'], $bind, $step,
) : null;
$liveConnectorClass = $live ? "{ '{$doneConnector}': ({$bind}) > {$step}, 'border-border': ({$bind}) <= {$step} }" : null;
$liveTitleClass = $live ? "{ 'text-foreground': ({$bind}) >= {$step}, 'text-muted-foreground': ({$bind}) < {$step} }" : null;
$itemLayout = match (true) {
$isCentered => 'min-w-0 flex-1 flex-col items-center gap-2 text-center',
$orientation === 'horizontal' => 'flex-1 flex-col items-start gap-2',
default => 'gap-3',
};
$markerRow = match (true) {
$isCentered => 'relative w-full items-center justify-center',
$orientation === 'horizontal' => 'relative w-full items-center',
default => 'relative flex-col items-center self-stretch',
};
@endphp
<li
data-slot="stepper-item"
data-status="{{ $status }}"
@if ($status === 'active') aria-current="step" @endif
@if ($live)
x-bind:data-status="{{ $liveStatus }}"
x-bind:aria-current="({{ $bind }}) === {{ $step }} ? 'step' : false"
@endif
{{ $attributes->merge(['class' => 'relative flex '.$itemLayout]) }}
>
<div class="flex {{ $markerRow }}">
@if ($isCentered)
{{-- One rail per gap, drawn from this marker's centre to the next one's.
The column is exactly one step wide, so `w-full` from the midpoint
lands on the neighbouring marker; the marker's own z-10 covers it. --}}
@unless ($isLast)
<span data-slot="stepper-connector" aria-hidden="true" class="absolute start-1/2 top-1/2 h-px w-full -translate-y-1/2 border-t {{ $connectorBase }}" @if ($live) x-bind:class="{{ $liveConnectorClass }}" @endif></span>
@endunless
@endif
<span data-slot="stepper-marker" class="{{ $markerClasses }}" @if ($live) x-bind:class="{{ $liveMarkerClass }}" @endif>
@if ($live)
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="size-4" aria-hidden="true" x-show="({{ $bind }}) > {{ $step }}" @if ($status !== 'completed') style="display: none;" @endif>
<path d="M20 6 9 17l-5-5" />
</svg>
<span class="sr-only" x-show="({{ $bind }}) > {{ $step }}" @if ($status !== 'completed') style="display: none;" @endif>{{ __('Completed') }}</span>
<span x-show="({{ $bind }}) <= {{ $step }}" @if ($status === 'completed') style="display: none;" @endif>
@if ($icon !== null)
{{ $icon }}
@elseif ($numbered)
{{ $step }}
@endif
</span>
@elseif ($status === 'completed')
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="size-4" aria-hidden="true">
<path d="M20 6 9 17l-5-5" />
</svg>
{{-- The check is decorative; the state must also reach assistive tech
in text (WCAG 1.3.1, 1.4.1). The active step has aria-current. --}}
<span class="sr-only">{{ __('Completed') }}</span>
@elseif ($icon !== null)
{{ $icon }}
@elseif ($numbered)
{{ $step }}
@endif
</span>
@unless ($isLast || $isCentered)
@if ($orientation === 'horizontal')
<span data-slot="stepper-connector" aria-hidden="true" class="ms-2 h-px flex-1 border-t {{ $connectorBase }}" @if ($live) x-bind:class="{{ $liveConnectorClass }}" @endif></span>
@else
<span data-slot="stepper-connector" aria-hidden="true" class="mt-1 w-0 flex-1 border-s {{ $connectorBase }}" @if ($live) x-bind:class="{{ $liveConnectorClass }}" @endif></span>
@endif
@endunless
</div>
<div data-slot="stepper-content" class="{{ $orientation === 'vertical' ? 'pb-6' : ($isCentered ? 'min-w-0' : '') }}">
@if ($title !== null)
<div data-slot="stepper-title" class="font-medium leading-8 text-foreground" @if ($live) x-bind:class="{{ $liveTitleClass }}" @endif>{{ $title }}</div>
@endif
@if ($description !== null)
<p data-slot="stepper-description" class="text-sm text-muted-foreground">{{ $description }}</p>
@endif
@if (trim($slot ?? '') !== '')
<div class="mt-2 text-sm text-muted-foreground">{{ $slot }}</div>
@endif
</div>
</li>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.5.2 - Status
- Stable
- License
-
open - Accessibility reviewed
- No review date recorded
- Last breaking change
- No date recorded
- Deprecation
- Not deprecated
- Contract
-
v6 - Foundation
-
≥ 1.0.0