{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "layered-button",
  "type": "registry:ui",
  "title": "Layered Button",
  "description": "A layered button with a radial expand effect and smooth sliding hover label animation.",
  "author": "Ahdeetai <https://aditya.is-cool.dev>",
  "registryDependencies": [],
  "dependencies": [
    "@radix-ui/react-slot",
    "class-variance-authority",
    "clsx",
    "tailwind-merge"
  ],
  "files": [
    {
      "type": "registry:ui",
      "path": "components/ui/layered-button.tsx",
      "content": "'use client';\nimport React, { useEffect, useRef } from 'react';\nimport { Slot } from '@radix-ui/react-slot';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\n\nconst buttonVariants = cva(\n  'relative inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 overflow-hidden box-border [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',\n  {\n    variants: {\n      variant: {\n        default: 'bg-primary text-primary-foreground shadow-sm',\n        outline: 'border border-input bg-transparent shadow-xs',\n      },\n      size: {\n        default: 'h-9 px-4 py-2',\n        sm: 'h-8 px-3 text-xs',\n        lg: 'h-10 px-8',\n        icon: 'h-9 w-9',\n      },\n    },\n    defaultVariants: {\n      variant: 'default',\n      size: 'default',\n    },\n  },\n);\n\nexport interface LayeredButtonProps\n  extends\n    React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof buttonVariants> {\n  borderWidth?: number;\n  asChild?: boolean;\n}\n\nconst LayeredButton = React.forwardRef<HTMLButtonElement, LayeredButtonProps>(\n  (\n    {\n      className,\n      variant,\n      size,\n      children,\n      borderWidth = 3,\n      asChild = false,\n      ...props\n    },\n    ref,\n  ) => {\n    const internalRef = useRef<HTMLButtonElement>(null);\n    const circleRef = useRef<HTMLSpanElement>(null);\n    const labelRef = useRef<HTMLSpanElement>(null);\n    const hoverLabelRef = useRef<HTMLSpanElement>(null);\n\n    React.useImperativeHandle(ref, () => internalRef.current!);\n\n    useEffect(() => {\n      const button = internalRef.current;\n      const circle = circleRef.current;\n      const label = labelRef.current;\n      const hoverLabel = hoverLabelRef.current;\n\n      if (!button || !circle) return;\n\n      const layout = () => {\n        const rect = button.getBoundingClientRect();\n        const { width: w, height: h } = rect;\n\n        const R = ((w * w) / 4 + h * h) / (2 * h);\n        const D = Math.ceil(2 * R) + 2;\n        const delta =\n          Math.ceil(R - Math.sqrt(Math.max(0, R * R - (w * w) / 4))) + 1;\n        const originY = D - delta;\n\n        circle.style.width = `${D}px`;\n        circle.style.height = `${D}px`;\n        circle.style.bottom = `-${delta}px`;\n        circle.style.left = '50%';\n        circle.style.transform = 'translateX(-50%) scale(0)';\n        circle.style.transformOrigin = `50% ${originY}px`;\n\n        if (label) {\n          label.style.transform = 'translateY(0)';\n        }\n        if (hoverLabel) {\n          hoverLabel.style.transform = `translateY(${h + 12}px)`;\n          hoverLabel.style.opacity = '0';\n        }\n\n        circle.dataset.scale = '1.2';\n        if (label) label.dataset.moveY = (-(h + 8)).toString();\n        if (hoverLabel) {\n          hoverLabel.dataset.startY = Math.ceil(h + 100).toString();\n        }\n      };\n\n      layout();\n\n      const onResize = () => layout();\n      window.addEventListener('resize', onResize);\n\n      return () => {\n        window.removeEventListener('resize', onResize);\n      };\n    }, []);\n\n    const handleMouseEnter = () => {\n      const circle = circleRef.current;\n      const label = labelRef.current;\n      const hoverLabel = hoverLabelRef.current;\n\n      if (circle) {\n        circle.style.transition =\n          'transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1)';\n        circle.style.transform = `translateX(-50%) scale(${\n          circle.dataset.scale || 1.2\n        })`;\n      }\n\n      if (label) {\n        label.style.transition =\n          'transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1)';\n        label.style.transform = `translateY(${label.dataset.moveY || -50}px)`;\n      }\n\n      if (hoverLabel) {\n        hoverLabel.style.transition =\n          'transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), opacity 0.3s cubic-bezier(0.25, 0.1, 0.25, 1)';\n        hoverLabel.style.transform = 'translateY(0)';\n        hoverLabel.style.opacity = '1';\n      }\n    };\n\n    const handleMouseLeave = () => {\n      const circle = circleRef.current;\n      const label = labelRef.current;\n      const hoverLabel = hoverLabelRef.current;\n\n      if (circle) {\n        circle.style.transition =\n          'transform 0.2s cubic-bezier(0.25, 0.1, 0.25, 1)';\n        circle.style.transform = 'translateX(-50%) scale(0)';\n      }\n\n      if (label) {\n        label.style.transition =\n          'transform 0.2s cubic-bezier(0.25, 0.1, 0.25, 1)';\n        label.style.transform = 'translateY(0)';\n      }\n\n      if (hoverLabel) {\n        hoverLabel.style.transition =\n          'transform 0.2s cubic-bezier(0.25, 0.1, 0.25, 1), opacity 0.2s cubic-bezier(0.25, 0.1, 0.25, 1)';\n        hoverLabel.style.transform = `translateY(${\n          hoverLabel.dataset.startY || 100\n        }px)`;\n        hoverLabel.style.opacity = '0';\n      }\n    };\n\n    const Comp = asChild ? Slot : 'button';\n\n    return (\n      <Comp\n        ref={internalRef}\n        className={cn(\n          buttonVariants({ variant, size }),\n          'rounded-md p-0!',\n          className,\n        )}\n        onMouseEnter={handleMouseEnter}\n        onMouseLeave={handleMouseLeave}\n        {...props}\n      >\n        <span\n          className='absolute inset-0 rounded-[inherit] pointer-events-none'\n          style={{\n            padding: borderWidth,\n          }}\n        >\n          <span className='block w-full h-full rounded-[inherit] bg-background' />\n        </span>\n\n        <span\n          ref={circleRef}\n          className='absolute rounded-full pointer-events-none will-change-transform bg-primary'\n        />\n\n        <span className='relative inline-flex items-center justify-center gap-2 z-2 px-4 py-2'>\n          <span\n            ref={labelRef}\n            className='inline-flex items-center justify-center gap-2 relative z-2 text-foreground'\n            style={{\n              willChange: 'transform',\n            }}\n          >\n            {children}\n          </span>\n          <span\n            ref={hoverLabelRef}\n            className='inline-flex items-center justify-center gap-2 absolute left-0 top-0 z-3 whitespace-nowrap text-primary-foreground px-4 py-2'\n            style={{\n              willChange: 'transform, opacity',\n            }}\n          >\n            {children}\n          </span>\n        </span>\n      </Comp>\n    );\n  },\n);\n\nLayeredButton.displayName = 'LayeredButton';\n\nexport { LayeredButton, buttonVariants };\n"
    },
    {
      "type": "registry:lib",
      "path": "lib/utils.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}"
    }
  ]
}
