{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "light-rays",
  "title": "Light Rays",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "Soft beams of theme-colored light fanning down from the top edge, each swaying and breathing at its own pace, deterministic so the server and browser match.",
  "dependencies": [
    "motion@^12"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/light-rays.tsx",
      "content": "// Ballmac UI: Light Rays. https://ui.ballmac.com/components/light-rays\n\"use client\"\n\nimport * as React from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype RayTone = \"foreground\" | \"primary\" | \"chart-1\" | \"chart-2\" | \"chart-3\" | \"chart-4\" | \"chart-5\"\n\nconst VAR: Record<RayTone, string> = {\n  foreground: \"var(--foreground)\",\n  primary: \"var(--primary)\",\n  \"chart-1\": \"var(--chart-1)\",\n  \"chart-2\": \"var(--chart-2)\",\n  \"chart-3\": \"var(--chart-3)\",\n  \"chart-4\": \"var(--chart-4)\",\n  \"chart-5\": \"var(--chart-5)\",\n}\n\ntype LightRaysProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n  /** Number of rays. */\n  count?: number\n  /** Color of the light. */\n  tone?: RayTone\n  /** How far the rays fan out, in degrees from one edge to the other. */\n  spread?: number\n  /** Brightness from 0 to 1. */\n  intensity?: number\n  /** Seconds for a ray to sway once. Longer is calmer. */\n  duration?: number\n}\n\n// Deterministic pseudo-randomness keeps the server and browser identical.\nconst noise = (i: number, salt: number) => {\n  const v = Math.sin(i * 127.1 + salt * 311.7) * 43758.5453\n  return v - Math.floor(v)\n}\n\n/** Soft beams of light fanning down from the top edge, swaying slowly. Decorative. */\nfunction LightRays({ count = 7, tone = \"chart-1\", spread = 70, intensity = 0.55, duration = 9, className, ...props }: LightRaysProps) {\n  const reduce = useReducedMotion()\n  const color = VAR[tone]\n  return (\n    <div data-slot=\"light-rays\" aria-hidden=\"true\" className={cn(\"pointer-events-none absolute inset-0 overflow-hidden\", className)} {...props}>\n      <span\n        className=\"absolute inset-x-0 top-0 h-1/2\"\n        style={{ background: `radial-gradient(60% 100% at 50% 0%, color-mix(in oklab, ${color} ${Math.round(intensity * 45)}%, transparent), transparent 70%)` }}\n      />\n      {Array.from({ length: count }, (_, i) => {\n        const t = count === 1 ? 0.5 : i / (count - 1)\n        const angle = (t - 0.5) * spread + (noise(i, 1) - 0.5) * 6\n        const width = 7 + noise(i, 2) * 12\n        const sway = 1.5 + noise(i, 3) * 3\n        const alpha = intensity * (0.35 + noise(i, 4) * 0.5)\n        return (\n          <motion.span\n            key={i}\n            className=\"absolute top-[-12%] left-1/2 h-[135%] origin-top blur-[10px]\"\n            style={{\n              width: `${width}%`,\n              marginLeft: `${-width / 2}%`,\n              rotate: angle,\n              background: `linear-gradient(to bottom, color-mix(in oklab, ${color} ${Math.round(alpha * 60)}%, transparent), transparent 82%)`,\n              clipPath: \"polygon(38% 0, 62% 0, 100% 100%, 0 100%)\",\n            }}\n            animate={reduce ? undefined : { rotate: [angle - sway, angle + sway, angle - sway], opacity: [0.65, 1, 0.65] }}\n            transition={{ duration: duration * (0.8 + noise(i, 5) * 0.6), repeat: Infinity, ease: \"easeInOut\", delay: -noise(i, 6) * duration }}\n          />\n        )\n      })}\n    </div>\n  )\n}\n\nexport { LightRays, type LightRaysProps, type RayTone }\n",
      "type": "registry:ui",
      "target": "@components/ballmac/light-rays.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "light",
      "rays",
      "glow",
      "background",
      "hero"
    ],
    "version": "1.0.0",
    "updated": "2026-10-01",
    "ai": {
      "summary": "<LightRays count tone spread intensity duration /> inside a relative, overflow-hidden parent. Tone is a theme token, so it follows any palette.",
      "whenToUse": [
        "Hero and pricing sections that want warmth",
        "Behind modals and announcement bars"
      ],
      "whenNotToUse": [
        "Light backgrounds with dark text at high intensity"
      ],
      "composesWith": [
        "aurora-background",
        "retro-grid",
        "beams-background"
      ],
      "a11y": [
        {
          "keys": "Screen readers",
          "action": "Decorative: aria-hidden and pointer-events-none"
        },
        {
          "keys": "Reduced motion",
          "action": "The rays are still"
        }
      ],
      "customization": [
        "count",
        "tone",
        "spread",
        "intensity",
        "duration"
      ]
    },
    "examples": [
      "light-rays-demo",
      "light-rays-tones"
    ],
    "url": "https://ui.ballmac.com/components/light-rays"
  },
  "categories": [
    "backgrounds"
  ],
  "type": "registry:ui"
}