

<Preview name="BasicStepperExample" />

## Overview [#overview]

The `Stepper` component provides a visual representation of a multi-step process, allowing users to navigate through sequential steps with clear progress indication. It supports both horizontal and vertical orientations, controlled and uncontrolled states, and includes loading states for asynchronous operations.

## Usage [#usage]

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

```tsx
<Stepper defaultValue={1}>
  <StepperItem step={1}>
    <StepperTrigger>
      <StepperIndicator />
      <StepperTitle>Step One</StepperTitle>
    </StepperTrigger>
  </StepperItem>
  <StepperItem step={2}>
    <StepperTrigger>
      <StepperIndicator />
      <StepperTitle>Step Two</StepperTitle>
    </StepperTrigger>
  </StepperItem>
</Stepper>
```

<Accordions type="single">
  <Accordion title="URL State Management">
    Using Cubitt's URL-state hooks, you can sync the stepper state with the URL by providing a `paramName`:

    ```tsx
    // Current step synced with ?step=2
    <Stepper paramName="step" defaultValue={1}>
      <StepperItem step={1}>
        <StepperTrigger>
          <StepperIndicator />
          <StepperTitle>Account Setup</StepperTitle>
        </StepperTrigger>
      </StepperItem>
      <StepperItem step={2}>
        <StepperTrigger>
          <StepperIndicator />
          <StepperTitle>Profile Info</StepperTitle>
        </StepperTrigger>
      </StepperItem>
      <StepperItem step={3}>
        <StepperTrigger>
          <StepperIndicator />
          <StepperTitle>Complete</StepperTitle>
        </StepperTrigger>
      </StepperItem>
    </Stepper>

    // Advanced options:
    <Stepper
      paramName="wizard"
      paramClearOnDefault={true}    // Remove param when value equals default
      paramDebounce={300}           // Debounce URL updates
      onUrlValueChange={(step) => console.log('Current step:', step)}
      defaultValue={1}
    >
      {/* ... */}
    </Stepper>
    ```
  </Accordion>
</Accordions>

## Examples [#examples]

### Basic Stepper [#basic-stepper]

A horizontal stepper with titles and descriptions.

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

  <Tab value="Code">
    ```tsx
    <Stepper defaultValue={2}>
      {[
        {
          step: 1,
          title: "Step One",
          description: "Desc for step one",
        },
        {
          step: 2,
          title: "Step Two",
          description: "Desc for step two",
        },
        {
          step: 3,
          title: "Step Three",
          description: "Desc for step three",
        },
      ].map(({ step, title, description }) => (
        <StepperItem key={step} step={step} className="relative flex-1 flex-col!">
          <StepperTrigger className="flex-col gap-3 rounded">
            <StepperIndicator />
            <div className="space-y-0.5 px-2">
              <StepperTitle>{title}</StepperTitle>
              <StepperDescription className="max-sm:hidden">
                {description}
              </StepperDescription>
            </div>
          </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 Orientation [#vertical-orientation]

Stepper with vertical orientation for more detailed descriptions.

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

  <Tab value="Code">
    ```tsx
    <Stepper defaultValue={2} orientation="vertical">
      {[
        {
          step: 1,
          title: "Step One",
          description: "Desc for step one",
        },
        {
          step: 2,
          title: "Step Two",
          description: "Desc for step two",
        },
        {
          step: 3,
          title: "Step Three",
          description: "Desc for step three",
        },
      ].map(({ step, title, description }) => (
        <StepperItem
          key={step}
          step={step}
          className="relative items-start not-last:flex-1"
        >
          <StepperTrigger className="items-start rounded pb-12 last:pb-0">
            <StepperIndicator />
            <div className="mt-0.5 space-y-0.5 px-2 text-left">
              <StepperTitle>{title}</StepperTitle>
              <StepperDescription>{description}</StepperDescription>
            </div>
          </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=horizontal]/stepper:w-[calc(100%-1.5rem-0.25rem)] group-data-[orientation=horizontal]/stepper:flex-none group-data-[orientation=vertical]/stepper:h-[calc(100%-1.5rem-0.25rem)]" />
          )}
        </StepperItem>
      ))}
    </Stepper>
    ```
  </Tab>
</Tabs>

### Mixed Elements [#mixed-elements]

Stepper with custom indicators including images, loading states, and icons.

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

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

    <Stepper defaultValue={2}>
      <StepperItem step={1} className="not-last:flex-1">
        <StepperTrigger>
          <StepperIndicator>
            <img
              className="rounded-full object-cover aspect-square"
              src="https://tilt.legal/_next/image?url=%2Fapi%2Fmedia%2Ffile%2Fsandon-lai.jpg&w=3840&q=75"
              alt="Mike Palmer"
            />
          </StepperIndicator>
        </StepperTrigger>
        <StepperSeparator />
      </StepperItem>
      <StepperItem step={2} className="not-last:flex-1" loading>
        <StepperTrigger>
          <StepperIndicator />
        </StepperTrigger>
        <StepperSeparator />
      </StepperItem>
      <StepperItem step={3} className="not-last:flex-1">
        <StepperTrigger>
          <StepperIndicator>
            <Shuffle size={14} aria-hidden="true" />
            <span className="sr-only">Shuffle</span>
          </StepperIndicator>
        </StepperTrigger>
      </StepperItem>
    </Stepper>;
    ```
  </Tab>
</Tabs>

### Progress Bar Style [#progress-bar-style]

Stepper styled as a progress bar.

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

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

### URL State [#url-state]

