{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "stepper",
  "title": "Stepper",
  "description": "Multi-step progress with an ink-filling connector.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.cuelark.com/r/theme.json",
    "https://ui.cuelark.com/r/motion.json"
  ],
  "files": [
    {
      "path": "src/registry/cuelark/ui/stepper.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { CheckIcon } from \"lucide-react\";\n\nimport { cueTo, gsap, motionScale } from \"@/lib/motion\";\nimport { cn } from \"@/lib/utils\";\n\ntype Step = { title: React.ReactNode; description?: React.ReactNode };\n\ntype StepperProps = Omit<React.ComponentProps<\"ol\">, \"children\"> & {\n  steps: Step[];\n  /** Zero-based index of the current step; earlier steps show as complete. */\n  current: number;\n  orientation?: \"horizontal\" | \"vertical\";\n  /** Makes completed steps clickable. */\n  onStepClick?: (index: number) => void;\n};\n\n/**\n * Progress through a multi-step flow. The connector fills like ink toward the current step, and the\n * newly reached marker pops (GSAP).\n */\nfunction Stepper({ className, steps, current, orientation = \"horizontal\", onStepClick, ...props }: StepperProps) {\n  const rootRef = React.useRef<HTMLOListElement>(null);\n  const first = React.useRef(true);\n  // Server-rendered fill state; afterwards GSAP owns the transform so React never resets a running tween.\n  const initial = React.useRef(current);\n\n  React.useLayoutEffect(() => {\n    const root = rootRef.current;\n    if (!root) return;\n    const scale = first.current ? 0 : motionScale(root);\n    first.current = false;\n    root.querySelectorAll<HTMLElement>('[data-slot=\"stepper-fill\"]').forEach((fill, i) => {\n      cueTo(fill, { [orientation === \"vertical\" ? \"scaleY\" : \"scaleX\"]: i < current ? 1 : 0, duration: 0.5 }, scale);\n    });\n    const marker = root.querySelector<HTMLElement>(`[data-step=\"${current}\"] [data-slot=\"stepper-marker\"]`);\n    if (marker && scale) gsap.fromTo(marker, { scale: 1 - 0.3 * scale }, { scale: 1, duration: 0.5, ease: \"cue-snap\", delay: 0.3 });\n  }, [current, orientation]);\n\n  const vertical = orientation === \"vertical\";\n\n  return (\n    <ol ref={rootRef} data-slot=\"stepper\" data-orientation={orientation} className={cn(\"flex\", vertical ? \"flex-col\" : \"w-full items-start\", className)} {...props}>\n      {steps.map((step, i) => {\n        const state = i < current ? \"complete\" : i === current ? \"current\" : \"upcoming\";\n        const clickable = !!onStepClick && state === \"complete\";\n        const Marker = clickable ? \"button\" : \"span\";\n        return (\n          <li\n            key={i}\n            data-step={i}\n            data-state={state}\n            aria-current={state === \"current\" ? \"step\" : undefined}\n            className={cn(\"group/step relative flex min-w-0\", vertical ? \"gap-3 pb-6 last:pb-0\" : \"flex-1 flex-col gap-2 last:flex-none\")}\n          >\n            <div className={cn(\"flex items-center\", vertical ? \"flex-col self-stretch\" : \"w-full\")}>\n              <Marker\n                {...(clickable ? { type: \"button\" as const, onClick: () => onStepClick?.(i), \"aria-label\": `Go back to step ${i + 1}` } : {})}\n                data-slot=\"stepper-marker\"\n                className={cn(\n                  \"relative z-10 inline-flex size-7 shrink-0 items-center justify-center rounded-full border font-mono text-xs font-medium tabular-nums outline-none transition-colors duration-300\",\n                  \"focus-visible:ring-3 focus-visible:ring-ring/50\",\n                  state === \"complete\" && \"border-primary bg-primary text-primary-foreground shadow-control\",\n                  state === \"current\" && \"border-primary bg-card text-primary shadow-[0_0_0_4px_color-mix(in_oklch,var(--signal)_55%,transparent)]\",\n                  state === \"upcoming\" && \"border-dashed border-input bg-card text-muted-foreground\",\n                )}\n              >\n                {state === \"complete\" ? <CheckIcon className=\"size-3.5\" strokeWidth={3} /> : String(i + 1).padStart(2, \"0\")}\n              </Marker>\n              {i < steps.length - 1 && (\n                <span aria-hidden=\"true\" className={cn(\"relative overflow-hidden rounded-full bg-border\", vertical ? \"my-1 w-px flex-1\" : \"mx-2 h-px flex-1\")}>\n                  <span\n                    data-slot=\"stepper-fill\"\n                    className={cn(\"absolute inset-0 bg-primary\", vertical ? \"origin-top\" : \"origin-left\")}\n                    style={{ transform: `${vertical ? \"scaleY\" : \"scaleX\"}(${i < initial.current ? 1 : 0})` }}\n                  />\n                </span>\n              )}\n            </div>\n            <div className={cn(\"grid gap-0.5\", vertical ? \"pt-1\" : \"pe-2 sm:pe-4\", !vertical && state !== \"current\" && \"max-sm:sr-only\")}>\n              <span className={cn(\"text-sm font-medium\", state === \"upcoming\" && \"text-muted-foreground\")}>{step.title}</span>\n              {step.description && <span className=\"text-xs text-muted-foreground\">{step.description}</span>}\n            </div>\n          </li>\n        );\n      })}\n    </ol>\n  );\n}\n\nexport { Stepper };\nexport type { StepperProps, Step };\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Docs and examples: https://ui.cuelark.com/components/stepper",
  "categories": [
    "navigation"
  ],
  "type": "registry:ui"
}