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.
Right-to-left: Rays fan out from a point centred with left-1/2 and a negative margin; both are physical and symmetric, so the picture is the same in either direction.
Let there be light
Soft rays, sway, and nothing else moving.
import { LightRays } from "@/components/ballmac/light-rays"
export default function LightRaysDemo() {
return (
<div className="relative flex h-80 w-full max-w-2xl items-center justify-center overflow-hidden rounded-2xl border bg-background">
<LightRays tone="chart-1" count={8} />
<div className="relative z-10 text-center">
<h3 className="text-4xl font-semibold tracking-tight">Let there be light</h3>
<p className="mt-1 text-sm text-muted-foreground">Soft rays, sway, and nothing else moving.</p>
</div>
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/light-raysInstall the dependencies.
$ pnpm add motion@^12Copy the source into your project.
components/ballmac/light-rays.tsx// Ballmac UI: Light Rays. https://ui.ballmac.com/components/light-rays "use client" import * as React from "react" import { motion, useReducedMotion } from "motion/react" import { cn } from "@/lib/utils" type RayTone = "foreground" | "primary" | "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" const VAR: Record<RayTone, string> = { foreground: "var(--foreground)", primary: "var(--primary)", "chart-1": "var(--chart-1)", "chart-2": "var(--chart-2)", "chart-3": "var(--chart-3)", "chart-4": "var(--chart-4)", "chart-5": "var(--chart-5)", } type LightRaysProps = Omit<React.ComponentProps<"div">, "children"> & { /** Number of rays. */ count?: number /** Color of the light. */ tone?: RayTone /** How far the rays fan out, in degrees from one edge to the other. */ spread?: number /** Brightness from 0 to 1. */ intensity?: number /** Seconds for a ray to sway once. Longer is calmer. */ duration?: number } // Deterministic pseudo-randomness keeps the server and browser identical. const noise = (i: number, salt: number) => { const v = Math.sin(i * 127.1 + salt * 311.7) * 43758.5453 return v - Math.floor(v) } /** Soft beams of light fanning down from the top edge, swaying slowly. Decorative. */ function LightRays({ count = 7, tone = "chart-1", spread = 70, intensity = 0.55, duration = 9, className, ...props }: LightRaysProps) { const reduce = useReducedMotion() const color = VAR[tone] return ( <div data-slot="light-rays" aria-hidden="true" className={cn("pointer-events-none absolute inset-0 overflow-hidden", className)} {...props}> <span className="absolute inset-x-0 top-0 h-1/2" style={{ background: `radial-gradient(60% 100% at 50% 0%, color-mix(in oklab, ${color} ${Math.round(intensity * 45)}%, transparent), transparent 70%)` }} /> {Array.from({ length: count }, (_, i) => { const t = count === 1 ? 0.5 : i / (count - 1) const angle = (t - 0.5) * spread + (noise(i, 1) - 0.5) * 6 const width = 7 + noise(i, 2) * 12 const sway = 1.5 + noise(i, 3) * 3 const alpha = intensity * (0.35 + noise(i, 4) * 0.5) return ( <motion.span key={i} className="absolute top-[-12%] left-1/2 h-[135%] origin-top blur-[10px]" style={{ width: `${width}%`, marginLeft: `${-width / 2}%`, rotate: angle, background: `linear-gradient(to bottom, color-mix(in oklab, ${color} ${Math.round(alpha * 60)}%, transparent), transparent 82%)`, clipPath: "polygon(38% 0, 62% 0, 100% 100%, 0 100%)", }} animate={reduce ? undefined : { rotate: [angle - sway, angle + sway, angle - sway], opacity: [0.65, 1, 0.65] }} transition={{ duration: duration * (0.8 + noise(i, 5) * 0.6), repeat: Infinity, ease: "easeInOut", delay: -noise(i, 6) * duration }} /> ) })} </div> ) } export { LightRays, type LightRaysProps, type RayTone }Update the import paths to match your project setup.
Usage
import { LightRays } from "@/components/ballmac/light-rays"The full example is in the Code tab above.
Examples
Tones and count
import { LightRays, type RayTone } from "@/components/ballmac/light-rays"
const tones: RayTone[] = ["chart-2", "chart-3", "chart-4", "foreground"]
export default function LightRaysTones() {
return (
<div className="grid w-full max-w-xl grid-cols-2 gap-3">
{tones.map((tone, i) => (
<div key={tone} className="relative h-36 overflow-hidden rounded-xl border bg-background">
<LightRays tone={tone} count={4 + i} intensity={0.6} />
<span className="absolute bottom-2 start-3 z-10 font-mono text-[11px] text-muted-foreground">
{tone} · {4 + i} rays
</span>
</div>
))}
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
countNumber of rays. | number | 7 |
toneColor of the light. | RayTone | "chart-1" |
spreadHow far the rays fan out, in degrees from one edge to the other. | number | 70 |
intensityBrightness from 0 to 1. | number | 0.55 |
durationSeconds for a ray to sway once. Longer is calmer. | number | 9 |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| Screen readers | Decorative: aria-hidden and pointer-events-none |
| Reduced motion | The rays are still |
Use with AI
<LightRays count tone spread intensity duration /> inside a relative, overflow-hidden parent. Tone is a theme token, so it follows any palette. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Light Rays (@ballmac/light-rays) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Hero and pricing sections that want warmth
- Behind modals and announcement bars
Not for
- Light backgrounds with dark text at high intensity
Registry JSON: https://ui.ballmac.com/r/light-rays.json
Credits
Free to use in personal and commercial projects.
- npm
- motion@^12
- Registry
- shadcn/utils
Pairs well with
Aurora Background
Soft, slowly drifting aurora light made of blurred token-colored glows with fine vertical curtains and an optional radial fade. Pure CSS layers, paused off-screen, still under reduced motion.
Retro Grid
A perspective floor of grid lines gliding toward you under a glowing horizon, tinted with any theme color, tilt and cell size adjustable, still under reduced motion.
Beams Background
Hairline rails with thin light beams rising along them and dissolving near the top, over a soft glow. Canvas, token colors, DPR aware, pauses off-screen, a still frame under reduced motion.
Dot Pattern
A lightweight SVG dot grid background with an optional radial fade and a few softly pulsing glow dots. One pattern element, token colors, hydration-safe, still under reduced motion.