shadimage

Stepper

A compact horizontal or vertical stepper for install, publish, and form flows.

registry/corr/components/stepper.tsx

import type { CSSProperties } from "react"
import { Check } from "lucide-react"

import { cn } from "@/lib/utils"

export type StepperItem = {
  id: string
  title: string
  description?: string
}

export function Stepper({
  steps,
  currentStep,
  orientation = "horizontal",
  className,
}: {
  steps: StepperItem[]
  currentStep: number
  orientation?: "horizontal" | "vertical"
  className?: string
}) {
  return (
    <div
      className={cn(
        orientation === "vertical"
          ? "grid gap-4"
          : "grid gap-4 sm:grid-cols-[repeat(var(--step-count),minmax(0,1fr))]",
        className
      )}
      style={{ "--step-count": steps.length } as CSSProperties}
    >
      {steps.map((step, index) => {
        const complete = index < currentStep
        const active = index === currentStep

        return (
          <div
            key={step.id}
            className={cn(
              "relative flex gap-3",
              orientation === "horizontal" && "sm:flex-col"
            )}
          >
            <div className="flex items-center gap-3">
              <div
                className={cn(
                  "flex size-7 shrink-0 items-center justify-center rounded-full border text-xs font-medium transition-colors",
                  complete &&
                    "border-primary bg-primary text-primary-foreground",
                  active && "border-primary text-primary",
                  !complete && !active && "border-border text-muted-foreground"
                )}
              >
                {complete ? <Check className="size-3.5" /> : index + 1}
              </div>
              {index < steps.length - 1 ? (
                <div
                  className={cn(
                    "bg-border",
                    orientation === "vertical"
                      ? "absolute top-8 bottom-[-1rem] left-3.5 w-px"
                      : "hidden h-px flex-1 sm:block",
                    complete && "bg-primary"
                  )}
                />
              ) : null}
            </div>
            <div className="min-w-0">
              <div className="text-sm font-medium">{step.title}</div>
              {step.description ? (
                <div className="mt-1 text-xs leading-5 text-muted-foreground">
                  {step.description}
                </div>
              ) : null}
            </div>
          </div>
        )
      })}
    </div>
  )
}
Where it goes
onboarding

DependenciesBundle weight · light

icons
lucide-react