# Stepper

## Usage

`Stepper` lists the steps of a flow. `activeStep` is the zero-based current step. A step before it is completed, the current step is in progress, and the rest have not started.

Pass `@step-click` when a step can be chosen out of order. The handler receives the zero-based index, and only then do the steps render as buttons. The default `separated` layout draws a track above each horizontal step, with the indicator and the label on one row. `indicatorPosition="on-track"` sets the indicator into the connector and centers the label under it. `indicator` defaults to `auto`: a check when the step is done, a ring on the current step, and a number after that.

`horizontalOptions.minimumStepWidth` defaults to `112`. A horizontal stepper collapses once it is narrower than that width times the step count, and shows the current step with Previous and Next. The track stays. `collapsedVariant` defaults to `withLabelAndControls` and skips disabled steps. `withLabel` drops the controls, and `hiddenLabel` leaves only the track.

```vue
<script setup lang="ts">
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { ref } from 'vue'

const active = ref(2)
</script>

<template>
  <Stepper
    :active-step="active"
    :horizontal-options="{ minimumStepWidth: 112 }"
    @step-click="active = $event"
  >
    <Step label="Cart" :step="0" />
    <Step label="Shipping" :step="1" />
    <Step label="Payment" :step="2" />
    <Step label="Review" :step="3" />
    <Step label="Confirm" :step="4" />
  </Stepper>
</template>
```

### Anatomy

- **Stepper**: The ordered list holding the steps. Owns the orientation and the indicator placement the whole flow is laid out on.
- **Frame**: The layout frame that groups the ordered steps with the optional compact summary shown at narrow widths.
- **Compact summary**: The optional row a horizontal Stepper adds directly beneath the track once it is too narrow to label every step. horizontalOptions.collapsedVariant chooses a label with Previous/Next controls, the label alone, or no row for a bare progress track. The on-track layout keeps its indicators on the rail instead of repeating the active indicator beside the label. Every step keeps its name in the accessible sequence at any width.
- **Step**: One step in the flow, and the element carrying its status. Wraps the indicator, label, description, and the track segments belonging to it.
- **Progress bar**: A 4px segmented bar per step. Filled for completed and active steps. Advancing one step grows the fill along the track it just covered, so the movement reads as progress rather than a bar changing color. Every other change applies at once: going back, jumping forward by more than one step, mounting mid-flow, and any change at all under prefers-reduced-motion. Where a span is drawn by more than one segment (the on-track layouts split it between two steps, three when a content slot sits between them), the segments run in track order at one constant speed, so the fill reads as a single line growing rather than pieces lighting in turn.
- **Connector**: The track drawn between indicators in the on-track layouts. Each connector paints an unfilled line and, over it, the accent fill covering the progress made. How many pieces a connector is drawn from is an implementation detail of the layout, not a themeable part; use --step-connector-gap to hold the track off the indicator.
- **Indicator**: A numbered badge, a check, or any custom icon. Controlled via the indicator prop.
- **Label**: Text identifying the step.
- **Description**: Supporting text below the label with additional context.

## Examples

### Custom content

Each vertical step owns a slice of the page. The content slot can be fields, a summary, or a banner, and only the active step renders it.

```vue
<script setup lang="ts">
import { Badge } from '@astryx-vue/core/Badge'
import { Banner } from '@astryx-vue/core/Banner'
import { Button } from '@astryx-vue/core/Button'
import { Card } from '@astryx-vue/core/Card'
import { HStack, VStack } from '@astryx-vue/core/Stack'
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { Text } from '@astryx-vue/core/Text'
import { TextInput } from '@astryx-vue/core/TextInput'
import { ref } from 'vue'

const active = ref(0)
const name = ref('')
const repository = ref('')
</script>

<template>
  <div style="width: 680px; max-width: 100%">
    <Stepper :active-step="active" orientation="vertical" @step-click="active = $event">
      <Step
        description="Name it and point us at the source"
        indicator="number"
        label="Project details"
        :step="0"
      >
        <VStack v-if="active === 0" :gap="3">
          <HStack :gap="3">
            <TextInput v-model="name" label="Project name" placeholder="My awesome project" width="100%" />
            <TextInput v-model="repository" label="Repository URL" placeholder="https://github.com/..." width="100%" />
          </HStack>
          <div>
            <Button label="Continue" variant="primary" @click="active = 1" />
          </div>
        </VStack>
      </Step>
      <Step
        description="What we detected from your default branch"
        indicator="number"
        label="Review the build"
        :step="1"
      >
        <VStack v-if="active === 1" :gap="3">
          <Card :padding="3" variant="muted">
            <VStack :gap="2">
              <HStack align="center" :gap="2">
                <Text type="label">
                  Next.js 15
                </Text>
                <Badge variant="success">
                  Detected
                </Badge>
              </HStack>
              <Text type="supporting">
                Build command <code>next build</code> · Output <code>.next</code> · Node 20
              </Text>
            </VStack>
          </Card>
          <HStack :gap="2">
            <Button label="Back" variant="secondary" @click="active = 0" />
            <Button label="Looks right" variant="primary" @click="active = 2" />
          </HStack>
        </VStack>
      </Step>
      <Step description="Ships to production" indicator="number" label="Deploy" :step="2">
        <VStack v-if="active === 2" :gap="3">
          <Banner
            description="Later deploys reuse the build cache and finish in under a minute."
            status="info"
            title="First deploy takes a few minutes"
          />
          <HStack :gap="2">
            <Button label="Back" variant="secondary" @click="active = 1" />
            <Button label="Deploy now" variant="primary" @click="active = 3" />
          </HStack>
        </VStack>
      </Step>
      <Step indicator="number" label="Live" :step="3" />
    </Stepper>
  </div>
</template>
```

