

<Preview name="BasicStepperExample" />

## Overview [#overview]

The `Stepper` component guides users through sequential steps with separate
selection and status states. A step can be inactive, active, or disabled while
also using the incomplete, completed, or loading status. Previous and current
steps are progress-colored separately from the status icon. It supports
horizontal and vertical layouts, controlled and uncontrolled values, animated
step content, and optional URL state.

Stepper indicators are surface-aware. Unreached indicators land two levels above
the parent surface with the matching contextual border. Reached indicators use
the brand fill, while status controls whether the marker shows a number, check
icon, or spinner. Hovering a step increases the indicator and the wrapper around
the title/description without requiring local color overrides.

## Usage [#usage]

```tsx
import {
  Stepper,
  StepperDescription,
  StepperHeading,
  StepperIndicator,
  StepperItem,
  StepperSeparator,
  StepperTitle,
  StepperTrigger,
} from "@tilt-legal/cubitt-components/stepper";
```

```tsx
<Stepper defaultValue={1}>
  <StepperItem step={1}>
    <StepperTrigger>
      <StepperIndicator />
      <StepperHeading>
        <StepperTitle>Account</StepperTitle>
        <StepperDescription>Create your account</StepperDescription>
      </StepperHeading>
    </StepperTrigger>
    <StepperSeparator />
  </StepperItem>
  <StepperItem step={2}>
    <StepperTrigger>
      <StepperIndicator />
      <StepperHeading>
        <StepperTitle>Profile</StepperTitle>
        <StepperDescription>Add your profile details</StepperDescription>
      </StepperHeading>
    </StepperTrigger>
  </StepperItem>
</Stepper>
```

## URL State [#url-state]

Provide `paramName` to sync the active step with the current URL.

```tsx
<Stepper defaultValue={1} paramName="step">
  <StepperItem step={1}>
    <StepperTrigger>
      <StepperIndicator />
      <StepperHeading>
        <StepperTitle>Account</StepperTitle>
      </StepperHeading>
    </StepperTrigger>
  </StepperItem>
  <StepperItem step={2}>
    <StepperTrigger>
      <StepperIndicator />
      <StepperHeading>
        <StepperTitle>Profile</StepperTitle>
      </StepperHeading>
    </StepperTrigger>
  </StepperItem>
  <StepperItem step={3}>
    <StepperTrigger>
      <StepperIndicator />
      <StepperHeading>
        <StepperTitle>Complete</StepperTitle>
      </StepperHeading>
    </StepperTrigger>
  </StepperItem>
</Stepper>
```

## Examples [#examples]

### States [#states]

Toggle one centered step through selection state and status.