Stepper with URL state synchronization for shareable progress.

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

  <Tab value="Code">
    ```tsx
    // The current step will be synced with ?step=2
    <Stepper paramName="step" defaultValue={1} className="items-start gap-4">
      {[
        { 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 key={step} step={step} className="flex-1">
          <StepperTrigger className="w-full flex-col items-start gap-2 rounded">
            <StepperIndicator className="bg-border h-1 w-full" />
            <div className="flex flex-col items-start gap-1">
              <StepperTitle>{title}</StepperTitle>
              <StepperDescription className="text-xs">
                {description}
              </StepperDescription>
            </div>
          </StepperTrigger>
        </StepperItem>
      ))}
    </Stepper>
    ```
  </Tab>
</Tabs>

### Animated Content [#animated-content]

Stepper with smooth height transitions when content is added or removed. Enable with `animateContent` prop.

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

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

    <Stepper
      animateContent
      className="w-full"
      defaultValue={2}
      orientation="vertical"
    >
      {[
        {
          step: 1,
          title: "Upload Documents",
          description: "Select files to upload",
        },
        { step: 2, title: "Review Details", description: "Verify information" },
        { step: 3, title: "Complete", description: "Finish submission" },
      ].map(({ step, title, description }) => (
        <StepperItem
          key={step}
          step={step}
          className="relative items-start not-last:flex-1"
        >
          <StepperTrigger className="items-start rounded pb-4">
            <StepperIndicator />
            <div className="mt-0.5 space-y-0.5 px-2 text-left">
              <StepperTitle>{title}</StepperTitle>
              <StepperDescription>{description}</StepperDescription>
            </div>
          </StepperTrigger>
          {/* Content outside StepperTrigger animates in/out */}
          {step === 2 && showContent && (
            <div className="ml-8 rounded-md border bg-muted/50 p-3">
              <p className="text-sm text-muted-foreground">
                This content animates in and out smoothly when toggled.
              </p>
            </div>
          )}
          {step < 3 && <StepperSeparator className="..." />}
        </StepperItem>
      ))}
    </Stepper>;
    ```
  </Tab>
</Tabs>

## API Reference [#api-reference]

### Stepper [#stepper]

Root component that manages stepper state.

| Prop             | Type                         | Default        | Description                                                                    |
| ---------------- | ---------------------------- | -------------- | ------------------------------------------------------------------------------ |
| `defaultValue`   | `number`                     | `0`            | The default active step when uncontrolled.                                     |
| `value`          | `number`                     | -              | The controlled active step value.                                              |
| `onValueChange`  | `(value: number) => void`    | -              | Event handler called when the active step changes.                             |
| `orientation`    | `'horizontal' \| 'vertical'` | `'horizontal'` | The orientation of the stepper.                                                |
| `animateContent` | `boolean`                    | `false`        | When true, content within StepperItem animates with smooth height transitions. |
| `className`      | `string`                     | -              | Additional CSS classes to apply to the stepper.                                |

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

The following props enable URL state management by syncing the stepper step with URL parameters via TanStack Router search params. When `paramName` is provided, the stepper's state will be reflected in the URL.

| Prop                  | Type                              | Default | Description                                                                                 |
| --------------------- | --------------------------------- | ------- | ------------------------------------------------------------------------------------------- |
| `paramName`           | `string`                          | -       | URL parameter name for syncing state with URL. When provided, enables URL state management. |
| `paramValue`          | `number`                          | -       | Controlled value for the URL parameter (syncs with stepper `value`).                        |
| `onUrlValueChange`    | `(value: number \| null) => void` | -       | Callback when URL parameter value changes.                                                  |
| `paramClearOnDefault` | `boolean`                         | `true`  | Remove URL param when value equals default.                                                 |
| `paramThrottle`       | `number`                          | -       | Throttle URL updates in milliseconds.                                                       |
| `paramDebounce`       | `number`                          | -       | Debounce URL updates in milliseconds.                                                       |

### StepperItem [#stepperitem]

Container for individual step content.

| Prop        | Type      | Default | Description                               |
| ----------- | --------- | ------- | ----------------------------------------- |
| `step`      | `number`  | -       | The step number for this item (required). |
| `completed` | `boolean` | `false` | Whether this step is completed.           |
| `disabled`  | `boolean` | `false` | Whether this step is disabled.            |
| `loading`   | `boolean` | `false` | Whether this step is in a loading state.  |
| `className` | `string`  | -       | Additional CSS classes for the item.      |

### StepperTrigger [#steppertrigger]

Button that activates the step.

| Prop        | Type           | Default | Description                             |
| ----------- | -------------- | ------- | --------------------------------------- |
| `render`    | `ReactElement` | -       | Custom element to render as the trigger |
| `className` | `string`       | -       | Additional CSS classes for the trigger  |

### StepperIndicator [#stepperindicator]

Visual indicator for the step (number, icon, or custom element).

| Prop        | Type     | Default | Description                               |
| ----------- | -------- | ------- | ----------------------------------------- |
| `children`  | `node`   | -       | Custom content to render in the indicator |
| `className` | `string` | -       | Additional CSS classes for the indicator  |

### StepperTitle [#steppertitle]

The title text for the step.

| Prop        | Type     | Default | Description                           |
| ----------- | -------- | ------- | ------------------------------------- |
| `className` | `string` | -       | Additional CSS classes for the title. |

### StepperDescription [#stepperdescription]

Description text that provides context about the step.

| Prop        | Type     | Default | Description                                 |
| ----------- | -------- | ------- | ------------------------------------------- |
| `className` | `string` | -       | Additional CSS classes for the description. |

### StepperSeparator [#stepperseparator]

Visual separator between steps.

| Prop        | Type     | Default | Description                               |
| ----------- | -------- | ------- | ----------------------------------------- |
| `className` | `string` | -       | Additional CSS classes for the separator. |