### Indicator modes

None leaves progress to the bar and the label. Auto uses a check, a ring, or a number. Number always shows the step. A custom icon can replace the glyph.

```vue
<script setup lang="ts">
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { Text } from '@astryx-vue/core/Text'
import { ref } from 'vue'

const active = ref(2)
</script>

<template>
  <div class="stepper-modes">
    <div>
      <Text type="label">
        None
      </Text>
      <Stepper :active-step="active" orientation="vertical" @step-click="active = $event">
        <Step indicator="none" label="Account" :step="0" />
        <Step indicator="none" label="Profile" :step="1" />
        <Step indicator="none" label="Settings" :step="2" />
        <Step indicator="none" label="Review" :step="3" />
      </Stepper>
    </div>
    <div>
      <Text type="label">
        Auto
      </Text>
      <Stepper :active-step="active" orientation="vertical" @step-click="active = $event">
        <Step label="Account" :step="0" />
        <Step label="Profile" :step="1" />
        <Step label="Settings" :step="2" />
        <Step label="Review" :step="3" />
      </Stepper>
    </div>
    <div>
      <Text type="label">
        Number
      </Text>
      <Stepper :active-step="active" orientation="vertical" @step-click="active = $event">
        <Step indicator="number" label="Account" :step="0" />
        <Step indicator="number" label="Profile" :step="1" />
        <Step indicator="number" label="Settings" :step="2" />
        <Step indicator="number" label="Review" :step="3" />
      </Stepper>
    </div>
    <div>
      <Text type="label">
        Custom icon
      </Text>
      <Stepper :active-step="active" orientation="vertical" @step-click="active = $event">
        <Step label="Account" :step="0">
          <template #icon>
            <span class="css-icon css-icon--sm i-carbon-information" aria-hidden="true" />
          </template>
        </Step>
        <Step label="Profile" :step="1">
          <template #icon>
            <span class="css-icon css-icon--sm i-carbon-search" aria-hidden="true" />
          </template>
        </Step>
        <Step label="Settings" :step="2">
          <template #icon>
            <span class="css-icon css-icon--sm i-carbon-tools" aria-hidden="true" />
          </template>
        </Step>
        <Step label="Review" :step="3">
          <template #icon>
            <span class="css-icon css-icon--sm i-carbon-checkmark" aria-hidden="true" />
          </template>
        </Step>
      </Stepper>
    </div>
  </div>
</template>

<style scoped>
.stepper-modes {
  display: flex;
  flex-wrap: wrap;
  gap: 48px;
}

.stepper-modes > div {
  max-width: 220px;
}
</style>
```

### On-track horizontal

On a horizontal track, each indicator sits in the connector so the numbers read as beads on one line. Labels center under their node.

```vue
<script setup lang="ts">
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { ref } from 'vue'

const active = ref(2)
</script>

<template>
  <div style="width: 100%; max-width: 640px">
    <Stepper
      :active-step="active"
      indicator-position="on-track"
      @step-click="active = $event"
    >
      <Step indicator="number" label="Workspace" :step="0" />
      <Step indicator="number" label="Team" :step="1" />
      <Step indicator="number" label="Integrations" :step="2" />
      <Step indicator="number" label="Import" :step="3" />
      <Step indicator="number" label="Launch" :step="4" />
    </Stepper>
  </div>
</template>
```