<Tabs className="bg-transparent border-none rounded-none" items="['Preview', 'Code']">
  <Tab value="Preview" className="p-0">
    <Preview name="StepperStatesExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { useState } from "react";
    import {
      Stepper,
      StepperDescription,
      StepperHeading,
      StepperIndicator,
      StepperItem,
      StepperTitle,
      StepperTrigger,
    } from "@tilt-legal/cubitt-components/stepper";
    import {
      ToggleGroup,
      ToggleGroupItem,
    } from "@tilt-legal/cubitt-components/toggle-group";

    type SelectionState = "inactive" | "active" | "disabled";
    type Status = "incomplete" | "completed" | "loading";

    const selectionStates = ["inactive", "active", "disabled"] as const;
    const statuses = ["incomplete", "completed", "loading"] as const;

    const [selectionState, setSelectionState] = useState<SelectionState>("active");
    const [status, setStatus] = useState<Status>("incomplete");
    const activeStep = selectionState === "active" ? 1 : 0;

    <>
      <Stepper
        className="justify-center"
        orientation="horizontal"
        value={activeStep}
      >
        <StepperItem
          className="flex-col!"
          completed={status === "completed"}
          disabled={selectionState === "disabled"}
          loading={status === "loading"}
          step={1}
        >
          <StepperTrigger className="flex-col gap-3 rounded">
            <StepperIndicator />
            <StepperHeading className="space-y-0.5 text-center">
              <StepperTitle>
                {`${selectionState[0]?.toUpperCase()}${selectionState.slice(1)}`}
              </StepperTitle>
              <StepperDescription>
                {`${status[0]?.toUpperCase()}${status.slice(1)} status`}
              </StepperDescription>
            </StepperHeading>
          </StepperTrigger>
        </StepperItem>
      </Stepper>
      <PreviewFooter className="flex flex-wrap items-center justify-center gap-2">
        <ToggleGroup
          aria-label="Stepper selection state"
          groupVariant="segmented"
          multiple={false}
          onValueChange={(value) => {
            if (selectionStates.includes(value as SelectionState)) {
              setSelectionState(value as SelectionState);
            }
          }}
          value={selectionState}
        >
          <ToggleGroupItem value="inactive">Inactive</ToggleGroupItem>
          <ToggleGroupItem value="active">Active</ToggleGroupItem>
          <ToggleGroupItem value="disabled">Disabled</ToggleGroupItem>
        </ToggleGroup>
        <ToggleGroup
          aria-label="Stepper status"
          groupVariant="segmented"
          multiple={false}
          onValueChange={(value) => {
            if (statuses.includes(value as Status)) {
              setStatus(value as Status);
            }
          }}
          value={status}
        >
          <ToggleGroupItem value="incomplete">Incomplete</ToggleGroupItem>
          <ToggleGroupItem value="completed">Completed</ToggleGroupItem>
          <ToggleGroupItem value="loading">Loading</ToggleGroupItem>
        </ToggleGroup>
      </PreviewFooter>
    </>;
    ```
  </Tab>
</Tabs>

### Basic [#basic]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="BasicStepperExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    <Stepper defaultValue={2}>
      {[1, 2, 3].map((step) => (
        <StepperItem className="relative flex-1 flex-col!" key={step} step={step}>
          <StepperTrigger className="flex-col gap-3 rounded">
            <StepperIndicator />
            <StepperHeading className="space-y-0.5 text-center">
              <StepperTitle>{`Step ${step}`}</StepperTitle>
              <StepperDescription className="max-sm:hidden">
                {`Description for step ${step}`}
              </StepperDescription>
            </StepperHeading>
          </StepperTrigger>
          {step < 3 && (
            <StepperSeparator className="absolute inset-x-0 top-3 left-[calc(50%+0.75rem+0.125rem)] -order-1 m-0 -translate-y-1/2 group-data-[orientation=horizontal]/stepper:w-[calc(100%-1.5rem-0.25rem)] group-data-[orientation=horizontal]/stepper:flex-none" />
          )}
        </StepperItem>
      ))}
    </Stepper>
    ```
  </Tab>
</Tabs>

### Vertical [#vertical]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="VerticalStepperExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    <Stepper defaultValue={2} orientation="vertical">
      {[1, 2, 3].map((step) => (
        <StepperItem className="relative not-last:flex-1 items-start" key={step} step={step}>
          <StepperTrigger className="rounded pb-12 last:pb-0">
            <StepperIndicator />
            <StepperHeading className="space-y-0.5 text-left">
              <StepperTitle>{`Step ${step}`}</StepperTitle>
              <StepperDescription>{`Description for step ${step}`}</StepperDescription>
            </StepperHeading>
          </StepperTrigger>
          {step < 3 && (
            <StepperSeparator className="absolute inset-y-0 top-[calc(1.5rem+0.125rem)] left-3 -order-1 m-0 -translate-x-1/2 group-data-[orientation=vertical]/stepper:h-[calc(100%-1.5rem-0.25rem)]" />
          )}
        </StepperItem>
      ))}
    </Stepper>
    ```
  </Tab>
</Tabs>

### Mixed Elements [#mixed-elements]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview className="px-25" name="MixedElementsStepperExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { Avatar, AvatarFallback } from "@tilt-legal/cubitt-components/avatar";
    import { Shuffle } from "@tilt-legal/cubitt-icons/ui/outline";

    <Stepper defaultValue={2}>
      <StepperItem className="not-last:flex-1" step={1}>
        <StepperTrigger>
          <StepperIndicator>
            <Avatar className="size-6">
              <AvatarFallback className="text-xs">M</AvatarFallback>
            </Avatar>
          </StepperIndicator>
        </StepperTrigger>
        <StepperSeparator />
      </StepperItem>
      <StepperItem className="not-last:flex-1" loading step={2}>
        <StepperTrigger>
          <StepperIndicator />
        </StepperTrigger>
        <StepperSeparator />
      </StepperItem>
      <StepperItem className="not-last:flex-1" step={3}>
        <StepperTrigger>
          <StepperIndicator>
            <Shuffle aria-hidden="true" className="size-3.5" />
            <span className="sr-only">Shuffle</span>
          </StepperIndicator>
        </StepperTrigger>
      </StepperItem>
    </Stepper>
    ```
  </Tab>
</Tabs>

