Stepper
A multi-step progress indicator, built on Reka UI's Stepper primitive.
Usage
Stepper only tracks and displays which step is current - v-model
is a plain 1-indexed number, and pairing it with your own step content
and Back/Next buttons is up to you, the same way v-model composes with
anything else:
Cart
Shipping
Payment
Step 1: Cart
<script setup lang="ts">
const items = [{ title: 'Cart' }, { title: 'Shipping' }, { title: 'Payment' }]
const step = ref(1)
</script>
<template>
<SStepper v-model="step" :items="items" />
<SButton :disabled="step === 1" @click="step--">
Back
</SButton>
<SButton :disabled="step === items.length" @click="step++">
Next
</SButton>
</template>
By default, clicking a step only ever advances at most one step ahead of
the current one - going back to any earlier (completed) step is always
allowed. Pass linear="false" to allow jumping to any step directly:
One
Two
Three
Four
<SStepper :items="items" :linear="false" />
Descriptions
Each item can carry a description alongside its title:
Address
Where to ship
Delivery
Pick a speed
Review
Confirm order
<SStepper :items="[{ title: 'Address', description: 'Where to ship' }, ...]" />
Icons
Each item can carry an icon, shown once its step is active or upcoming
- a completed step always shows a checkmark instead, regardless of
icon:
Account
Preferences
Billing
<SStepper :items="[{ title: 'Account', icon: 'hugeicons:user' }, ...]" />
Custom slots
#indicator, #title, and #description each replace that part
entirely, scoped with the item, its index, and its state - useful for
always showing an item's own icon (instead of falling back to a
checkmark once completed) or annotating the active step:
Draft
Review · current
Published
<SStepper :items="items">
<template #indicator="{ item }">
<SIcon :name="item.icon" class="size-4" />
</template>
<template #title="{ item, state }">
{{ item.title }}
<span v-if="state === 'active'">· current</span>
</template>
</SStepper>
Vertical
Account
Create your login
Profile
Tell us about yourself
Confirm
Review and finish
<SStepper :items="items" orientation="vertical" />
Color
One
Two
Three
One
Two
Three
One
Two
Three
One
Two
Three
One
Two
Three
One
Two
Three
One
Two
Three
<SStepper :items="items" color="success" />
Progress announcements
Stepper has one visually hidden, polite live region announcing the current step.
Its English default is Step 1 of 2. Translate it globally through the
message registry:
export default defineAppConfig({
selaras: {
messages: {
stepper: 'Progres',
stepperProgress: (step, total) => `Langkah ${step} dari ${total}`,
},
},
})
stepper names the root group; an explicit aria-label on the component takes
priority. stepperProgress(step, total) formats the live-region message.
Changing selaras.locale formats locale-aware values but does not translate UI
messages automatically. Update the message registry when switching languages;
announcements react to those changes and to step or item-count changes.
For one instance, the progress slot replaces only the announcement text.
Keep it concise; the component supplies the live-region semantics and hiding.
An empty item list has no default announcement.
Atur kunci
Simpan kunci
<SStepper v-model="step" :items="items" aria-label="Progres pembuatan kunci">
<template #progress="{ step: currentStep, total }">
Langkah {{ currentStep }} dari {{ total }}
</template>
</SStepper>
In markdown
Every Selaras component is already globally registered under its S-prefixed
name - a bare ::s-stepper{...} block works with no extra wiring (see
Callout's own "In markdown" section for how
the pattern works generally). items is an array prop, not a string - bind
it with a leading : so @nuxtjs/mdc parses the attribute as an expression
instead of literal text. Unlike Tabs or
Accordion, each step's own text comes
from its title/description fields directly, not a per-item slot - the
block needs no body at all:
::s-stepper{:items='[{"title":"Cart"},{"title":"Shipping"},{"title":"Payment"}]'}
::
Custom :ui
Global and scoped recipe overrides use ui.stepper.
To see exactly what you'd be overriding - the current default classes for
every slot and variant - here's Stepper's own theme file:
Props
| Prop | Type | Default |
|---|---|---|
items | Step[] | - |
modelValue | number | - |
defaultValue | number | - |
linear | boolean | true |
orientation | 'horizontal' | 'vertical' | horizontal |
size | 'sm' | 'md' | 'lg' | md |
color | 'primary' | 'neutral' | 'secondary' | 'success' | 'danger' | 'info' | 'warning' | primary |
ui | Partial<Record<StepperSlot, string | object>> | - |
Step is { title?: string, description?: string, icon?: string, disabled?: boolean }.
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | number | Fires when a step's trigger is clicked |
Slots
| Slot | Props | Description |
|---|---|---|
progress | { step, total } | Replaces the screen-reader progress text; step may be undefined when no step is active |
indicator | { item, index, state } | Replaces the indicator's content (default: a checkmark once completed, else the item's icon, else the step number) |
title | { item, index, state } | Replaces the title text |
description | { item, index, state } | Replaces the description text |
state is 'inactive' \| 'active' \| 'completed'.