{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pulsating-button",
  "type": "registry:ui",
  "title": "Pulsating Button",
  "description": "An animated pulsating button useful for capturing attention of users.",
  "files": [
    {
      "path": "registry/magicui/pulsating-button.tsx",
      "content": "\"use client\"\n\nimport React, { useImperativeHandle, useLayoutEffect, useRef } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface PulsatingButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n  pulseColor?: string\n  duration?: string\n  distance?: string\n  variant?: \"pulse\" | \"ripple\"\n}\n\nexport const PulsatingButton = React.forwardRef<\n  HTMLButtonElement,\n  PulsatingButtonProps\n>(\n  (\n    {\n      className,\n      children,\n      pulseColor,\n      duration = \"1.5s\",\n      distance = \"8px\",\n      variant = \"pulse\",\n      ...props\n    },\n    ref\n  ) => {\n    const innerRef = useRef<HTMLButtonElement>(null)\n    useImperativeHandle(ref, () => innerRef.current!)\n\n    useLayoutEffect(() => {\n      const button = innerRef.current\n      if (!button) return\n\n      if (pulseColor) {\n        button.style.removeProperty(\"--bg\")\n        return\n      }\n\n      let animationFrameId = 0\n      let currentBg = \"\"\n\n      const updateBg = () => {\n        animationFrameId = 0\n\n        const nextBg = getComputedStyle(button).backgroundColor\n        if (nextBg === currentBg) return\n\n        currentBg = nextBg\n        button.style.setProperty(\"--bg\", nextBg)\n      }\n\n      const scheduleBgUpdate = () => {\n        if (animationFrameId) return\n        animationFrameId = window.requestAnimationFrame(updateBg)\n      }\n\n      updateBg()\n\n      const themeObserver = new MutationObserver(scheduleBgUpdate)\n      themeObserver.observe(document.documentElement, {\n        attributes: true,\n        attributeFilter: [\"class\"],\n      })\n\n      const buttonObserver = new MutationObserver(scheduleBgUpdate)\n      buttonObserver.observe(button, {\n        attributes: true,\n      })\n\n      const syncEvents = [\n        \"blur\",\n        \"focus\",\n        \"pointerenter\",\n        \"pointerleave\",\n      ] as const\n\n      for (const eventName of syncEvents) {\n        button.addEventListener(eventName, scheduleBgUpdate)\n      }\n\n      return () => {\n        if (animationFrameId) {\n          window.cancelAnimationFrame(animationFrameId)\n        }\n\n        themeObserver.disconnect()\n        buttonObserver.disconnect()\n\n        for (const eventName of syncEvents) {\n          button.removeEventListener(eventName, scheduleBgUpdate)\n        }\n      }\n    }, [pulseColor])\n\n    return (\n      <button\n        ref={innerRef}\n        className={cn(\n          \"bg-primary text-primary-foreground relative flex cursor-pointer items-center justify-center rounded-lg px-4 py-2 text-center\",\n          className\n        )}\n        style={\n          {\n            ...(pulseColor && { \"--pulse-color\": pulseColor }),\n            \"--duration\": duration,\n            \"--distance\": distance,\n          } as React.CSSProperties\n        }\n        {...props}\n      >\n        <span className=\"relative z-10\">{children}</span>\n        <span\n          aria-hidden=\"true\"\n          className={cn(\n            \"pointer-events-none absolute inset-0 rounded-[inherit] bg-inherit\",\n            variant === \"pulse\" ? \"animate-pulse\" : \"animate-pulse-ripple\"\n          )}\n        />\n      </button>\n    )\n  }\n)\n\nPulsatingButton.displayName = \"PulsatingButton\"\n",
      "type": "registry:ui"
    }
  ],
  "cssVars": {
    "theme": {
      "animate-pulse": "pulse var(--duration) ease-out infinite",
      "animate-pulse-ripple": "pulse-ripple var(--duration) cubic-bezier(0.16, 1, 0.3, 1) infinite"
    }
  },
  "css": {
    "@keyframes pulse": {
      "0%, 100%": {
        "boxShadow": "0 0 0 0 var(--pulse-color, oklch(from var(--bg) l c h / 0.5))"
      },
      "50%": {
        "boxShadow": "0 0 0 var(--distance) var(--pulse-color, oklch(from var(--bg) l c h / 0.5))"
      }
    },
    "@keyframes pulse-ripple": {
      "0%": {
        "boxShadow": "0 0 0 0 oklch(from var(--pulse-color, var(--bg)) l c h / 1)"
      },
      "100%": {
        "boxShadow": "0 0 0 var(--distance) oklch(from var(--pulse-color, var(--bg)) l c h / 0)"
      }
    }
  }
}