### Progress Bar [#progress-bar]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview className="px-25" name="ProgressBarStepperExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    <Stepper className="items-start gap-4" defaultValue={2}>
      {[1, 2, 3, 4].map((step) => (
        <StepperItem className="flex-1" key={step} step={step}>
          <StepperTrigger className="w-full flex-col items-start gap-2 rounded">
            <StepperIndicator className="h-1 w-full">
              <span className="sr-only">{step}</span>
            </StepperIndicator>
            <StepperHeading className="space-y-0.5">
              <StepperTitle>{`Step ${step}`}</StepperTitle>
            </StepperHeading>
          </StepperTrigger>
        </StepperItem>
      ))}
    </Stepper>
    ```
  </Tab>
</Tabs>

### URL State [#url-state-1]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview className="px-25" name="URLStateStepperExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    <Stepper className="items-start gap-4" defaultValue={1} paramName="step">
      {[
        { step: 1, title: "Account", description: "Create your account" },
        { step: 2, title: "Profile", description: "Setup your profile" },
        { step: 3, title: "Settings", description: "Configure settings" },
      ].map(({ step, title, description }) => (
        <StepperItem className="flex-1" key={step} step={step}>
          <StepperTrigger className="w-full flex-col items-start gap-2 rounded">
            <StepperIndicator className="h-1 w-full">
              <span className="sr-only">{step}</span>
            </StepperIndicator>
            <StepperHeading className="flex flex-col items-start gap-1">
              <StepperTitle>{title}</StepperTitle>
              <StepperDescription className="text-xs">
                {description}
              </StepperDescription>
            </StepperHeading>
          </StepperTrigger>
        </StepperItem>
      ))}
    </Stepper>
    ```
  </Tab>
</Tabs>

