Use steppers to visualize a group of connected actions or the order of a workflow.
import {StepperModule} from "@qualcomm-ui/angular/stepper"Examples
Linear Mode
The stepper is linear by default (linear is true). Steps must be completed in order.
- Backward navigation is always allowed
- Previously visited steps can be re-clicked without restriction
- Forward navigation is restricted to the immediate next step
- canGoToStep can override forward navigation decisions
<div q-stepper-root [count]="items.length">
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
{{ item.title }} - {{ item.description }}
</div>
}
<div q-stepper-completed-content>All steps completed.</div>
<div class="mt-6 flex justify-between">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
>
Back
</button>
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
variant="outline"
>
Next
</button>
</div>
</div>
Non-linear Mode
Set linear to false to allow accessing any step at any time. Use the completed prop to manually track which steps are done.
canGoToStep applies to all navigation if provided. Otherwise, all navigation is allowed.
<div q-stepper-root [count]="items.length" [linear]="false">
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
{{ item.title }} - {{ item.description }}
</div>
}
</div>
Icon
Use the q-stepper-indicator-icon directive on an <svg> element to use an icon for each step's indicator.
<div q-stepper-root [count]="items.length">
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>
<svg q-stepper-indicator-icon [qIcon]="item.icon"></svg>
</div>
<span q-stepper-label>{{ item.title }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
{{ item.content }}
</div>
}
<div q-stepper-completed-content>All steps completed.</div>
<div class="mt-6 flex justify-between">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
>
Back
</button>
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
variant="outline"
>
Next
</button>
</div>
</div>
Hint
Add a hint to provide additional context below the step label.
<div q-stepper-root [count]="items.length" [defaultStep]="1">
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
<span q-stepper-hint>{{ item.hint }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
{{ item.content }}
</div>
}
<div q-stepper-completed-content>All steps completed.</div>
<div class="mt-6 flex justify-between">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
>
Back
</button>
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
variant="outline"
>
Next
</button>
</div>
</div>
Layout & Orientation
Stepper orientation and label placement are controlled through the orientation input.
Horizontal
horizontal (default): The step list is centered horizontally, and the labels are stacked vertically and centered below the step indicator.
<div q-stepper-root [count]="items.length" [defaultStep]="1">
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
<span q-stepper-hint>{{ item.hint }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
{{ item.content }}
</div>
}
<div q-stepper-completed-content>All steps completed.</div>
<div class="mt-6 flex justify-between">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
>
Back
</button>
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
variant="outline"
>
Next
</button>
</div>
</div>
Horizontal Inline
horizontal-inline is a variant of horizontal that places the step indicator and labels inline. The step items are left-aligned and take the full width of their parent container
<div orientation="horizontal-inline" q-stepper-root [count]="items.length">
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
<span q-stepper-hint>{{ item.description }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
{{ item.title }} - {{ item.description }}
</div>
}
<div q-stepper-completed-content>All steps completed.</div>
<div class="mt-6 flex justify-between">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
>
Back
</button>
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
variant="outline"
>
Next
</button>
</div>
</div>
Horizontal Bottom Start
horizontal-bottom-start is a variant of horizontal that places the labels below each step, aligned to the start. The items are left-aligned and take the full width of their parent container.
<div
orientation="horizontal-bottom-start"
q-stepper-root
[count]="items.length"
>
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
<span q-stepper-hint>{{ item.description }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
{{ item.title }} - {{ item.description }}
</div>
}
<div q-stepper-completed-content>All steps completed.</div>
<div class="mt-6 flex justify-between">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
>
Back
</button>
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
variant="outline"
>
Next
</button>
</div>
</div>
Vertical
Set orientation to vertical to stack the steps vertically. The labels are centered relative to the step indicator.
<div
class="min-h-96"
orientation="vertical"
q-stepper-root
[count]="items.length"
>
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
<span q-stepper-hint>{{ item.content }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
<div class="flex flex-col gap-4">
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
{{ item.content }}
</div>
}
<div q-stepper-completed-content>All steps completed.</div>
<div class="mt-6 flex gap-2">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
>
Back
</button>
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
variant="outline"
>
Next
</button>
</div>
</div>
</div>
Vertical Inline
Set orientation to vertical-inline to stack the steps vertically while aligning the labels inline with the step indicator.
<div
class="min-h-96"
orientation="vertical-inline"
q-stepper-root
[count]="items.length"
>
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
<span q-stepper-hint>{{ item.content }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
<div class="flex flex-col gap-4">
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
{{ item.content }}
</div>
}
<div q-stepper-completed-content>All steps completed.</div>
<div class="mt-6 flex gap-2">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
>
Back
</button>
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
variant="outline"
>
Next
</button>
</div>
</div>
</div>
Controlled State
Use step and (stepChanged) to control the active step externally.
import {Component, signal} from "@angular/core"
import {ChevronLeft, ChevronRight} from "lucide-angular"
import {provideIcons} from "@qualcomm-ui/angular-core/lucide"
import {ButtonModule} from "@qualcomm-ui/angular/button"
import {StepperModule} from "@qualcomm-ui/angular/stepper"
@Component({
imports: [StepperModule, ButtonModule],
providers: [provideIcons({ChevronLeft, ChevronRight})],
selector: "stepper-controlled-demo",
template: `
<div
q-stepper-root
[count]="items.length"
[step]="step()"
(stepChanged)="step.set($event)"
>
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
{{ item.content }}
</div>
}
<div q-stepper-completed-content>All steps completed.</div>
<div class="mt-6 flex justify-between">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
>
Back
</button>
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
variant="outline"
>
Next
</button>
</div>
</div>
`,
})
export class StepperControlledDemo {
readonly items = [
{content: "Contact details", title: "Step 1", value: "step-1"},
{content: "Payment info", title: "Step 2", value: "step-2"},
{content: "Confirmation", title: "Step 3", value: "step-3"},
]
readonly step = signal(0)
}Manual Completion
Set linear to false and use the completed input to manually control which steps are marked as complete.
import {Component, computed, signal} from "@angular/core"
import {ChevronLeft, ChevronRight} from "lucide-angular"
import {provideIcons} from "@qualcomm-ui/angular-core/lucide"
import {ButtonModule} from "@qualcomm-ui/angular/button"
import {StepperModule} from "@qualcomm-ui/angular/stepper"
@Component({
imports: [StepperModule, ButtonModule],
providers: [provideIcons({ChevronLeft, ChevronRight})],
selector: "stepper-completed-demo",
template: `
<div
q-stepper-root
[completed]="completed()"
[count]="items.length"
[linear]="false"
>
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
<div class="flex items-center gap-4">
<span>{{ item.title }} content</span>
<button
q-button
size="sm"
[variant]="completed()[i] ? 'outline' : 'fill'"
(click)="toggleCompleted(i)"
>
{{ completed()[i] ? "Mark Incomplete" : "Mark Complete" }}
</button>
</div>
</div>
}
<div q-stepper-completed-content>All steps completed.</div>
<div class="mt-6 flex justify-between">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
>
Back
</button>
<ng-container *stepperContext="let api">
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
variant="outline"
[disabled]="
!api.hasNextStep ||
(api.step === api.count - 1 && !allCompleted())
"
>
Next
</button>
</ng-container>
</div>
</div>
`,
})
export class StepperCompletedDemo {
readonly items = [
{title: "Account", value: "account"},
{title: "Profile", value: "profile"},
{title: "Review", value: "review"},
]
readonly completed = signal<Record<number, boolean>>({})
readonly allCompleted = computed(() => {
const completed = this.completed()
return this.items.every((_, index) => completed[index])
})
toggleCompleted(index: number) {
this.completed.update((prev) => ({...prev, [index]: !prev[index]}))
}
}Pending Steps
Use the pending input to mark steps as pending. This is typically used to indicate that a step has been visited but not completed.
<div q-stepper-root [count]="items.length" [pending]="pending()">
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
<div class="flex items-center gap-4">
<span>{{ item.title }} content</span>
</div>
</div>
}
<div q-stepper-completed-content>All steps completed.</div>
<ng-container *stepperContext="let api">
<div class="mt-6 flex justify-between">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
>
Back
</button>
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
variant="outline"
(click)="pendingStep.set(api.step + 1)"
>
Next
</button>
</div>
</ng-container>
</div>
Sizes
The stepper comes in two sizes: sm and lg. The default size is lg.
import {Component} from "@angular/core"
import {StepperModule} from "@qualcomm-ui/angular/stepper"
@Component({
imports: [StepperModule],
selector: "stepper-sizes-demo",
template: `
<div class="flex w-full flex-col gap-16">
@for (size of sizes; track size) {
<div q-stepper-root [count]="items.length" [size]="size">
<div q-stepper-list>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
@for (item of items; track item.value; let i = $index) {
<div q-stepper-content [index]="i">
{{ item.content }}
</div>
}
</div>
}
</div>
`,
})
export class StepperSizesDemo {
readonly items = [
{content: "Contact details", title: "Step 1", value: "step-1"},
{content: "Payment info", title: "Step 2", value: "step-2"},
{content: "Confirmation", title: "Step 3", value: "step-3"},
]
readonly sizes = ["sm", "lg"] as const
}Validation Examples
Non-linear Form
Combine linear={false} with per-step validation to let users complete steps in any order. Use the invalid and completed props to track each step's status, and stepperContext to conditionally render a submit action once all steps pass.
import {Component, inject, signal} from "@angular/core"
import {FormBuilder, ReactiveFormsModule, Validators} from "@angular/forms"
import {ChevronLeft, ChevronRight} from "lucide-angular"
import {provideIcons} from "@qualcomm-ui/angular-core/lucide"
import {ButtonModule} from "@qualcomm-ui/angular/button"
import {StepperModule} from "@qualcomm-ui/angular/stepper"
import {TextInputModule} from "@qualcomm-ui/angular/text-input"
@Component({
imports: [StepperModule, ButtonModule, TextInputModule, ReactiveFormsModule],
providers: [provideIcons({ChevronLeft, ChevronRight})],
selector: "stepper-nonlinear-form-demo",
template: `
<div
q-stepper-root
[completed]="completed()"
[count]="items.length"
[invalid]="invalid()"
[linear]="false"
>
<div q-stepper-list>
@for (item of items; track item.name; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>{{ item.title }}</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
@for (item of items; track item.name; let i = $index) {
<div q-stepper-content [index]="i">
<q-text-input
class="w-72"
[errorText]="getErrorText(item.name)"
[formControl]="form.controls[item.name]"
[invalid]="isFieldInvalid(item.name)"
[label]="item.label"
[placeholder]="item.placeholder"
/>
</div>
}
<div q-stepper-completed-content>
Survey submitted. Thank you for your feedback!
</div>
<div class="mt-6 flex justify-between">
<ng-container *stepperContext="let api">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
(click)="saveStep(api.step)"
>
Back
</button>
@if (allCompleted()) {
<button
endIcon="ChevronRight"
q-button
size="sm"
(click)="saveStep(api.step); api.goToNextStep()"
>
Submit
</button>
} @else {
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
[disabled]="
!api.hasNextStep ||
(api.step === items.length - 1 && !allCompleted())
"
(click)="saveStep(api.step)"
>
Next
</button>
}
</ng-container>
</div>
</div>
`,
})
export class StepperNonlinearFormDemo {
readonly items = [
{
label: "Age range",
name: "age" as const,
placeholder: "25-34",
title: "Demographics",
},
{
label: "Preferred contact method",
name: "contact" as const,
placeholder: "Email",
title: "Preferences",
},
{
label: "Comments",
name: "comments" as const,
placeholder: "Tell us what you think",
title: "Feedback",
},
]
readonly completed = signal<Record<number, boolean>>({})
readonly invalid = signal<Record<number, boolean>>({})
private fb = inject(FormBuilder)
readonly form = this.fb.group({
age: ["", Validators.required],
comments: ["", Validators.required],
contact: ["", Validators.required],
})
allCompleted(): boolean {
return this.items.every((_, i) => this.completed()[i])
}
isFieldInvalid(name: "age" | "comments" | "contact"): boolean {
const control = this.form.controls[name]
return control.invalid && (control.dirty || control.touched)
}
getErrorText(name: "age" | "comments" | "contact"): string {
const control = this.form.controls[name]
if (control.hasError("required")) {
const item = this.items.find((i) => i.name === name)
return `${item?.label} is required`
}
return ""
}
saveStep(index: number) {
const field = this.items[index]?.name
if (!field) {
return
}
const control = this.form.controls[field]
control.markAsTouched()
if (control.valid) {
this.invalid.update((prev) => ({...prev, [index]: false}))
this.completed.update((prev) => ({...prev, [index]: true}))
} else {
this.invalid.update((prev) => ({...prev, [index]: true}))
this.completed.update((prev) => ({...prev, [index]: false}))
}
}
}Skippable Steps
Use isStepSkippable to mark optional steps that can be bypassed during navigation.
import {Component, inject, signal} from "@angular/core"
import {FormBuilder, ReactiveFormsModule, Validators} from "@angular/forms"
import {ChevronLeft, ChevronRight} from "lucide-angular"
import {provideIcons} from "@qualcomm-ui/angular-core/lucide"
import {ButtonModule} from "@qualcomm-ui/angular/button"
import {StepperModule} from "@qualcomm-ui/angular/stepper"
import {TextInputModule} from "@qualcomm-ui/angular/text-input"
import type {
CanGoToStepDetails,
StepInvalidDetails,
} from "@qualcomm-ui/core/stepper"
const PROMO_STEP = 1
const steps = [
{title: "Shipping", value: "shipping"},
{title: "Promo Code", value: "promo"},
{title: "Payment", value: "payment"},
]
@Component({
imports: [StepperModule, ButtonModule, TextInputModule, ReactiveFormsModule],
providers: [provideIcons({ChevronLeft, ChevronRight})],
selector: "stepper-skippable-steps-demo",
template: `
<div
q-stepper-root
[canGoToStep]="canGoToStep"
[count]="steps.length"
[isStepSkippable]="isStepSkippable"
[step]="step()"
(stepChanged)="step.set($event)"
(stepInvalid)="onStepInvalid($event)"
>
<div q-stepper-list>
@for (s of steps; track s.value; let i = $index) {
<div q-stepper-item [index]="i">
<button q-stepper-trigger>
<div q-stepper-indicator>{{ i + 1 }}</div>
<span q-stepper-label>
{{ s.title }}
@if (i === promoStep) {
<span q-stepper-hint>(optional)</span>
}
</span>
</button>
<div q-stepper-separator></div>
</div>
}
</div>
<div q-stepper-content [index]="0">
<q-text-input
class="max-w-56"
label="Shipping address"
placeholder="123 Main St"
[errorText]="getErrorText('address')"
[formControl]="form.controls.address"
[invalid]="isFieldInvalid('address')"
/>
</div>
<div q-stepper-content [index]="1">
<q-text-input
class="max-w-56"
label="Promo code"
placeholder="SAVE20"
[formControl]="form.controls.promo"
/>
</div>
<div q-stepper-content [index]="2">
<q-text-input
class="max-w-56"
label="Card number"
placeholder="4242 4242 4242 4242"
[errorText]="getErrorText('card')"
[formControl]="form.controls.card"
[invalid]="isFieldInvalid('card')"
/>
</div>
<div q-stepper-completed-content>
Order confirmed. Thank you for your purchase!
</div>
<div class="mt-6 flex justify-between">
<button
q-button
q-stepper-prev-trigger
size="sm"
startIcon="ChevronLeft"
variant="outline"
>
Back
</button>
<button
endIcon="ChevronRight"
q-button
q-stepper-next-trigger
size="sm"
variant="outline"
>
Next
</button>
</div>
</div>
`,
})
export class StepperSkippableStepsDemo {
readonly steps = steps
readonly promoStep = PROMO_STEP
readonly step = signal(0)
private fb = inject(FormBuilder)
readonly form = this.fb.group({
address: ["", Validators.required],
card: ["", Validators.required],
promo: [""],
})
readonly isStepSkippable = (index: number): boolean => index === PROMO_STEP
readonly canGoToStep = ({
current,
target,
}: CanGoToStepDetails): boolean | undefined => {
if (target <= current) {
return undefined
}
return this.validateStep(current)
}
isFieldInvalid(name: "address" | "card" | "promo"): boolean {
const control = this.form.controls[name]
return control.invalid && (control.dirty || control.touched)
}
getErrorText(name: "address" | "card"): string {
const control = this.form.controls[name]
if (control.hasError("required")) {
return `${name === "address" ? "Shipping address" : "Card number"} is required`
}
return ""
}
onStepInvalid({step}: StepInvalidDetails) {
if (step === 0) {
this.form.controls.address.markAsTouched()
this.form.controls.address.markAsDirty()
}
if (step === 2) {
this.form.controls.card.markAsTouched()
this.form.controls.card.markAsDirty()
}
}
private validateStep(index: number): boolean {
if (index === 0) {
return this.form.controls.address.valid
}
if (index === 2) {
return this.form.controls.card.valid
}
return true
}
}Explorer
Component Anatomy
Hover to highlight, click to view API
API
q-stepper-root
number(details: {
current: number
target: number
visited: boolean
}) => boolean
Return
false to block navigation, true to allow it, or undefined
to defer to the built-in navigation rules.Record<
number,
boolean
>
number'ltr' | 'rtl'
() =>
| Node
| ShadowRoot
| Document
stringRecord<
number,
boolean
>
(
index: number,
) => boolean
booleantrue, the stepper requires the user to complete the steps in order.| 'horizontal'
| 'horizontal-inline'
| 'horizontal-bottom-start'
| 'vertical'
| 'vertical-inline'
Record<
number,
boolean
>
'sm' | 'lg'
numbernumber{
action: 'set' | 'next'
step: number
targetStep?: number
}
class'qui-stepper__root'data-orientation| 'horizontal'
| 'horizontal-inline'
| 'horizontal-bottom-start'
| 'vertical'
| 'vertical-inline'
data-size'sm' | 'lg'
data-stepper-part'root'q-stepper-list
stringclass'qui-stepper__list'data-orientation| 'horizontal'
| 'horizontal-inline'
| 'horizontal-bottom-start'
| 'vertical'
| 'vertical-inline'
data-size'sm' | 'lg'
data-stepper-part'list'q-stepper-item
numberclass'qui-stepper__item'data-currentdata-firstdata-lastdata-orientation| 'horizontal'
| 'horizontal-inline'
| 'horizontal-bottom-start'
| 'vertical'
| 'vertical-inline'
data-previousdata-size'sm' | 'lg'
data-skippabledata-stepper-part'item'q-stepper-trigger
stringclass'qui-stepper__trigger'data-completedata-currentdata-incompletedata-invaliddata-lastdata-orientation| 'horizontal'
| 'horizontal-inline'
| 'horizontal-bottom-start'
| 'vertical'
| 'vertical-inline'
data-pendingdata-size'sm' | 'lg'
data-state| 'open'
| 'closed'
data-stepper-part'trigger'tabIndex-1 | 0
q-stepper-indicator
| LucideIconData
| string
| LucideIconData
| string
class'qui-stepper__indicator'data-completedata-currentdata-incompletedata-orientation| 'horizontal'
| 'horizontal-inline'
| 'horizontal-bottom-start'
| 'vertical'
| 'vertical-inline'
data-size'sm' | 'lg'
data-stepper-part'indicator'styleq-stepper-separator
class'qui-stepper__separator'data-completedata-incompletedata-orientation| 'horizontal'
| 'horizontal-inline'
| 'horizontal-bottom-start'
| 'vertical'
| 'vertical-inline'
data-size'sm' | 'lg'
data-stepper-part'separator'styleq-stepper-completed-content
Content displayed when all steps are completed.
class'qui-stepper__completed-content'data-size'sm' | 'lg'
data-stepper-part'completed-content'hiddenbooleanq-stepper-content
numberstringclass'qui-stepper__content'data-orientation| 'horizontal'
| 'horizontal-inline'
| 'horizontal-bottom-start'
| 'vertical'
| 'vertical-inline'
data-size'sm' | 'lg'
data-state| 'open'
| 'closed'
data-stepper-part'content'hiddenbooleantabIndex0q-stepper-label
class'qui-stepper__label'data-completedata-currentdata-incompletedata-orientation| 'horizontal'
| 'horizontal-inline'
| 'horizontal-bottom-start'
| 'vertical'
| 'vertical-inline'
data-size'sm' | 'lg'
data-stepper-part'label'q-stepper-hint
class'qui-stepper__hint'data-completedata-currentdata-incompletedata-orientation| 'horizontal'
| 'horizontal-inline'
| 'horizontal-bottom-start'
| 'vertical'
| 'vertical-inline'
data-size'sm' | 'lg'
data-stepper-part'hint'q-stepper-next-trigger
class'qui-stepper__next-trigger'data-disableddata-stepper-part'next-trigger'q-stepper-prev-trigger
class'qui-stepper__prev-trigger'data-disableddata-stepper-part'prev-trigger'stepperContext
StepperApi
number(props: {
index: number
}) => {
completed: boolean
contentId: string
current: boolean
first: boolean
incomplete: boolean
index: number
invalid: boolean
last: boolean
pending?: boolean
previous?: boolean
skippable: boolean
triggerId: string
visited: boolean
}
() => void
() => void
booleanboolean(
index: number,
) => boolean
() => void
(
step: number,
) => void
number