{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bubble-up-button",
  "type": "registry:ui",
  "title": "BubbleUpButton",
  "description": "The `BubbleUpButton` component provides an interactive button with a fluid \"bubble-up\" animation effect when hovered. It creates an engaging user experience with minimal effort.",
  "files": [
    {
      "path": "registry/sonaui/bubble-up-button/bubble-up-button.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport {\n  type MotionConfigProps,\n  motion,\n  useAnimation,\n  useReducedMotion,\n} from \"motion/react\";\nimport { type ReactNode, useRef } from \"react\";\nimport { cn } from \"@/lib/sona-utils\";\n\ninterface BubbleUpButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n  /** Content to display inside the button */\n  children?: ReactNode;\n  /** Motion configuration for animations */\n  motionControls?: MotionConfigProps;\n  /** Additional CSS classes */\n  className?: string;\n}\nexport default function BubbleUpButton({\n  children = \"Hover me!\",\n  motionControls = {\n    transition: { type: \"spring\", stiffness: 300, damping: 32 },\n  },\n  className = \"\",\n  disabled = false,\n  ...props\n}: BubbleUpButtonProps) {\n  const controls = useAnimation();\n  const shouldReduceMotion = useReducedMotion();\n  // Tracks the latest intent so a re-enter during the exit animation\n  // doesn't get clobbered by the post-exit reset.\n  const hoverIntent = useRef(false);\n\n  const fill = async () => {\n    hoverIntent.current = true;\n    await controls.start({\n      clipPath: \"ellipse(120% 120% at 50% 100%)\",\n      transition: shouldReduceMotion ? { duration: 0 } : undefined,\n    });\n  };\n\n  const drain = async () => {\n    hoverIntent.current = false;\n    await controls.start({\n      clipPath: \"ellipse(120% 120% at 50% -120%)\",\n      transition: shouldReduceMotion ? { duration: 0 } : undefined,\n    });\n    if (!hoverIntent.current) {\n      controls.set({ clipPath: \"ellipse(0% 0% at 50% 100%)\" });\n    }\n  };\n\n  return (\n    <button\n      onMouseEnter={fill}\n      onMouseLeave={drain}\n      onFocus={fill}\n      onBlur={drain}\n      disabled={disabled}\n      className={cn(\n        \"relative isolate flex h-fit w-fit cursor-pointer overflow-clip rounded-2xl bg-background text-foreground px-16 py-2\",\n        \"transition-opacity duration-200\",\n        \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background focus:outline-none\",\n        disabled && \"cursor-not-allowed opacity-50\",\n        className,\n      )}\n      {...props}\n    >\n      <motion.div\n        animate={controls}\n        initial={{ clipPath: \"ellipse(0% 0% at 50% 100%)\" }}\n        transition={motionControls.transition}\n        className=\"absolute left-0 top-0 h-full w-full bg-foreground\"\n        aria-hidden=\"true\"\n      />\n      <span className=\"relative text-white mix-blend-difference\">\n        {children}\n      </span>\n    </button>\n  );\n}\n",
      "target": "components/ui/bubble-up-button/bubble-up-button.tsx"
    }
  ],
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "@sona-ui/sona-utils"
  ]
}