{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mouse-gradient-blob",
  "type": "registry:ui",
  "title": "Mouse Gradient Blob",
  "description": "Large blurred gradient blob that trails the cursor with spring physics. Ambient hero background.",
  "dependencies": [
    "@saasflare/ui",
    "motion"
  ],
  "files": [
    {
      "path": "components/ui/mouse-gradient-blob.tsx",
      "type": "registry:ui",
      "content": "\"use client\"\n\n/**\n * @fileoverview Mouse-following gradient blob ambient effect.\n * @author Saasflare™\n * Renders a large blurred radial gradient that tracks the mouse position\n * with smooth spring physics. Ideal for hero sections and landing pages.\n * @module packages/ui/components/ui/mouse-gradient-blob\n * @package ui\n *\n * @component\n * @example\n * import { MouseGradientBlob } from '@saasflare/ui';\n * <div className=\"relative overflow-hidden\">\n *   <MouseGradientBlob />\n *   <h1>Your content here</h1>\n * </div>\n *\n * @example\n * // Custom colors and size\n * <MouseGradientBlob\n *   size={600}\n *   colors={[\"var(--primary)\", \"var(--chart-2)\"]}\n *   opacity={0.2}\n * />\n *\n * @example\n * // Freeze the effect via the provider kill-switch or per-instance\n * <MouseGradientBlob animated={false} />\n */\n\nimport * as React from \"react\"\nimport { useEffect, useCallback, useRef } from \"react\"\nimport { m, useMotionValue, useSpring } from \"motion/react\"\nimport { cn } from \"@saasflare/ui\"\nimport {\n  useSaasflareProps,\n  type SaasflareComponentProps,\n} from \"@saasflare/ui\"\nimport { springGentle, useSaasflareMotion } from \"@saasflare/ui\"\n\n/** Props for the MouseGradientBlob component. */\nexport interface MouseGradientBlobProps\n  extends Omit<React.ComponentProps<\"div\">, keyof SaasflareComponentProps>,\n    SaasflareComponentProps {\n  /** Diameter of the blob in pixels. Default: `500` */\n  size?: number\n  /** Gradient color stops. Default: primary + chart-2 tokens */\n  colors?: [string, string]\n  /** Opacity of the blob (0–1). Default: `0.15` */\n  opacity?: number\n  /** Blur radius in pixels. Default: `80` */\n  blur?: number\n  /** Additional class names for the container */\n  className?: string\n}\n\n/**\n * Ambient gradient blob that follows the mouse.\n *\n * - Uses spring physics for smooth, organic motion\n * - Fades out when the mouse leaves the container\n * - Renders nothing when motion is disabled (reduced-motion preference or\n *   `animated={false}` from prop/provider)\n * - Uses `pointer-events: none` so it never blocks interaction\n *\n * @component\n * @package ui\n */\nexport function MouseGradientBlob({\n  size = 500,\n  colors = [\"var(--primary)\", \"var(--chart-2)\"],\n  opacity = 0.15,\n  blur = 80,\n  className,\n  surface,\n  radius,\n  animated,\n  iconWeight,\n  ...rest\n}: MouseGradientBlobProps) {\n  const sf = useSaasflareProps({ surface, radius, animated, iconWeight })\n  const motion = useSaasflareMotion(sf.animated, springGentle)\n\n  const containerRef = useRef<HTMLDivElement>(null)\n\n  const mouseX = useMotionValue(0)\n  const mouseY = useMotionValue(0)\n\n  const blobX = useSpring(mouseX, { stiffness: 150, damping: 20 })\n  const blobY = useSpring(mouseY, { stiffness: 150, damping: 20 })\n\n  /* gBCR per mousemove forces a sync layout read in the hottest input path —\n   * cache the host rect for the hover duration; invalidate on enter/scroll/resize. */\n  const hostRectRef = useRef<DOMRect | null>(null)\n\n  const onMouseMove = useCallback(\n    (e: MouseEvent) => {\n      const rect =\n        hostRectRef.current ??\n        (hostRectRef.current = (e.currentTarget as HTMLElement).getBoundingClientRect())\n      mouseX.set(e.clientX - rect.left - size / 2)\n      mouseY.set(e.clientY - rect.top - size / 2)\n    },\n    [mouseX, mouseY, size],\n  )\n\n  useEffect(() => {\n    // The container is `pointer-events-none`, so it never receives pointer\n    // events — track the cursor on the positioned host element it overlays.\n    const host = containerRef.current?.parentElement\n    if (!host || motion.disabled) return\n\n    const invalidate = () => {\n      hostRectRef.current = null\n    }\n    host.addEventListener(\"mouseenter\", invalidate, { passive: true })\n    window.addEventListener(\"scroll\", invalidate, { passive: true, capture: true })\n    window.addEventListener(\"resize\", invalidate, { passive: true })\n    host.addEventListener(\"mousemove\", onMouseMove as EventListener, { passive: true })\n    return () => {\n      host.removeEventListener(\"mouseenter\", invalidate)\n      window.removeEventListener(\"scroll\", invalidate, { capture: true })\n      window.removeEventListener(\"resize\", invalidate)\n      host.removeEventListener(\"mousemove\", onMouseMove as EventListener)\n    }\n  }, [onMouseMove, motion.disabled])\n\n  if (motion.disabled) return null\n\n  return (\n    <div\n      ref={containerRef}\n      data-blob-container\n      data-slot=\"mouse-gradient-blob\"\n      data-surface={sf.surface}\n      data-radius={sf.radius}\n      data-animated={String(sf.animated)}\n      className={cn(\"pointer-events-none absolute inset-0 overflow-hidden\", className)}\n      aria-hidden=\"true\"\n      {...rest}\n    >\n      <m.div\n        style={{\n          x: blobX,\n          y: blobY,\n          width: size,\n          height: size,\n          background: `radial-gradient(circle, ${colors[0]} 0%, ${colors[1]} 50%, transparent 70%)`,\n          opacity,\n          filter: `blur(${blur}px)`,\n          borderRadius: \"50%\",\n          position: \"absolute\",\n          top: 0,\n          left: 0,\n        }}\n      />\n    </div>\n  )\n}\n",
      "target": "components/ui/mouse-gradient-blob.tsx"
    }
  ],
  "$meta": {
    "source": "https://ui.saasflare.io/r/mouse-gradient-blob.json"
  }
}
