{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "components-backgrounds-bubble",
  "title": "Bubble Background",
  "description": "An interactive background featuring smoothly animated gradient bubbles, creating a playful, dynamic, and visually engaging backdrop.",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/components/backgrounds/bubble/index.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport {\n  motion,\n  useMotionValue,\n  useSpring,\n  type SpringOptions,\n} from 'motion/react';\n\nimport { cn } from '@/lib/utils';\n\ntype BubbleColors = {\n  first: string;\n  second: string;\n  third: string;\n  fourth: string;\n  fifth: string;\n  sixth: string;\n};\n\ntype BubbleBackgroundProps = React.ComponentProps<'div'> & {\n  interactive?: boolean;\n  transition?: SpringOptions;\n  colors?: BubbleColors;\n};\n\nfunction BubbleBackground({\n  ref,\n  className,\n  children,\n  interactive = false,\n  transition = { stiffness: 100, damping: 20 },\n  colors = {\n    first: '18,113,255',\n    second: '221,74,255',\n    third: '0,220,255',\n    fourth: '200,50,50',\n    fifth: '180,180,50',\n    sixth: '140,100,255',\n  },\n  ...props\n}: BubbleBackgroundProps) {\n  const containerRef = React.useRef<HTMLDivElement>(null);\n  React.useImperativeHandle(ref, () => containerRef.current as HTMLDivElement);\n\n  const mouseX = useMotionValue(0);\n  const mouseY = useMotionValue(0);\n  const springX = useSpring(mouseX, transition);\n  const springY = useSpring(mouseY, transition);\n\n  const rectRef = React.useRef<DOMRect | null>(null);\n  const rafIdRef = React.useRef<number | null>(null);\n\n  React.useLayoutEffect(() => {\n    const updateRect = () => {\n      if (containerRef.current) {\n        rectRef.current = containerRef.current.getBoundingClientRect();\n      }\n    };\n\n    updateRect();\n\n    const el = containerRef.current;\n    const ro = new ResizeObserver(updateRect);\n    if (el) ro.observe(el);\n\n    window.addEventListener('resize', updateRect);\n    window.addEventListener('scroll', updateRect, { passive: true });\n\n    return () => {\n      ro.disconnect();\n      window.removeEventListener('resize', updateRect);\n      window.removeEventListener('scroll', updateRect);\n    };\n  }, []);\n\n  React.useEffect(() => {\n    if (!interactive) return;\n\n    const el = containerRef.current;\n    if (!el) return;\n\n    const handleMouseMove = (e: MouseEvent) => {\n      const rect = rectRef.current;\n      if (!rect) return;\n      const centerX = rect.left + rect.width / 2;\n      const centerY = rect.top + rect.height / 2;\n\n      if (rafIdRef.current != null) cancelAnimationFrame(rafIdRef.current);\n      rafIdRef.current = requestAnimationFrame(() => {\n        mouseX.set(e.clientX - centerX);\n        mouseY.set(e.clientY - centerY);\n      });\n    };\n\n    el.addEventListener('mousemove', handleMouseMove as EventListener, {\n      passive: true,\n    });\n    return () => {\n      el.removeEventListener('mousemove', handleMouseMove as EventListener);\n      if (rafIdRef.current != null) cancelAnimationFrame(rafIdRef.current);\n    };\n  }, [interactive, mouseX, mouseY]);\n\n  return (\n    <div\n      ref={containerRef}\n      data-slot=\"bubble-background\"\n      className={cn(\n        'relative size-full overflow-hidden bg-gradient-to-br from-violet-900 to-blue-900',\n        className,\n      )}\n      {...props}\n    >\n      <style>\n        {`\n            :root {\n              --first-color: ${colors.first};\n              --second-color: ${colors.second};\n              --third-color: ${colors.third};\n              --fourth-color: ${colors.fourth};\n              --fifth-color: ${colors.fifth};\n              --sixth-color: ${colors.sixth};\n            }\n          `}\n      </style>\n\n      <svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        className=\"absolute top-0 left-0 w-0 h-0\"\n      >\n        <defs>\n          <filter id=\"goo\">\n            <feGaussianBlur\n              in=\"SourceGraphic\"\n              stdDeviation=\"16\"\n              result=\"blur\"\n            />\n            <feColorMatrix\n              in=\"blur\"\n              mode=\"matrix\"\n              values=\"1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 18 -8\"\n              result=\"goo\"\n            />\n            <feBlend in=\"SourceGraphic\" in2=\"goo\" />\n          </filter>\n        </defs>\n      </svg>\n\n      <div\n        className=\"absolute inset-0\"\n        style={{ filter: 'url(#goo) blur(40px)' }}\n      >\n        <motion.div\n          className=\"absolute rounded-full size-[80%] top-[10%] left-[10%] mix-blend-hard-light bg-[radial-gradient(circle_at_center,rgba(var(--first-color),0.8)_0%,rgba(var(--first-color),0)_50%)]\"\n          animate={{ y: [-50, 50, -50] }}\n          transition={{ duration: 30, ease: 'easeInOut', repeat: Infinity }}\n          style={{ transform: 'translateZ(0)', willChange: 'transform' }}\n        />\n\n        <motion.div\n          className=\"absolute inset-0 flex justify-center items-center origin-[calc(50%-400px)]\"\n          animate={{ rotate: 360 }}\n          transition={{\n            duration: 20,\n            ease: 'linear',\n            repeat: Infinity,\n            repeatType: 'loop',\n          }}\n          style={{ transform: 'translateZ(0)', willChange: 'transform' }}\n        >\n          <div className=\"rounded-full size-[80%] top-[10%] left-[10%] mix-blend-hard-light bg-[radial-gradient(circle_at_center,rgba(var(--second-color),0.8)_0%,rgba(var(--second-color),0)_50%)]\" />\n        </motion.div>\n\n        <motion.div\n          className=\"absolute inset-0 flex justify-center items-center origin-[calc(50%+400px)]\"\n          animate={{ rotate: 360 }}\n          transition={{ duration: 40, ease: 'linear', repeat: Infinity }}\n          style={{ transform: 'translateZ(0)', willChange: 'transform' }}\n        >\n          <div className=\"absolute rounded-full size-[80%] bg-[radial-gradient(circle_at_center,rgba(var(--third-color),0.8)_0%,rgba(var(--third-color),0)_50%)] mix-blend-hard-light top-[calc(50%+200px)] left-[calc(50%-500px)]\" />\n        </motion.div>\n\n        <motion.div\n          className=\"absolute rounded-full size-[80%] top-[10%] left-[10%] mix-blend-hard-light bg-[radial-gradient(circle_at_center,rgba(var(--fourth-color),0.8)_0%,rgba(var(--fourth-color),0)_50%)] opacity-70\"\n          animate={{ x: [-50, 50, -50] }}\n          transition={{ duration: 40, ease: 'easeInOut', repeat: Infinity }}\n          style={{ transform: 'translateZ(0)', willChange: 'transform' }}\n        />\n\n        <motion.div\n          className=\"absolute inset-0 flex justify-center items-center origin-[calc(50%_-_800px)_calc(50%_+_200px)]\"\n          animate={{ rotate: 360 }}\n          transition={{ duration: 20, ease: 'linear', repeat: Infinity }}\n          style={{ transform: 'translateZ(0)', willChange: 'transform' }}\n        >\n          <div className=\"absolute rounded-full size-[160%] mix-blend-hard-light bg-[radial-gradient(circle_at_center,rgba(var(--fifth-color),0.8)_0%,rgba(var(--fifth-color),0)_50%)] top-[calc(50%-80%)] left-[calc(50%-80%)]\" />\n        </motion.div>\n\n        {interactive && (\n          <motion.div\n            className=\"absolute rounded-full size-full mix-blend-hard-light bg-[radial-gradient(circle_at_center,rgba(var(--sixth-color),0.8)_0%,rgba(var(--sixth-color),0)_50%)] opacity-70\"\n            style={{\n              x: springX,\n              y: springY,\n              transform: 'translateZ(0)',\n              willChange: 'transform',\n            }}\n          />\n        )}\n      </div>\n\n      {children}\n    </div>\n  );\n}\n\nexport { BubbleBackground, type BubbleBackgroundProps };\n",
      "type": "registry:ui",
      "target": "components/animate-ui/components/backgrounds/bubble.tsx"
    }
  ],
  "type": "registry:ui"
}