{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "border-beam",
  "title": "Border Beam",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/border-beam/BorderBeam.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport type { CSSProperties, HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\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  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  size: 2,\n  duration: 6,\n  delay: 0,\n  color: 'var(--primary)',\n  paused: false,\n})\n\n// Honor prefers-reduced-motion by rendering a static beam at a fixed angle.\nconst reducedMotion = ref(false)\nlet query: MediaQueryList | null = null\n\nfunction onChange(event: MediaQueryListEvent) {\n  reducedMotion.value = event.matches\n}\n\nonMounted(() => {\n  query = window.matchMedia('(prefers-reduced-motion: reduce)')\n  reducedMotion.value = query.matches\n  query.addEventListener('change', onChange)\n})\n\nonBeforeUnmount(() => {\n  query?.removeEventListener('change', onChange)\n})\n\nconst beamStyles = computed<CSSProperties[]>(() => [\n  {\n    padding: `${props.size}px`,\n    background: reducedMotion.value\n      ? `conic-gradient(from 45deg, transparent 0deg, transparent 290deg, ${props.color} 330deg, transparent 360deg)`\n      : `conic-gradient(from var(--uipkge-border-angle), transparent 0deg, transparent 290deg, ${props.color} 330deg, transparent 360deg)`,\n    animation: reducedMotion.value ? 'none' : `uipkge-border-beam ${props.duration}s linear infinite ${props.delay}s`,\n    animationPlayState: !reducedMotion.value && props.paused ? 'paused' : undefined,\n  },\n  {\n    // -webkit fallbacks first so the standard properties below win where both exist.\n    WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',\n    WebkitMaskComposite: 'xor',\n    mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',\n    maskComposite: 'exclude',\n  },\n])\n</script>\n\n<template>\n  <span\n    data-uipkge\n    data-slot=\"border-beam\"\n    aria-hidden=\"true\"\n    :class=\"cn('pointer-events-none absolute inset-0 rounded-[inherit]', props.class)\"\n    :style=\"beamStyles\"\n  />\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/border-beam/BorderBeam.vue"
    },
    {
      "path": "packages/registry-vue/components/border-beam/index.ts",
      "content": "export { default as BorderBeam } from './BorderBeam.vue'\n",
      "type": "registry:ui",
      "target": "~/app/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"
  ]
}