### On-track vertical

On a vertical rail, the indicator sits on the connector, with the label and description beside the node.

```vue
<script setup lang="ts">
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { ref } from 'vue'

const active = ref(2)
</script>

<template>
  <div style="width: 100%; max-width: 400px">
    <Stepper
      :active-step="active"
      indicator-position="on-track"
      orientation="vertical"
      @step-click="active = $event"
    >
      <Step description="Name and configure your workspace" label="Create workspace" :step="0" />
      <Step description="Add collaborators by email" label="Invite team members" :step="1" />
      <Step description="Connect Slack, GitHub, Jira" label="Set up integrations" :step="2" />
      <Step description="Bring in existing projects" label="Import data" :step="3" />
      <Step description="Go live with your team" label="Launch" :step="4" />
    </Stepper>
  </div>
</template>
```

### Validation status

Success, error, and accent recolor the indicator in a verification flow. Status never recolors the connector.

```vue
<script setup lang="ts">
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { ref } from 'vue'

const active = ref(3)
</script>

<template>
  <div style="width: 100%; max-width: 420px">
    <Stepper :active-step="active" orientation="vertical" @step-click="active = $event">
      <Step description="you@example.com" label="Email verified" :step="0" status="success" />
      <Step description="+1 (555) 012-3456" label="Phone verified" :step="1" status="success" />
      <Step description="Passport upload failed" label="Identity document" :step="2" status="error" />
      <Step description="Pending review" label="Address verification" :step="3" status="accent" />
      <Step description="Skipped" is-optional label="Background check" :step="4" />
      <Step label="Account activated" :step="5" />
    </Stepper>
  </div>
</template>
```

### Horizontal narrow collapsed

At 320px, four steps fall below the 112px minimum width and collapse to the current step, with Previous and Next.

```vue
<script setup lang="ts">
import { Step, Stepper } from '@astryx-vue/core/Stepper'
import { ref } from 'vue'

const active = ref(1)
</script>

<template>
  <div style="width: 320px; max-width: 100%">
    <Stepper
      :active-step="active"
      :horizontal-options="{ minimumStepWidth: 112 }"
      @step-click="active = $event"
    >
      <Step label="Cart" :step="0" />
      <Step description="Where it goes" label="Shipping" :step="1" />
      <Step label="Delivery" :step="2" />
      <Step label="Payment" :step="3" />
    </Stepper>
  </div>
</template>
```

## Stepper props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `activeStep` | `number` | - | Zero-based index of the current step. |
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout direction. |
| `onStepClick` | `(index: number) => void` | - | When set, steps are buttons. `@step-click` fills this prop. |
| `label` | `string` | Progress | Accessible name of the list. |
| `density` | `'compact' \| 'balanced' \| 'spacious'` | `'balanced'` | Space around each step. |
| `indicatorPosition` | `'separated' \| 'on-track'` | `'separated'` | Whether the marker sits on the connector or beside a separate bar. |
| `hasCollapsibleLabels` | `boolean` | `false` | In a horizontal stepper narrower than 480px, hide every label except the current one. |
| `horizontalOptions` | `{ minimumStepWidth?: number, collapsedVariant?: 'withLabelAndControls' \| 'withLabel' \| 'hiddenLabel' }` | `{ minimumStepWidth: 112, collapsedVariant: 'withLabelAndControls' }` | When the horizontal stepper is narrower than `minimumStepWidth` times the step count, it keeps the track and shows the current step. `withLabelAndControls` adds Previous and Next when `onStepClick` is set. `withLabel` shows the label only. `hiddenLabel` leaves the track. |

## Step props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `step` | `number` | - | Zero-based index of this step. |
| `label` | `string` | - | Step name. |
| `description` | `string` | - | Supporting line under the label. |
| `status` | `'accent' \| 'success' \| 'warning' \| 'error'` | - | Recolors the indicator only. The connector stays on the progress color. |
| `isDisabled` | `boolean` | `false` | The step cannot be activated. |
| `isOptional` | `boolean` | `false` | Appends an Optional marker. |
| `indicator` | `'auto' \| 'number' \| 'none'` | `'auto'` | `auto` shows a check, a ring, or a number. `number` always shows the step number. |
| `density` | `'compact' \| 'balanced' \| 'spacious'` | The stepper's density | Space for this step. |

## Step slots

| Name | Description |
| --- | --- |
| `default` | Content under the label. In a vertical separated step it sits outside the button. |
| `icon` | Replaces the built-in indicator artwork. |
| `end` | Trailing content on the label row. |
