{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "border-beam",
  "title": "Border Beam",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-svelte/components/border-beam/BorderBeam.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface BorderBeamProps extends HTMLAttributes<HTMLSpanElement> {\n    /** Ring thickness in px. */\n    size?: number\n    /** Seconds per revolution. */\n    duration?: number\n    /** Seconds; negative values offset the beam's starting position around the ring. */\n    delay?: number\n    /** Any CSS color for the beam highlight. */\n    color?: string\n    /** Freeze the beam in place (animation-play-state: paused). */\n    paused?: boolean\n    /** The overlay <span>, via `bind:ref`. */\n    ref?: HTMLSpanElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let {\n    class: className,\n    size = 2,\n    duration = 6,\n    delay = 0,\n    color = 'var(--primary)',\n    paused = false,\n    children,\n    ref = $bindable(null),\n    ...restProps\n  }: BorderBeamProps = $props()\n\n  // Destructured (not rendered): the beam is a pure overlay with no slot —\n  // this keeps a stray `children` prop out of the span's spread attributes.\n  void children\n\n  // Honor prefers-reduced-motion by rendering a static beam at a fixed angle.\n  let reducedMotion = $state(false)\n\n  $effect(() => {\n    const query = window.matchMedia('(prefers-reduced-motion: reduce)')\n    const onChange = (event: MediaQueryListEvent) => {\n      reducedMotion = event.matches\n    }\n    reducedMotion = query.matches\n    query.addEventListener('change', onChange)\n    return () => query.removeEventListener('change', onChange)\n  })\n\n  const beamStyle = $derived.by(() => {\n    const background = reducedMotion\n      ? `conic-gradient(from 45deg, transparent 0deg, transparent 290deg, ${color} 330deg, transparent 360deg)`\n      : `conic-gradient(from var(--uipkge-border-angle), transparent 0deg, transparent 290deg, ${color} 330deg, transparent 360deg)`\n    const animation = reducedMotion ? 'none' : `uipkge-border-beam ${duration}s linear infinite ${delay}s`\n    const playState = !reducedMotion && paused ? 'paused' : undefined\n    // -webkit fallbacks first so the standard properties below win where both exist.\n    return [\n      `padding: ${size}px`,\n      `background: ${background}`,\n      `animation: ${animation}`,\n      ...(playState ? [`animation-play-state: ${playState}`] : []),\n      '-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',\n      '-webkit-mask-composite: xor',\n      'mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',\n      'mask-composite: exclude',\n    ].join('; ')\n  })\n</script>\n\n<span\n  bind:this={ref}\n  data-uipkge=\"\"\n  data-slot=\"border-beam\"\n  aria-hidden=\"true\"\n  class={cn('pointer-events-none absolute inset-0 rounded-[inherit]', className)}\n  style={beamStyle}\n  {...restProps}\n></span>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/border-beam/BorderBeam.svelte"
    },
    {
      "path": "packages/registry-svelte/components/border-beam/index.ts",
      "content": "export { default as BorderBeam, type BorderBeamProps } from './BorderBeam.svelte'\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/border-beam/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Rotating conic light beam that traces the border ring of its parent. Place inside a `relative` parent (e.g. a Card); the overlay is absolutely positioned, pointer-events-none, and masked down to a ring of `size` px thickness. Tune `duration`, `delay`, and `color`, or set `paused` to freeze it. Keyframes and the `--uipkge-border-angle` @property ship in the canonical tailwind tokens — re-pull init/tailwind if upgrading.",
  "categories": [
    "feedback",
    "motion"
  ]
}