{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "glow-border",
  "title": "Glow Border",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "Wraps any content in a border drawn by a rotating conic gradient in theme colors, with a soft outer glow. Speed, width, radius and arc length are props; it stops at a still angle under reduced motion.",
  "dependencies": [
    "motion@^12"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/glow-border.tsx",
      "content": "// Ballmac UI: Glow Border. https://ui.ballmac.com/components/glow-border\n\"use client\"\n\nimport * as React from \"react\"\nimport { useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype GlowBorderProps = React.ComponentProps<\"div\"> & {\n  /** Seconds for one full rotation of the gradient. */\n  duration?: number\n  /** Border thickness in pixels. */\n  width?: number\n  /** Outer corner radius in pixels; the content's radius is reduced by `width` so the ring stays even. */\n  radius?: number\n  /** Colors of the moving arc, as CSS colors or variables. The arc fades out before the first color repeats. */\n  colors?: string[]\n  /** How much of the ring the arc covers, 0 to 1. Use 1 for a continuous gradient ring. */\n  arc?: number\n  /** Strength of the soft outer glow, 0 to 1. */\n  glow?: number\n  /** Class names for the inner content surface. */\n  contentClassName?: string\n}\n\nconst ANGLE = \"--glow-border-angle\"\n\nlet registered = false\n/** Registers the angle as a typed property so browsers can interpolate it; returns false if unsupported. */\nfunction registerAngle() {\n  if (registered) return true\n  if (typeof CSS === \"undefined\" || !(\"registerProperty\" in CSS)) return false\n  try {\n    CSS.registerProperty({ name: ANGLE, syntax: \"<angle>\", inherits: true, initialValue: \"0deg\" })\n  } catch {\n    // Already registered (hot reload, a second copy of this file): still usable.\n  }\n  registered = true\n  return true\n}\n\nfunction gradientFor(colors: string[], arc: number) {\n  if (arc >= 1) return `conic-gradient(from var(${ANGLE}), ${[...colors, colors[0]].join(\", \")})`\n  const span = Math.max(0.05, arc) * 100\n  const step = span / (colors.length + 1)\n  const stops = colors.map((c, i) => `${c} ${(step * (i + 1)).toFixed(2)}%`)\n  return `conic-gradient(from var(${ANGLE}), transparent 0%, ${stops.join(\", \")}, transparent ${span.toFixed(2)}%, transparent 100%)`\n}\n\nfunction GlowBorder({\n  duration = 4,\n  width = 1.5,\n  radius = 14,\n  colors = [\"var(--chart-1)\", \"var(--chart-4)\", \"var(--chart-5)\", \"var(--chart-2)\"],\n  arc = 0.55,\n  glow = 0.5,\n  contentClassName,\n  className,\n  style,\n  children,\n  ...props\n}: GlowBorderProps) {\n  const rootRef = React.useRef<HTMLDivElement>(null)\n  const reduceMotion = useReducedMotion()\n\n  React.useEffect(() => {\n    const root = rootRef.current\n    if (!root || reduceMotion || !registerAngle() || typeof root.animate !== \"function\") return\n    const animation = root.animate([{ [ANGLE]: \"0deg\" }, { [ANGLE]: \"360deg\" }], {\n      duration: duration * 1000,\n      iterations: Infinity,\n      easing: \"linear\",\n    })\n    // Pause while off-screen; browsers already throttle hidden tabs.\n    const observer = new IntersectionObserver(([entry]) => {\n      if (entry?.isIntersecting) animation.play()\n      else animation.pause()\n    })\n    observer.observe(root)\n    return () => {\n      observer.disconnect()\n      animation.cancel()\n    }\n  }, [duration, reduceMotion])\n\n  const background = gradientFor(colors, arc)\n\n  return (\n    <div\n      ref={rootRef}\n      data-slot=\"glow-border\"\n      className={cn(\"relative isolate\", className)}\n      style={\n        {\n          borderRadius: radius,\n          padding: width,\n          // Static angle under reduced motion: the arc rests across the top-right corner.\n          [ANGLE]: \"40deg\",\n          ...style,\n        } as React.CSSProperties\n      }\n      {...props}\n    >\n      {/* Soft glow behind the ring. */}\n      {glow > 0 && (\n        <span\n          aria-hidden=\"true\"\n          data-slot=\"glow-border-glow\"\n          className=\"pointer-events-none absolute inset-0 -z-10 rounded-[inherit] blur-md\"\n          style={{ background, opacity: glow }}\n        />\n      )}\n      {/* The ring: the gradient shows only in the padding around the content. */}\n      <span\n        aria-hidden=\"true\"\n        data-slot=\"glow-border-ring\"\n        className=\"pointer-events-none absolute inset-0 -z-10 rounded-[inherit] bg-border\"\n      >\n        <span className=\"absolute inset-0 rounded-[inherit]\" style={{ background }} />\n      </span>\n      <div\n        data-slot=\"glow-border-content\"\n        className={cn(\"relative h-full rounded-[inherit] bg-card text-card-foreground\", contentClassName)}\n        style={{ borderRadius: Math.max(0, radius - width) }}\n      >\n        {children}\n      </div>\n    </div>\n  )\n}\n\nexport { GlowBorder, type GlowBorderProps }\n",
      "type": "registry:ui",
      "target": "@components/ballmac/glow-border.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "border",
      "gradient",
      "glow",
      "conic",
      "highlight",
      "pricing",
      "button",
      "motion"
    ],
    "version": "1.0.0",
    "updated": "2026-09-29",
    "ai": {
      "summary": "Put content inside <GlowBorder>; it renders a padded wrapper whose padding shows a rotating conic gradient and a content surface (bg-card, radius minus width). Use arc={1} for a full rainbow ring, a smaller arc for a comet.",
      "whenToUse": [
        "The recommended plan in a pricing table",
        "One primary call-to-action or an AI entry point",
        "Marking a card as new, live or selected"
      ],
      "whenNotToUse": [
        "Several elements at once on the same screen (use it for one focal point)",
        "A thin light that travels along an existing border (use border-beam)"
      ],
      "composesWith": [
        "button",
        "border-beam",
        "tilt-card"
      ],
      "a11y": [],
      "customization": [
        "duration: seconds per rotation (default 4); width: ring px (default 1.5); radius: px (default 14, 999 for pills)",
        "colors: CSS colors or var(--chart-n) values; arc: 0–1 share of the ring lit (default 0.55)",
        "glow: 0–1 outer glow strength (0 turns it off)",
        "contentClassName styles the inner surface (bg-background, bg-primary…)",
        "Animates a registered CSS property (--glow-border-angle); pauses off-screen"
      ]
    },
    "examples": [
      "glow-border-demo",
      "glow-border-button"
    ],
    "url": "https://ui.ballmac.com/components/glow-border"
  },
  "categories": [
    "motion"
  ],
  "type": "registry:ui"
}