{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "magnetic-button",
  "title": "Magnetic Button",
  "description": "A tactile action that leans toward the pointer and snaps back.",
  "registryDependencies": [
    "@rivelle/utils"
  ],
  "files": [
    {
      "path": "registry/nova/effects/magnetic-button.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\ntype MagneticButtonProps = React.ComponentProps<\"button\"> & {\n  strength?: number;\n};\n\nfunction MagneticButton({\n  children,\n  className,\n  onPointerLeave,\n  onPointerMove,\n  strength = 18,\n  style,\n  type = \"button\",\n  ...props\n}: MagneticButtonProps) {\n  const frame = React.useRef<number | null>(null);\n\n  const move = (\n    element: HTMLButtonElement,\n    x: number,\n    y: number,\n    glowX: number,\n    glowY: number,\n  ) => {\n    if (frame.current !== null) cancelAnimationFrame(frame.current);\n    frame.current = requestAnimationFrame(() => {\n      element.style.setProperty(\"--magnetic-x\", `${x}px`);\n      element.style.setProperty(\"--magnetic-y\", `${y}px`);\n      element.style.setProperty(\"--magnetic-glow-x\", `${glowX}%`);\n      element.style.setProperty(\"--magnetic-glow-y\", `${glowY}%`);\n    });\n  };\n\n  React.useEffect(\n    () => () => {\n      if (frame.current !== null) cancelAnimationFrame(frame.current);\n    },\n    [],\n  );\n\n  return (\n    <button\n      className={cn(\n        \"group relative inline-flex min-h-12 items-center justify-center gap-2 overflow-hidden rounded-full bg-foreground px-6 text-sm font-semibold text-background shadow-[0_18px_50px_-24px_var(--foreground)] outline-none transition-[transform,box-shadow] duration-200 ease-out hover:shadow-[0_24px_65px_-28px_var(--foreground)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background active:scale-[.98] disabled:pointer-events-none disabled:opacity-50 motion-reduce:transform-none motion-reduce:transition-none\",\n        className,\n      )}\n      data-slot=\"magnetic-button\"\n      onPointerLeave={(event) => {\n        move(event.currentTarget, 0, 0, 50, 50);\n        onPointerLeave?.(event);\n      }}\n      onPointerMove={(event) => {\n        if (!window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) {\n          const bounds = event.currentTarget.getBoundingClientRect();\n          const relativeX = (event.clientX - bounds.left) / bounds.width;\n          const relativeY = (event.clientY - bounds.top) / bounds.height;\n          const x = (relativeX - 0.5) * strength * 0.72;\n          const y = (relativeY - 0.5) * strength * 0.72;\n          move(event.currentTarget, x, y, relativeX * 100, relativeY * 100);\n        }\n        onPointerMove?.(event);\n      }}\n      style={\n        {\n          \"--magnetic-glow-x\": \"50%\",\n          \"--magnetic-glow-y\": \"50%\",\n          \"--magnetic-x\": \"0px\",\n          \"--magnetic-y\": \"0px\",\n          ...style,\n        } as React.CSSProperties\n      }\n      type={type}\n      {...props}\n    >\n      <span\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute inset-0 bg-primary opacity-0 transition-opacity duration-300 ease-out group-hover:opacity-100\"\n      />\n      <span\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-200 group-hover:opacity-100\"\n        style={{\n          background:\n            \"radial-gradient(70px circle at var(--magnetic-glow-x) var(--magnetic-glow-y), color-mix(in oklch, white 32%, transparent), transparent 72%)\",\n        }}\n      />\n      <span\n        className=\"relative z-10 inline-flex items-center gap-2 transition-[color,transform] duration-150 ease-out will-change-transform [backface-visibility:hidden] group-hover:text-primary-foreground motion-reduce:transform-none\"\n        style={{\n          transform: \"translate3d(var(--magnetic-x), var(--magnetic-y), 0)\",\n        }}\n      >\n        {children}\n      </span>\n    </button>\n  );\n}\n\nexport { MagneticButton };\nexport type { MagneticButtonProps };\n",
      "type": "registry:ui",
      "target": "@effects/magnetic-button.tsx"
    }
  ],
  "type": "registry:ui"
}