<script setup>
import { Steps } from "@dicehub/kappa/components/steps";
const steps = ["Configuration", "Review", "Run"];
</script>
<template>
<Steps.Root :count="steps.length" :default-step="0">
<Steps.Progress />
<Steps.List aria-label="Case setup progress">
<Steps.Item v-for="(label, index) in steps" :key="label" :index="index">
<Steps.Trigger>
<Steps.Indicator v-slot="{ completed }">
{{ completed ? "✓" : index + 1 }}
</Steps.Indicator>
<span>{{ label }}</span>
</Steps.Trigger>
<Steps.Separator v-if="index < steps.length - 1" />
</Steps.Item>
</Steps.List>
<Steps.Content v-for="(label, index) in steps" :key="label" :index="index">
{{ label }} settings.
</Steps.Content>
<Steps.CompletedContent aria-label="Case setup complete">
Ready to run.
</Steps.CompletedContent>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.Context v-slot="{ isCompleted, resetStep }">
<button v-if="isCompleted" type="button" @click="resetStep">Reset</button>
<Steps.NextTrigger v-else>Next</Steps.NextTrigger>
</Steps.Context>
</Steps.Root>
</template>Installation
Barrel
import { Steps } from "@dicehub/kappa";Granular
import { Steps } from "@dicehub/kappa/components/steps";Usage
Give each item and content panel the same zero-based index. Set count to the number of items. The completion state starts when step equals count.
<script setup>
import { Steps } from "@dicehub/kappa/components/steps";
const steps = ["Configuration", "Review", "Run"];
</script>
<template>
<Steps.Root :count="steps.length" :default-step="0">
<Steps.Progress />
<Steps.List aria-label="Case setup progress">
<Steps.Item v-for="(label, index) in steps" :key="label" :index="index">
<Steps.Trigger>
<Steps.Indicator v-slot="{ completed }">
{{ completed ? "✓" : index + 1 }}
</Steps.Indicator>
<span>{{ label }}</span>
</Steps.Trigger>
<Steps.Separator v-if="index < steps.length - 1" />
</Steps.Item>
</Steps.List>
<Steps.Content v-for="(label, index) in steps" :key="label" :index="index">
{{ label }} settings.
</Steps.Content>
<Steps.CompletedContent aria-label="Case setup complete">
Ready to run.
</Steps.CompletedContent>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.Context v-slot="{ isCompleted, resetStep }">
<button v-if="isCompleted" type="button" @click="resetStep">Reset</button>
<Steps.NextTrigger v-else>Next</Steps.NextTrigger>
</Steps.Context>
</Steps.Root>
</template>Composition
Steps.Root
├── Steps.Progress (optional)
├── Steps.List
│ └── Steps.Item (one per index)
│ ├── Steps.Trigger
│ │ └── Steps.Indicator
│ └── Steps.Separator (except last item)
├── Steps.Content (one per index)
├── Steps.CompletedContent
├── Steps.PrevTrigger
└── Steps.NextTriggerPlace each trigger, indicator, and separator inside its item. Every enabled item needs a matching content panel. The indicator slot exposes its item state for custom complete and current markers.
<script setup>
import {
StepsCompletedContent,
StepsContent,
StepsIndicator,
StepsItem,
StepsList,
StepsRoot,
StepsSeparator,
StepsTrigger,
} from "@dicehub/kappa/components/steps";
</script>
<template>
<StepsRoot :count="2" :default-step="0">
<StepsList aria-label="Import progress">
<StepsItem :index="0">
<StepsTrigger>
<StepsIndicator as-child><span>1</span></StepsIndicator>
Upload
</StepsTrigger>
<StepsSeparator />
</StepsItem>
<StepsItem :index="1">
<StepsTrigger><StepsIndicator>2</StepsIndicator>Map fields</StepsTrigger>
</StepsItem>
</StepsList>
<StepsContent :index="0">Choose a case archive.</StepsContent>
<StepsContent :index="1">Map the imported fields.</StepsContent>
<StepsCompletedContent aria-label="Import complete">Import complete.</StepsCompletedContent>
</StepsRoot>
</template>Examples
Basic
Connect indexed items and panels, then use the built-in navigation triggers.
<script setup>
import { Steps } from "@dicehub/kappa/components/steps";
const steps = ["Configuration", "Review", "Run"];
</script>
<template>
<Steps.Root :count="steps.length" :default-step="0">
<Steps.Progress />
<Steps.List aria-label="Case setup progress">
<Steps.Item v-for="(label, index) in steps" :key="label" :index="index">
<Steps.Trigger>
<Steps.Indicator v-slot="{ completed }">
{{ completed ? "✓" : index + 1 }}
</Steps.Indicator>
<span>{{ label }}</span>
</Steps.Trigger>
<Steps.Separator v-if="index < steps.length - 1" />
</Steps.Item>
</Steps.List>
<Steps.Content v-for="(label, index) in steps" :key="label" :index="index">
{{ label }} settings.
</Steps.Content>
<Steps.CompletedContent aria-label="Case setup complete">
Ready to run.
</Steps.CompletedContent>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.Context v-slot="{ isCompleted, resetStep }">
<button v-if="isCompleted" type="button" @click="resetStep">Reset</button>
<Steps.NextTrigger v-else>Next</Steps.NextTrigger>
</Steps.Context>
</Steps.Root>
</template>Vertical
Use a vertical list when labels need more room or the flow sits beside its panel.
<Steps.Root :count="3" :default-step="1" orientation="vertical">
<Steps.List aria-label="Deployment progress">
<Steps.Item v-for="(label, index) in steps" :key="label" :index="index">
<Steps.Trigger>
<Steps.Indicator>{{ index + 1 }}</Steps.Indicator>
<span>{{ label }}</span>
</Steps.Trigger>
<Steps.Separator v-if="index < steps.length - 1" />
</Steps.Item>
</Steps.List>
<Steps.Content v-for="(label, index) in steps" :key="label" :index="index">
{{ label }} settings.
</Steps.Content>
<Steps.CompletedContent aria-label="Deployment complete">Complete.</Steps.CompletedContent>
</Steps.Root>Sizes
Use the small size for dense workflows and the base size for standard forms.
<Steps.Root :count="3" :default-step="1">
<Steps.List size="base" aria-label="Base steps">...</Steps.List>
</Steps.Root>
<Steps.Root :count="3" :default-step="1">
<Steps.List size="sm" aria-label="Small steps">...</Steps.List>
</Steps.Root>Controlled
Bind step when the application must reset or coordinate the active stage.
<script setup>
import { ref } from "vue";
import { Steps } from "@dicehub/kappa/components/steps";
const step = ref(1);
</script>
<template>
<Steps.Root v-model:step="step" :count="3">
<Steps.List aria-label="Controlled progress">...</Steps.List>
<Steps.Content :index="0">Configuration</Steps.Content>
<Steps.Content :index="1">Review</Steps.Content>
<Steps.Content :index="2">Run</Steps.Content>
<Steps.CompletedContent aria-label="Process complete">Complete.</Steps.CompletedContent>
<button type="button" @click="step = 0">Reset</button>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</Steps.Root>
<output aria-live="polite">Step: {{ step }}</output>
</template>Correspondence
| Reference | Corresponding component | Role in Kappa |
|---|---|---|
| Kumo | None | No corresponding component. |
| Ark UI | Steps | Behavior, state, and accessibility base. |
| shadcn | None | No official Steps component. |
Accessibility
- The list uses
tablist, each trigger usestab, and each panel usestabpanel. - In non-linear mode, users can select an item trigger directly. In linear mode, movement uses the previous and next triggers.
- Validation can block forward movement. A skippable step can bypass its validation check.
- Give
CompletedContentan accessible label because it has no matching item trigger. - Use at least one step. Keep controlled values in the range from zero through
count. - Focus remains visible. State transitions stop when reduced motion is requested.
Kappa preserves Ark UI behavior.
API Reference
Steps.Root
| Prop | Type | Default | Description |
|---|---|---|---|
count | number | — | Total number of indexed steps. |
step | number | — | Controlled zero-based step. Completion is count. |
defaultStep | number | 0 | Initial uncontrolled step. |
orientation | "horizontal" | "vertical" | "horizontal" | List direction. |
linear | boolean | false | Requires progress through the navigation API in order. |
isStepValid | (index: number) => boolean | — | Checks a step before forward movement. |
isStepSkippable | (index: number) => boolean | — | Allows forward movement without validation for a step. |
dir | "ltr" | "rtl" | inherited | Overrides the inherited direction. |
Steps.List
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "base" | "base" | Marker and label density. |
asChild | boolean | false | Merges list behavior into its child. |
Steps.Item and Steps.Content
| Prop | Type | Default | Description |
|---|---|---|---|
index | number | — | Required zero-based step index. |
asChild | boolean | false | Merges part behavior into its child. |
Parts
| Part | Element | Description |
|---|---|---|
Steps.Root | div | Owns the step state. |
Steps.List | div | Contains the indexed items. |
Steps.Item | div | Provides one index to its parts. |
Steps.Trigger | button | Selects an item in non-linear mode. |
Steps.Indicator | div | Shows current, complete, or incomplete state. |
Steps.Separator | div | Connects adjacent items. |
Steps.Content | div | Panel for one indexed item. |
Steps.CompletedContent | div | Panel shown when step equals count. |
Steps.PrevTrigger | button | Moves to the previous step. |
Steps.NextTrigger | button | Moves to the next step. |
Steps.Progress | div | Reports and shows completion percentage. |
Events
| Event | Payload | Description |
|---|---|---|
stepChange | StepChangeDetails | Runs when the active step changes. |
stepComplete | void | Runs when the value reaches count. |
stepInvalid | StepsInvalidDetails | Runs when validation blocks forward movement. |
update:step | number | Supports v-model:step. |
Exports
| Export | Description |
|---|---|
Steps | Root component with all compound parts. |
StepsRoot … StepsItemContext | Named component exports. |
useSteps | Creates a controlled Ark UI steps machine. |
useStepsContext | Reads the root machine. |
useStepsItemContext | Reads state for the current item. |
STEPS_SIZES | Supported list sizes. |