{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "border-beam",
  "type": "registry:ui",
  "title": "Border Beam",
  "description": "A single gradient square that travels around the inside edge of its parent, drawn with `offset-path` and a two-layer CSS mask so only the border ring shows. Drop it inside any `relative`, rounded container to give that container a moving outline.",
  "author": "ofri-peretz <https://github.com/ofri-peretz>",
  "categories": [
    "decorative",
    "effect"
  ],
  "dependencies": [],
  "registryDependencies": [
    "https://ds.interlace.tools/r/theme.json",
    "https://ds.interlace.tools/r/cn.json"
  ],
  "files": [
    {
      "path": "registry/interlace-ui/magicui/border-beam.tsx",
      "target": "components/ui/magicui/border-beam.tsx",
      "type": "registry:ui",
      "content": "\"use client\"\n\nimport { cn } from \"@/lib/utils\"\n\n\n\n// @interlace/border-beam v1.1.0 — Interlace design system.\n// Docs, props and live preview: https://ds.interlace.tools/c/border-beam\n// What changed since: https://ds.interlace.tools/c/border-beam#history\n// Generated banner — keep it, the upgrade diff reads this version.\n\n/**\n * @interlace/ui — BorderBeam\n *\n * A single gradient square that travels around the inside edge of its parent,\n * drawn with `offset-path` and a two-layer CSS mask so only the border ring\n * shows. Drop it inside any `relative`, rounded container to give that\n * container a moving outline.\n *\n * It positions itself `absolute inset-0` and inherits the parent's radius, so\n * the only thing the parent has to supply is a stacking context.\n *\n * Our reimplementation of the Magic UI component of the same name, rebuilt on\n * CSS `offset-path` instead of Framer Motion — the whole effect is one\n * compositor-driven property and this file imports no animation library.\n *\n * ## Anatomy\n *\n *   div                              (absolute inset-0, rounded-[inherit],\n *                                     transparent border + mask-intersect —\n *                                     this is what clips the beam to the ring)\n *     └─ div.animate-border-beam     (the beam: an aspect-square gradient\n *                                     riding `offset-path: rect(… round Npx)`)\n *\n * ## Motion\n *\n * Pure CSS, and covered twice: `.animate-border-beam` is named in the\n * `prefers-reduced-motion: reduce` block in `styles/tokens.css`\n * (`animation: none !important`) and also caught by the `animation-duration`\n * wildcard in `styles/preflight.css`. There is no `useReducedMotion` call and\n * none is needed. Note what `reduce` leaves behind: with the animation off,\n * `offset-distance` sits at its initial `0%`, so the beam parks as a static\n * gradient square at the start of the path rather than disappearing.\n *\n * ## Colour, and why `--chart-2` is allowed here\n *\n * `colorFrom` / `colorTo` default to `var(--primary)` → `var(--chart-2)`, the\n * brand orange-to-green sweep. They were the raw literals `#ffaa40` / `#9c40ff`\n * (R19), which also meant the beam did not re-resolve per theme.\n *\n * `--chart-2` is a 3:1-class token, not a 4.5:1 one, and that is the right\n * choice HERE specifically: the beam is a decorative overlay inside a container\n * that draws its own `border`, so it is neither text (SC 1.4.3) nor the\n * boundary that identifies a control (SC 1.4.11) — remove it entirely and\n * nothing becomes unusable. Contrast `magicui/animated-gradient-text.tsx`,\n * where the gradient IS the glyph fill and both stops are consequently pinned\n * to text-grade tokens.\n *\n * ## Accessibility\n *\n * The wrapper carries `aria-hidden=\"true\"`, which takes the beam with it — a\n * subtree hidden at the root is hidden entire. Both divs are empty, roleless\n * and `pointer-events-none`; there is nothing here for a screen reader to\n * announce, and before this the reader walked two anonymous group nodes\n * inside every card that used the effect. `aria-hidden` sits on the wrapper\n * ONLY: putting it on the beam as well would be redundant, and putting it\n * anywhere a consumer's `className` could reach would let a caller\n * accidentally hide real content.\n *\n * ## One API edge worth knowing\n *\n * `className` lands on the BEAM element, not the wrapper. Use it to restyle\n * the travelling gradient; you cannot reach the masked ring from outside.\n */\n\ninterface BorderBeamProps {\n  /**\n   * The size of the border beam.\n   */\n  size?: number\n  /**\n   * The duration of the border beam.\n   */\n  duration?: number\n  /**\n   * The delay of the border beam.\n   */\n  delay?: number\n  /**\n   * Leading colour of the travelling gradient.\n   * @default \"var(--primary)\"\n   */\n  colorFrom?: string\n  /**\n   * Trailing colour of the travelling gradient, before it fades to transparent.\n   * @default \"var(--chart-2)\"\n   */\n  colorTo?: string\n  /**\n   * The class name of the border beam.\n   */\n  className?: string\n  /**\n   * The style of the border beam.\n   */\n  style?: React.CSSProperties\n  /**\n   * Whether to reverse the animation direction.\n   */\n  reverse?: boolean\n  /**\n   * The initial offset position (0-100).\n   */\n  initialOffset?: number\n  /**\n   * The border width of the beam.\n   */\n  borderWidth?: number\n}\n\n/**\n * BorderBeam Component - Performance Optimized\n * \n * Converted from Framer Motion to pure CSS animation for better GPU acceleration.\n * Uses CSS offset-path animation which is hardware-accelerated.\n */\nexport const BorderBeam = ({\n  className,\n  size = 50,\n  delay = 0,\n  duration = 6,\n  colorFrom = \"var(--primary)\",\n  colorTo = \"var(--chart-2)\",\n  style,\n  reverse = false,\n  initialOffset = 0,\n  borderWidth = 1,\n}: BorderBeamProps) => {\n  return (\n    <div\n      aria-hidden=\"true\"\n      className=\"pointer-events-none absolute inset-0 rounded-[inherit] border-(length:--border-beam-width) border-transparent mask-[linear-gradient(transparent,transparent),linear-gradient(#000,#000)] mask-intersect [mask-clip:padding-box,border-box]\"\n      style={\n        {\n          \"--border-beam-width\": `${borderWidth}px`,\n        } as React.CSSProperties\n      }\n    >\n      {/* Performance: Using CSS animation instead of Framer Motion */}\n      <div\n        className={cn(\n          \"absolute aspect-square animate-border-beam\",\n          \"bg-linear-to-l from-(--color-from) via-(--color-to) to-transparent\",\n          className\n        )}\n        style={\n          {\n            width: size,\n            offsetPath: `rect(0 auto auto 0 round ${size}px)`,\n            \"--color-from\": colorFrom,\n            \"--color-to\": colorTo,\n            \"--border-beam-duration\": `${duration}s`,\n            \"--border-beam-delay\": `${-delay}s`,\n            \"--border-beam-initial\": `${initialOffset}%`,\n            \"--border-beam-direction\": reverse ? \"reverse\" : \"normal\",\n            animationDelay: `${-delay}s`,\n            ...style,\n          } as React.CSSProperties\n        }\n      />\n    </div>\n  )\n}\n"
    }
  ],
  "meta": {
    "tier": "effect",
    "client": true,
    "minViewport": null,
    "loading": false,
    "version": "1.1.0",
    "since": "1.0.0"
  },
  "docs": "## @interlace/border-beam\n\nInstalled to `components/ui/magicui/border-beam.tsx`.\n\n```tsx\nimport { /* … */ } from '@/components/ui/magicui/border-beam';\n```\n\nProps, a11y contract, live preview and source: https://ds.interlace.tools/c/border-beam\n\nRequires the `@interlace/theme` CSS baseline (installed automatically as a registry dependency)."
}