### Animated Content [#animated-content]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="AnimatedContentStepperExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    const [showContent, setShowContent] = useState(false);

    <>
      <PreviewFooter>
        <Button onClick={() => setShowContent(!showContent)} variant="secondary">
          {showContent ? "Hide" : "Show"} Step Content
        </Button>
      </PreviewFooter>
      <Stepper animateContent className="w-48" defaultValue={2} orientation="vertical">
        {[1, 2, 3].map((step) => (
          <StepperItem
            className="relative not-last:flex-1 items-start pb-12 last:pb-0"
            key={step}
            step={step}
          >
            <StepperTrigger className="rounded">
              <StepperIndicator />
              <StepperHeading className="space-y-0.5 text-left">
                <StepperTitle>{step === 2 ? "Review Details" : `Step ${step}`}</StepperTitle>
                <StepperDescription>Verify information</StepperDescription>
              </StepperHeading>
            </StepperTrigger>
            {step === 2 && showContent && (
              <div className="mt-4 ml-12 w-72 space-y-3 text-base text-fg-2">
                <p>Review the uploaded materials before continuing.</p>
                <p>The step body can hold supporting copy, form fields, or actions.</p>
                <p>When hidden, the layout collapses around the stepper controls.</p>
              </div>
            )}
            {step < 3 && (
              <StepperSeparator className="absolute inset-y-0 top-[calc(1.5rem+0.125rem)] left-3 -order-1 m-0 -translate-x-1/2 group-data-[orientation=vertical]/stepper:h-[calc(100%-1.5rem-0.25rem)]" />
            )}
          </StepperItem>
        ))}
      </Stepper>
    </>;
    ```
  </Tab>
</Tabs>

## API Reference [#api-reference]

### Stepper [#stepper]

The root component owns active-step state and orientation. It does not add a
surface of its own.

| Prop             | Type                                   | Default        | Description                                            |
| ---------------- | -------------------------------------- | -------------- | ------------------------------------------------------ |
| `defaultValue`   | `number`                               | `0`            | The initial active step when uncontrolled.             |
| `value`          | `number`                               | -              | Controlled active step value.                          |
| `onValueChange`  | `(value: number) => void`              | -              | Called when the active step changes.                   |
| `orientation`    | `"horizontal" \| "vertical"`           | `"horizontal"` | Stepper layout direction.                              |
| `animateContent` | `boolean`                              | `false`        | Animates non-trigger `StepperItem` content in and out. |
| `className`      | `string`                               | -              | Additional classes for layout and sizing.              |
| `...props`       | `React.HTMLAttributes<HTMLDivElement>` | -              | Standard `div` attributes.                             |

### URL State Props [#url-state-props]

| Prop                  | Type                              | Default | Description                                                   |
| --------------------- | --------------------------------- | ------- | ------------------------------------------------------------- |
| `paramName`           | `string`                          | -       | URL parameter name for syncing the step.                      |
| `paramValue`          | `number`                          | -       | Controlled value for URL state.                               |
| `onUrlValueChange`    | `(value: number \| null) => void` | -       | Called when the URL value changes.                            |
| `paramClearOnDefault` | `boolean`                         | `true`  | Removes the URL parameter when the value matches the default. |
| `paramThrottle`       | `number`                          | -       | Throttle window for URL updates.                              |
| `paramDebounce`       | `number`                          | -       | Debounce window for URL updates.                              |

### StepperItem [#stepperitem]

Container for one step. It provides selection state and status to its trigger,
indicator, heading, title, description, and separator.

| Prop        | Type                                   | Default | Description                                                                                  |
| ----------- | -------------------------------------- | ------- | -------------------------------------------------------------------------------------------- |
| `step`      | `number`                               | -       | Step value for this item.                                                                    |
| `completed` | `boolean`                              | `false` | Sets the item status to completed. This can combine with inactive or active selection state. |
| `disabled`  | `boolean`                              | `false` | Disables the step trigger.                                                                   |
| `loading`   | `boolean`                              | `false` | Sets the item status to loading. This can combine with inactive or active selection state.   |
| `className` | `string`                               | -       | Additional classes for the item.                                                             |
| `...props`  | `React.HTMLAttributes<HTMLDivElement>` | -       | Standard `div` attributes.                                                                   |

### StepperTrigger [#steppertrigger]

Interactive control that activates its step.

| Prop        | Type                                            | Default | Description                              |
| ----------- | ----------------------------------------------- | ------- | ---------------------------------------- |
| `render`    | `ReactElement`                                  | -       | Custom element to render as the trigger. |
| `className` | `string`                                        | -       | Additional classes for trigger layout.   |
| `...props`  | `React.ButtonHTMLAttributes<HTMLButtonElement>` | -       | Standard button attributes.              |

### StepperIndicator [#stepperindicator]

Visual step marker. Unreached indicators are surface-aware; reached indicators
use the brand fill. Status controls whether the marker shows the step number,
check icon, or spinner.

| Prop        | Type                                    | Default | Description                                                                |
| ----------- | --------------------------------------- | ------- | -------------------------------------------------------------------------- |
| `children`  | `ReactNode`                             | -       | Custom indicator content. Defaults to step number, check icon, or spinner. |
| `className` | `string`                                | -       | Additional classes for shape and size.                                     |
| `...props`  | `React.HTMLAttributes<HTMLSpanElement>` | -       | Standard `span` attributes.                                                |

### StepperHeading [#stepperheading]

Wrapper for step title and description content. It receives the shared hover
background from the step trigger and the active selection background when its
step is active.

| Prop        | Type                                   | Default | Description                                            |
| ----------- | -------------------------------------- | ------- | ------------------------------------------------------ |
| `className` | `string`                               | -       | Additional classes for layout, alignment, and spacing. |
| `...props`  | `React.HTMLAttributes<HTMLDivElement>` | -       | Standard `div` attributes.                             |

### StepperTitle [#steppertitle]

Title text for the step. Use it inside `StepperHeading` when title and
description should share one interaction background.

| Prop        | Type                                       | Default | Description                       |
| ----------- | ------------------------------------------ | ------- | --------------------------------- |
| `className` | `string`                                   | -       | Additional classes for the title. |
| `...props`  | `React.HTMLAttributes<HTMLHeadingElement>` | -       | Standard heading attributes.      |

### StepperDescription [#stepperdescription]

Supporting text for the step. Use it inside `StepperHeading` with
`StepperTitle`.

| Prop        | Type                                         | Default | Description                             |
| ----------- | -------------------------------------------- | ------- | --------------------------------------- |
| `className` | `string`                                     | -       | Additional classes for the description. |
| `...props`  | `React.HTMLAttributes<HTMLParagraphElement>` | -       | Standard paragraph attributes.          |

### StepperSeparator [#stepperseparator]

Connector between steps. Separators for previous steps use the brand fill;
pending separators use the contextual border color one level above the parent
surface.

| Prop        | Type                                   | Default | Description                                             |
| ----------- | -------------------------------------- | ------- | ------------------------------------------------------- |
| `className` | `string`                               | -       | Additional classes for position, length, and thickness. |
| `...props`  | `React.HTMLAttributes<HTMLDivElement>` | -       | Standard `div` attributes.                              |
