{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "stepper",
  "title": "Stepper",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/stepper/stepper.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { Check, X, type LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { stepperIndicatorVariants } from './stepper.variants'\nimport {\n  StepperContext,\n  useStepperContext,\n  type StepperOrientation,\n  type StepperSize,\n  type StepperStatus,\n} from './context'\n// Aliased: the standalone <StepperStep> component (below) owns the bare name.\nimport type { StepperStep as StepperStepConfig } from './types'\n\n/* ------------------------------------------------------------------ */\n/* Stepper (root)                                                      */\n/* ------------------------------------------------------------------ */\n\nexport interface StepperProps {\n  steps?: StepperStepConfig[]\n  value?: number\n  onValueChange?: (value: number) => void\n  orientation?: StepperOrientation\n  size?: StepperSize\n  className?: string\n  /** Custom header strip. Falls back to the auto-rendered `<ol>` of items. */\n  stepsSlot?: React.ReactNode\n  /** Content area. Receives the active step + steps for convenience. */\n  children?: React.ReactNode | ((args: { activeStep: number; steps: StepperStepConfig[] }) => React.ReactNode)\n}\n\nconst Stepper = React.forwardRef<HTMLDivElement, StepperProps>(\n  (\n    { steps = [], value = 1, onValueChange, orientation = 'horizontal', size = 'default', className, stepsSlot, children },\n    ref,\n  ) => {\n    const activeStep = value\n\n    const getStatus = React.useCallback(\n      (index: number): StepperStatus => {\n        const step = steps[index]\n        if (step?.error) return 'error'\n        if (index + 1 === activeStep) return 'active'\n        if (index + 1 < activeStep) return 'completed'\n        return 'pending'\n      },\n      [steps, activeStep],\n    )\n\n    const isClickable = React.useCallback((index: number): boolean => index + 1 < activeStep, [activeStep])\n\n    const goToStep = React.useCallback(\n      (stepIndex: number) => {\n        if (stepIndex < 1 || stepIndex > steps.length) return\n        const step = steps[stepIndex - 1]\n        if (step?.disabled) return\n        onValueChange?.(stepIndex)\n      },\n      [steps, onValueChange],\n    )\n\n    const ctx = React.useMemo(\n      () => ({ orientation, size, activeStep, steps, goToStep, isClickable, getStatus }),\n      [orientation, size, activeStep, steps, goToStep, isClickable, getStatus],\n    )\n\n    return (\n      <StepperContext.Provider value={ctx}>\n        <div\n          ref={ref}\n          className={cn('w-full', className)}\n          role=\"tablist\"\n          aria-orientation={orientation}\n          data-orientation={orientation}\n        >\n          {/* Header strip with steps */}\n          {stepsSlot ??\n            (steps.length > 0 && (\n              <ol\n                className={cn(\n                  'flex',\n                  orientation === 'horizontal' ? 'flex-row items-start' : 'flex-col items-stretch',\n                )}\n              >\n                {steps.map((step, index) => (\n                  <StepperItem key={step.id} step={step} index={index} />\n                ))}\n              </ol>\n            ))}\n\n          {/* Content area */}\n          {children != null && (\n            <div className=\"mt-6 flex-1\">\n              {typeof children === 'function' ? children({ activeStep, steps }) : children}\n            </div>\n          )}\n        </div>\n      </StepperContext.Provider>\n    )\n  },\n)\nStepper.displayName = 'Stepper'\n\n/* ------------------------------------------------------------------ */\n/* StepperIndicator                                                    */\n/* ------------------------------------------------------------------ */\n\nexport interface StepperIndicatorProps {\n  status?: StepperStatus\n  size?: StepperSize\n  index?: number\n  icon?: LucideIcon\n  clickable?: boolean\n  className?: string\n  onClick?: () => void\n  children?: React.ReactNode\n}\n\nconst StepperIndicator = React.forwardRef<HTMLButtonElement, StepperIndicatorProps>(\n  ({ status = 'pending', size = 'default', index, icon: Icon, clickable = false, className, onClick, children }, ref) => {\n    const FallbackIcon: LucideIcon | null =\n      Icon ?? (status === 'completed' ? Check : status === 'error' ? X : null)\n\n    return (\n      <button\n        ref={ref}\n        type=\"button\"\n        className={cn(\n          stepperIndicatorVariants({ status, size }),\n          'ring-background relative z-10 ring-4 transition-colors duration-200 outline-none',\n          clickable && 'focus-visible:ring-ring cursor-pointer focus-visible:ring-2 focus-visible:outline-none',\n          !clickable && 'cursor-default',\n          className,\n        )}\n        disabled={!clickable}\n        aria-current={status === 'active' ? 'step' : undefined}\n        onClick={onClick}\n      >\n        {children ??\n          (FallbackIcon ? (\n            <FallbackIcon className=\"size-4\" aria-hidden=\"true\" />\n          ) : index !== undefined ? (\n            <span className=\"font-medium\">{index}</span>\n          ) : null)}\n      </button>\n    )\n  },\n)\nStepperIndicator.displayName = 'StepperIndicator'\n\n/* ------------------------------------------------------------------ */\n/* StepperItem                                                         */\n/* ------------------------------------------------------------------ */\n\nexport interface StepperItemProps {\n  step: StepperStepConfig\n  index: number\n  className?: string\n}\n\nconst StepperItem = React.forwardRef<HTMLLIElement, StepperItemProps>(({ step, index, className }, ref) => {\n  const ctx = useStepperContext()\n\n  const status = ctx.getStatus(index)\n  const orientation = ctx.orientation\n  const isFirst = index === 0\n  const isLast = index === ctx.steps.length - 1\n  const clickable = ctx.isClickable(index) && !step.disabled\n\n  // A connector \"segment\" is the line drawn between this indicator and the\n  // adjacent one. We split it into left/right halves so each item owns its\n  // own piece — they butt up at item boundaries for pixel alignment.\n  const leftSegmentCompleted = index < ctx.activeStep\n  const rightSegmentCompleted = index < ctx.activeStep - 1\n\n  function handleNavigate() {\n    if (clickable) ctx.goToStep(index + 1)\n  }\n\n  return (\n    <li\n      ref={ref}\n      className={cn(\n        'group/stepper-item relative min-w-0',\n        orientation === 'horizontal'\n          ? 'flex flex-1 flex-col items-center gap-2'\n          : 'flex flex-row items-start gap-3 pb-6 last:pb-0',\n        step.disabled && 'opacity-50',\n        className,\n      )}\n      role=\"tab\"\n      aria-selected={status === 'active'}\n      aria-disabled={step.disabled || undefined}\n      data-status={status}\n    >\n      {/* Indicator row: contains the indicator + connector segments */}\n      <div\n        className={cn(\n          'relative flex shrink-0',\n          orientation === 'horizontal'\n            ? 'h-9 w-full items-center justify-center'\n            : 'w-9 flex-col items-center justify-start self-stretch',\n        )}\n      >\n        {/* Connector segments (absolute, butt up at item boundaries) */}\n        {!isFirst && (\n          <span\n            aria-hidden=\"true\"\n            className={cn(\n              'pointer-events-none absolute transition-colors duration-200',\n              orientation === 'horizontal'\n                ? 'top-1/2 right-1/2 left-0 h-px -translate-y-1/2'\n                : 'top-0 bottom-1/2 left-1/2 w-px -translate-x-1/2',\n              leftSegmentCompleted ? 'bg-primary' : 'bg-border',\n            )}\n          />\n        )}\n        {!isLast && (\n          <span\n            aria-hidden=\"true\"\n            className={cn(\n              'pointer-events-none absolute transition-colors duration-200',\n              orientation === 'horizontal'\n                ? 'top-1/2 right-0 left-1/2 h-px -translate-y-1/2'\n                : 'top-1/2 bottom-0 left-1/2 w-px -translate-x-1/2',\n              rightSegmentCompleted ? 'bg-primary' : 'bg-border',\n            )}\n          />\n        )}\n\n        <StepperIndicator\n          status={status}\n          index={index + 1}\n          icon={step.icon}\n          clickable={clickable}\n          className=\"focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none\"\n          onClick={handleNavigate}\n        />\n      </div>\n\n      {/* Title + description */}\n      <div className={cn('min-w-0', orientation === 'horizontal' ? 'max-w-[12rem] text-center' : 'flex-1 pt-1.5')}>\n        <button\n          type=\"button\"\n          className={cn(\n            'text-foreground text-sm font-medium text-balance transition-colors outline-none',\n            clickable &&\n              'hover:text-primary focus-visible:text-primary focus-visible:ring-ring cursor-pointer focus-visible:ring-2 focus-visible:outline-none',\n            !clickable && 'cursor-default',\n            status === 'pending' && 'text-muted-foreground',\n            status === 'error' && 'text-destructive',\n          )}\n          disabled={!clickable}\n          onClick={handleNavigate}\n        >\n          {step.title}\n        </button>\n        {step.description && (\n          <p className=\"text-muted-foreground mt-0.5 text-xs text-balance\">{step.description}</p>\n        )}\n      </div>\n    </li>\n  )\n})\nStepperItem.displayName = 'StepperItem'\n\n/* ------------------------------------------------------------------ */\n/* StepperHeader                                                       */\n/* ------------------------------------------------------------------ */\n\nconst StepperHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => (\n    <div ref={ref} className={cn('stepper-header flex items-center gap-0', className)} {...props} />\n  ),\n)\nStepperHeader.displayName = 'StepperHeader'\n\n/* ------------------------------------------------------------------ */\n/* StepperContent                                                      */\n/* ------------------------------------------------------------------ */\n\nexport interface StepperContentProps extends React.HTMLAttributes<HTMLDivElement> {\n  step?: number\n  activeStep?: number\n}\n\nconst StepperContent = React.forwardRef<HTMLDivElement, StepperContentProps>(\n  ({ step = 1, activeStep = 1, className, children, ...props }, ref) => {\n    const isActive = step === activeStep\n    return (\n      <div\n        ref={ref}\n        className={cn(\n          'stepper-content motion-safe:animate-in motion-safe:fade-in-50 motion-safe:duration-200',\n          className,\n        )}\n        style={!isActive ? { display: 'none' } : undefined}\n        role=\"tabpanel\"\n        aria-hidden={!isActive}\n        {...props}\n      >\n        {children}\n      </div>\n    )\n  },\n)\nStepperContent.displayName = 'StepperContent'\n\n/* ------------------------------------------------------------------ */\n/* StepperTitle                                                        */\n/* ------------------------------------------------------------------ */\n\nconst StepperTitle = React.forwardRef<HTMLSpanElement, React.HTMLAttributes<HTMLSpanElement>>(\n  ({ className, ...props }, ref) => (\n    <span ref={ref} className={cn('text-foreground text-sm font-medium', className)} {...props} />\n  ),\n)\nStepperTitle.displayName = 'StepperTitle'\n\n/* ------------------------------------------------------------------ */\n/* StepperDescription                                                  */\n/* ------------------------------------------------------------------ */\n\nconst StepperDescription = React.forwardRef<HTMLSpanElement, React.HTMLAttributes<HTMLSpanElement>>(\n  ({ className, ...props }, ref) => (\n    <span ref={ref} className={cn('text-muted-foreground text-xs', className)} {...props} />\n  ),\n)\nStepperDescription.displayName = 'StepperDescription'\n\n/* ------------------------------------------------------------------ */\n/* StepperStep (standalone, slot-driven)                               */\n/* ------------------------------------------------------------------ */\n\nexport interface StepperStepProps {\n  title: string\n  description?: string\n  icon?: React.ReactNode\n  completed?: boolean\n  active?: boolean\n  error?: boolean\n  disabled?: boolean\n  status?: 'active' | 'completed' | 'pending' | 'error'\n  index?: number\n  className?: string\n  titleSlot?: React.ReactNode\n  descriptionSlot?: React.ReactNode\n  iconSlot?: React.ReactNode\n}\n\nconst StepperStep = React.forwardRef<HTMLDivElement, StepperStepProps>(\n  (\n    {\n      title,\n      description,\n      completed = false,\n      active = false,\n      error = false,\n      disabled = false,\n      status,\n      index,\n      className,\n      titleSlot,\n      descriptionSlot,\n      iconSlot,\n    },\n    ref,\n  ) => {\n    const computedStatus: StepperStatus = status\n      ? status\n      : error\n        ? 'error'\n        : active\n          ? 'active'\n          : completed\n            ? 'completed'\n            : 'pending'\n\n    return (\n      <div\n        ref={ref}\n        className={cn('stepper-step flex gap-3', className)}\n        role=\"tab\"\n        aria-selected={active}\n        aria-disabled={disabled}\n      >\n        {/* Indicator */}\n        <div className={cn(stepperIndicatorVariants({ status: computedStatus, size: 'default' }))}>\n          {iconSlot ??\n            (computedStatus === 'completed' ? (\n              <Check className=\"size-4\" aria-hidden=\"true\" />\n            ) : index ? (\n              <span>{index}</span>\n            ) : null)}\n        </div>\n\n        {/* Content */}\n        <div className=\"flex flex-col gap-0.5 pt-1\">\n          {titleSlot ?? <span className=\"text-sm font-medium\">{title}</span>}\n          {descriptionSlot ??\n            (description && <span className=\"text-muted-foreground text-xs\">{description}</span>)}\n        </div>\n      </div>\n    )\n  },\n)\nStepperStep.displayName = 'StepperStep'\n\nexport {\n  Stepper,\n  StepperItem,\n  StepperIndicator,\n  StepperHeader,\n  StepperContent,\n  StepperTitle,\n  StepperDescription,\n  StepperStep,\n}\n",
      "type": "registry:ui",
      "target": "~/components/ui/stepper/stepper.tsx"
    },
    {
      "path": "packages/registry-react/components/stepper/context.ts",
      "content": "'use client'\n\nimport * as React from 'react'\nimport type { StepperStep } from './types'\n\nexport type StepperOrientation = 'horizontal' | 'vertical'\nexport type StepperStatus = 'active' | 'completed' | 'pending' | 'error'\nexport type StepperSize = 'sm' | 'default' | 'lg'\n\nexport interface StepperContextValue {\n  orientation: StepperOrientation\n  size: StepperSize\n  activeStep: number\n  steps: StepperStep[]\n  goToStep: (stepIndex: number) => void\n  isClickable: (index: number) => boolean\n  getStatus: (index: number) => StepperStatus\n}\n\nexport const StepperContext = React.createContext<StepperContextValue | null>(null)\n\nexport function useStepperContext(): StepperContextValue {\n  const ctx = React.useContext(StepperContext)\n  if (!ctx) throw new Error('StepperItem must be used inside <Stepper>')\n  return ctx\n}\n",
      "type": "registry:ui",
      "target": "~/components/ui/stepper/context.ts"
    },
    {
      "path": "packages/registry-react/components/stepper/stepper.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file (rather than the package\n * `index.ts`) so `StepperIndicator.vue` / `StepperStep.vue` can import\n * them without creating a circular dependency back through the index.\n * Sibling pattern to `card/card.variants.ts`.\n */\nexport const stepperIndicatorVariants = cva('flex items-center justify-center rounded-full font-semibold shrink-0', {\n  variants: {\n    status: {\n      pending: 'bg-muted text-muted-foreground',\n      active: 'bg-primary text-primary-foreground shadow-sm',\n      completed: 'bg-primary text-primary-foreground',\n      error: 'bg-destructive text-destructive-foreground',\n    },\n    size: {\n      sm: 'size-7 text-xs [&>svg]:size-3.5',\n      default: 'size-9 text-sm [&>svg]:size-4',\n      lg: 'size-11 text-base [&>svg]:size-5',\n    },\n  },\n  defaultVariants: {\n    status: 'pending',\n    size: 'default',\n  },\n})\n\nexport type StepperIndicatorVariants = VariantProps<typeof stepperIndicatorVariants>\n",
      "type": "registry:ui",
      "target": "~/components/ui/stepper/stepper.variants.ts"
    },
    {
      "path": "packages/registry-react/components/stepper/types.ts",
      "content": "import type { LucideIcon } from 'lucide-react'\n\nexport interface StepperStep {\n  id: string | number\n  title: string\n  description?: string\n  icon?: LucideIcon\n  disabled?: boolean\n  error?: boolean\n}\n",
      "type": "registry:ui",
      "target": "~/components/ui/stepper/types.ts"
    },
    {
      "path": "packages/registry-react/components/stepper/index.ts",
      "content": "export {\n  Stepper,\n  StepperHeader,\n  StepperItem,\n  StepperIndicator,\n  StepperContent,\n  StepperTitle,\n  StepperDescription,\n  StepperStep,\n  type StepperProps,\n  type StepperItemProps,\n  type StepperIndicatorProps,\n  type StepperContentProps,\n  type StepperStepProps,\n} from './stepper'\nexport type { StepperStep as StepperStepConfig } from './types'\nexport type { StepperOrientation, StepperSize, StepperStatus } from './context'\n\n// Re-export variant API from the sibling file (kept separate to avoid the\n// stepper.tsx <-> index.ts circular import that broke dev SSR in the Vue source).\nexport { stepperIndicatorVariants, type StepperIndicatorVariants } from './stepper.variants'\n",
      "type": "registry:ui",
      "target": "~/components/ui/stepper/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Multi-step indicator — horizontal or vertical, with completed / current / upcoming states and optional descriptions per step. Use for onboarding wizards and checkout flows.",
  "categories": [
    "navigation"
  ]
}