{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "buttons-border-gradient",
  "type": "registry:component",
  "title": "Border Gradient Button",
  "description": "Button with animated gradient border",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/new-york/animations/buttons/border-gradient.tsx",
      "content": "\"use client\";\nimport * as React from \"react\";\nimport { motion, HTMLMotionProps } from \"motion/react\";\ninterface BorderGradientButtonProps\n  extends Omit<HTMLMotionProps<\"button\">, \"children\"> {\n  children: React.ReactNode;\n  duration?: number;\n  colors?: string[];\n}\nfunction cn(...classes: (string | undefined)[]) {\n  return classes.filter(Boolean).join(\" \");\n}\nexport function BorderGradientButton({\n  children,\n  className,\n  duration = 2,\n  colors = [\"#3b82f6\", \"#8b5cf6\", \"#ec4899\"],\n  ...props\n}: BorderGradientButtonProps) {\n  const buttonRef = React.useRef<HTMLButtonElement>(null);\n  const [dimensions, setDimensions] = React.useState({\n    width: 0,\n    height: 0,\n    radius: 0,\n    perimeter: 0,\n  });\n  const gradientId = React.useId();\n  React.useEffect(() => {\n    if (!buttonRef.current) return;\n    const updateDimensions = () => {\n      if (buttonRef.current) {\n        const styles = window.getComputedStyle(buttonRef.current);\n        const width = buttonRef.current.offsetWidth;\n        const height = buttonRef.current.offsetHeight;\n        const borderRadius =\n          parseFloat(styles.borderTopLeftRadius) ||\n          parseFloat(styles.borderRadius) ||\n          0;\n        const maxRadius = Math.min(width, height) / 2;\n        const actualRadius = Math.min(borderRadius, maxRadius);\n        const straightEdges =\n          2 * (width - 2 * actualRadius + (height - 2 * actualRadius));\n        const curvedEdges = 2 * Math.PI * actualRadius;\n        const perimeter = straightEdges + curvedEdges;\n        setDimensions({\n          width,\n          height,\n          radius: actualRadius,\n          perimeter: perimeter || 1,\n        });\n      }\n    };\n    updateDimensions();\n    const observer = new ResizeObserver(updateDimensions);\n    if (buttonRef.current) {\n      observer.observe(buttonRef.current);\n    }\n    return () => observer.disconnect();\n  }, []);\n  return (\n    <motion.button\n      ref={buttonRef}\n      className={cn(\"relative\", className)}\n      {...props}\n    >\n      {}\n      {dimensions.width > 0 && (\n        <svg\n          className=\"absolute inset-0 pointer-events-none\"\n          width={dimensions.width}\n          height={dimensions.height}\n        >\n          <defs>\n            <linearGradient id={gradientId} x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n              {colors.map((color, i) => (\n                <stop\n                  key={i}\n                  offset={`${(i / (colors.length - 1)) * 100}%`}\n                  stopColor={color}\n                />\n              ))}\n            </linearGradient>\n          </defs>\n          <motion.rect\n            x=\"1\"\n            y=\"1\"\n            width={dimensions.width - 2}\n            height={dimensions.height - 2}\n            rx={dimensions.radius}\n            fill=\"none\"\n            stroke={`url(#${gradientId})`}\n            strokeWidth=\"2\"\n            strokeDasharray={dimensions.perimeter}\n            strokeDashoffset={0}\n            animate={{ strokeDashoffset: [0, -dimensions.perimeter] }}\n            transition={{\n              duration,\n              ease: \"linear\",\n              repeat: Infinity,\n            }}\n            style={{\n              strokeLinecap: \"round\",\n            }}\n          />\n        </svg>\n      )}\n      <span className=\"relative z-10\">{children}</span>\n    </motion.button>\n  );\n}\n",
      "type": "registry:component"
    }
  ]
}