{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "magic-button",
  "title": "Magic Button",
  "description": "A tactile 3D push button with an animated rainbow edge and shadow.",
  "registryDependencies": ["@godui/godui-theme"],
  "files": [
    {
      "path": "packages/components/src/magic-button/magic-button.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nexport type MagicButtonVariant = \"default\" | \"secondary\";\nexport type MagicButtonSize = \"sm\" | \"md\" | \"lg\";\n\nexport type MagicButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {\n  variant?: MagicButtonVariant;\n  size?: MagicButtonSize;\n  /** Animate the 3D edge and shadow with a flowing rainbow gradient */\n  rainbow?: boolean;\n};\n\n// 3D push button: a static shadow + edge sit behind a front face that lifts on\n// hover and dips on press. `group-*` variants drive the layered motion from the\n// button's interaction state; variant/rainbow colors are resolved per-render.\nconst BUTTON_CLASS =\n  \"group relative cursor-pointer select-none border-none bg-transparent p-0 font-medium [outline-offset:4px] [-webkit-tap-highlight-color:transparent] [transition:filter_600ms] hover:brightness-110 hover:[transition:filter_250ms] focus:outline-none focus-visible:brightness-110 focus-visible:[transition:filter_250ms] disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 disabled:filter-none\";\n\nconst SHADOW_BASE =\n  \"absolute inset-0 rounded-xl translate-y-[2px] [will-change:translate] [transition:translate_600ms_cubic-bezier(0.3,0.7,0.4,1)] group-hover:translate-y-[4px] group-hover:[transition:translate_250ms_cubic-bezier(0.3,0.7,0.4,1.5)] group-focus-visible:translate-y-[4px] group-focus-visible:[transition:translate_250ms_cubic-bezier(0.3,0.7,0.4,1.5)] group-active:translate-y-[1px] group-active:[transition:translate_34ms] group-data-[pressed=true]:translate-y-[1px] group-data-[pressed=true]:[transition:translate_34ms] group-disabled:hidden\";\n\nconst EDGE_BASE = \"absolute inset-0 rounded-xl group-disabled:hidden\";\n\nconst FRONT_BASE =\n  \"relative block rounded-xl [will-change:translate] -translate-y-[4px] [transition:translate_600ms_cubic-bezier(0.3,0.7,0.4,1)] group-hover:-translate-y-[6px] group-hover:[transition:translate_250ms_cubic-bezier(0.3,0.7,0.4,1.5)] group-focus-visible:-translate-y-[6px] group-focus-visible:[transition:translate_250ms_cubic-bezier(0.3,0.7,0.4,1.5)] group-active:-translate-y-[2px] group-active:[transition:translate_34ms] group-data-[pressed=true]:-translate-y-[2px] group-data-[pressed=true]:[transition:translate_34ms] group-disabled:translate-y-0 group-disabled:[transition:none]\";\n\nconst RAINBOW_FILL =\n  \"[background-image:linear-gradient(90deg,var(--rainbow-1),var(--rainbow-5),var(--rainbow-3),var(--rainbow-4),var(--rainbow-2))] [background-size:200%_100%] animate-magic-rainbow motion-reduce:animate-none\";\n\nconst SOLID_SHADOW = \"bg-[hsl(0deg_0%_0%_/_0.25)] blur-[4px]\";\nconst RAINBOW_SHADOW = `${RAINBOW_FILL} blur-[12px] opacity-70`;\n\nconst edgeBg: Record<MagicButtonVariant, string> = {\n  default:\n    \"[background:linear-gradient(to_left,color-mix(in_srgb,var(--primary)_50%,black)_0%,color-mix(in_srgb,var(--primary)_75%,black)_8%,color-mix(in_srgb,var(--primary)_75%,black)_92%,color-mix(in_srgb,var(--primary)_50%,black)_100%)]\",\n  secondary:\n    \"[background:linear-gradient(to_left,color-mix(in_srgb,var(--secondary)_50%,black)_0%,color-mix(in_srgb,var(--secondary)_75%,black)_8%,color-mix(in_srgb,var(--secondary)_75%,black)_92%,color-mix(in_srgb,var(--secondary)_50%,black)_100%)]\",\n};\n\nconst frontVariant: Record<MagicButtonVariant, string> = {\n  default: \"bg-primary text-primary-foreground\",\n  secondary: \"bg-secondary text-secondary-foreground\",\n};\n\nconst frontSize: Record<MagicButtonSize, string> = {\n  sm: \"px-[var(--button-px-sm)] py-[var(--button-py-sm)] text-[length:var(--button-text-sm)] leading-[var(--button-leading-sm)]\",\n  md: \"px-[var(--button-px-md)] py-[var(--button-py-md)] text-[length:var(--button-text-md)] leading-[var(--button-leading-md)]\",\n  lg: \"px-[var(--button-px-lg)] py-[var(--button-py-lg)] text-[length:var(--button-text-lg)] leading-[var(--button-leading-lg)]\",\n};\n\nconst MagicButton = React.forwardRef<HTMLButtonElement, MagicButtonProps>(\n  (\n    {\n      className,\n      variant = \"default\",\n      size = \"md\",\n      rainbow = true,\n      children,\n      onKeyDown,\n      onKeyUp,\n      ...props\n    },\n    ref,\n  ) => {\n    const [pressed, setPressed] = React.useState(false);\n\n    // The rainbow edge/shadow run an infinite background-position keyframe\n    // (main-thread paint). Pause it when the button is off screen so it costs\n    // nothing while idle — resumes seamlessly on scroll-in.\n    const rootRef = React.useRef<HTMLButtonElement>(null);\n    const setRefs = React.useCallback(\n      (node: HTMLButtonElement | null) => {\n        rootRef.current = node;\n        if (typeof ref === \"function\") ref(node);\n        else if (ref)\n          (ref as React.MutableRefObject<HTMLButtonElement | null>).current =\n            node;\n      },\n      [ref],\n    );\n    React.useEffect(() => {\n      const root = rootRef.current;\n      if (!rainbow || !root) return;\n      const view = root.ownerDocument.defaultView ?? window;\n      if (typeof view.IntersectionObserver === \"undefined\") return;\n      const io = new view.IntersectionObserver(\n        ([entry]) => {\n          for (const layer of root.querySelectorAll<HTMLElement>(\n            \".animate-magic-rainbow\",\n          ))\n            layer.style.animationPlayState = entry.isIntersecting\n              ? \"\"\n              : \"paused\";\n        },\n        { rootMargin: \"128px\" },\n      );\n      io.observe(root);\n      return () => io.disconnect();\n    }, [rainbow]);\n\n    const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n      if (event.key === \"Enter\" || event.key === \" \") {\n        setPressed(true);\n      }\n      onKeyDown?.(event);\n    };\n\n    const handleKeyUp = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n      if (event.key === \"Enter\" || event.key === \" \") {\n        setPressed(false);\n      }\n      onKeyUp?.(event);\n    };\n\n    return (\n      <button\n        ref={setRefs}\n        data-variant={variant}\n        data-rainbow={rainbow ? \"true\" : undefined}\n        data-pressed={pressed ? \"true\" : undefined}\n        onKeyDown={handleKeyDown}\n        onKeyUp={handleKeyUp}\n        className={`${BUTTON_CLASS} ${className ?? \"\"}`}\n        {...props}\n      >\n        <span\n          className={`${SHADOW_BASE} ${rainbow ? RAINBOW_SHADOW : SOLID_SHADOW}`}\n          aria-hidden=\"true\"\n        />\n        <span\n          className={`${EDGE_BASE} ${rainbow ? RAINBOW_FILL : edgeBg[variant]}`}\n          aria-hidden=\"true\"\n        />\n        <span\n          data-size={size}\n          className={`${FRONT_BASE} ${frontVariant[variant]} ${frontSize[size]}`}\n        >\n          {children}\n        </span>\n      </button>\n    );\n  },\n);\nMagicButton.displayName = \"MagicButton\";\n\nexport { MagicButton };\n",
      "type": "registry:ui",
      "target": "components/godui/magic-button.tsx"
    }
  ],
  "cssVars": {
    "theme": {
      "animate-magic-rainbow": "magic-rainbow var(--rainbow-speed, 2s) infinite linear"
    }
  },
  "css": {
    "@keyframes magic-rainbow": {
      "0%": {
        "background-position": "0% 50%"
      },
      "100%": {
        "background-position": "200% 50%"
      }
    }
  },
  "type": "registry:ui"
}
