{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "spinner",
  "title": "Spinner",
  "description": "Loaders, including the signature crop-mark spinner.",
  "dependencies": [
    "class-variance-authority"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.cuelark.com/r/theme.json",
    "https://ui.cuelark.com/r/motion.json"
  ],
  "files": [
    {
      "path": "src/registry/cuelark/ui/spinner.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { gsap, motionScale, useGSAP } from \"@/lib/motion\";\nimport { cn } from \"@/lib/utils\";\n\nconst spinnerVariants = cva(\"inline-block shrink-0 text-current\", {\n  variants: { size: { sm: \"size-4\", md: \"size-6\", lg: \"size-10\" } },\n  defaultVariants: { size: \"md\" },\n});\n\ntype SpinnerProps = Omit<React.ComponentProps<\"span\">, \"children\"> &\n  VariantProps<typeof spinnerVariants> & {\n    /**\n     * `marks`: the signature loader, four crop marks that close in, turn a quarter and open again.\n     * `ring` and `dots` are quieter alternatives.\n     */\n    variant?: \"marks\" | \"ring\" | \"dots\";\n    /** Accessible status text. */\n    label?: string;\n  };\n\nconst CORNERS = [\n  { d: \"M3 8V3h5\", x: 2, y: 2 },\n  { d: \"M16 3h5v5\", x: -2, y: 2 },\n  { d: \"M21 16v5h-5\", x: -2, y: -2 },\n  { d: \"M8 21H3v-5\", x: 2, y: -2 },\n];\n\nfunction Spinner({ className, size, variant = \"marks\", label = \"Loading\", ...props }: SpinnerProps) {\n  const ref = React.useRef<HTMLSpanElement>(null);\n\n  useGSAP(\n    () => {\n      const root = ref.current;\n      if (!root || variant === \"ring\") return;\n      const scale = motionScale(root);\n      if (!scale) return;\n      if (variant === \"marks\") {\n        const svg = root.querySelector(\"svg\")!;\n        const corners = Array.from(root.querySelectorAll<SVGPathElement>(\"path\"));\n        const tl = gsap.timeline({ repeat: -1, defaults: { ease: \"cue\" } });\n        tl.to(corners, { x: (i) => CORNERS[i]!.x, y: (i) => CORNERS[i]!.y, duration: 0.3, stagger: 0.04 })\n          .to(svg, { rotate: \"+=90\", duration: 0.45, ease: \"cue-snap\" }, \"<0.1\")\n          .to(corners, { x: 0, y: 0, duration: 0.35, stagger: 0.04 }, \"<0.15\");\n      } else {\n        gsap.to(root.querySelectorAll(\"circle\"), { y: -3, duration: 0.32, ease: \"sine.inOut\", yoyo: true, repeat: -1, stagger: { each: 0.12, repeat: -1, yoyo: true } });\n      }\n    },\n    { scope: ref, dependencies: [variant] },\n  );\n\n  return (\n    <span ref={ref} role=\"status\" aria-label={label} data-slot=\"spinner\" className={cn(spinnerVariants({ size }), className)} {...props}>\n      {variant === \"marks\" && (\n        <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\" className=\"size-full\">\n          {CORNERS.map((c) => (\n            <path key={c.d} d={c.d} />\n          ))}\n        </svg>\n      )}\n      {variant === \"ring\" && (\n        <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" className=\"size-full animate-spin motion-reduce:animate-[spin_1.6s_linear_infinite]\">\n          <circle cx=\"12\" cy=\"12\" r=\"9\" stroke=\"currentColor\" strokeOpacity=\"0.2\" strokeWidth=\"2.5\" />\n          <path d=\"M21 12a9 9 0 0 0-9-9\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" />\n        </svg>\n      )}\n      {variant === \"dots\" && (\n        <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\" className=\"size-full\">\n          <circle cx=\"5\" cy=\"12\" r=\"2.2\" />\n          <circle cx=\"12\" cy=\"12\" r=\"2.2\" />\n          <circle cx=\"19\" cy=\"12\" r=\"2.2\" />\n        </svg>\n      )}\n    </span>\n  );\n}\n\nexport { Spinner, spinnerVariants };\nexport type { SpinnerProps };\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Docs and examples: https://ui.cuelark.com/components/spinner",
  "categories": [
    "feedback"
  ],
  "type": "registry:ui"
}