UI Components Easy
Light Rays
Atmospheric light rays emanating from a point, like sunlight through clouds, using CSS gradients and animated opacity.
Open in Lab
MCP
css javascript svelte vue
Targets: TS JS HTML React Svelte Vue
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--ray-color: rgba(251, 191, 36, 0.4);
--ray-color-bright: rgba(251, 191, 36, 0.15);
--ray-speed: 4s;
}
body {
font-family: system-ui, -apple-system, sans-serif;
min-height: 100vh;
background: #0a0a0a;
overflow: hidden;
}
.rays-wrapper {
position: relative;
width: 100%;
height: 100vh;
display: grid;
place-items: center;
background: #0a0a0a;
overflow: hidden;
}
.rays-container {
position: absolute;
top: -10%;
left: 50%;
transform: translateX(-50%);
width: 100%;
height: 120%;
pointer-events: none;
}
.ray {
position: absolute;
top: 0;
left: 50%;
width: var(--ray-width, 2px);
height: 100%;
transform-origin: top center;
transform: translateX(-50%) rotate(var(--ray-angle, 0deg));
background: linear-gradient(
180deg,
var(--ray-color) 0%,
var(--ray-color-bright) 30%,
transparent 80%
);
opacity: var(--ray-opacity, 0.5);
animation: rayPulse var(--ray-speed) ease-in-out infinite;
animation-delay: var(--ray-delay, 0s);
filter: blur(var(--ray-blur, 4px));
}
@keyframes rayPulse {
0%,
100% {
opacity: var(--ray-opacity, 0.5);
}
50% {
opacity: calc(var(--ray-opacity, 0.5) * 0.3);
}
}
/* Central glow source */
.rays-container::before {
content: "";
position: absolute;
top: -5%;
left: 50%;
transform: translateX(-50%);
width: 300px;
height: 300px;
border-radius: 50%;
background: radial-gradient(
circle,
rgba(251, 191, 36, 0.3) 0%,
rgba(251, 191, 36, 0.1) 30%,
transparent 70%
);
filter: blur(40px);
z-index: 2;
}
.rays-overlay {
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 60% at 50% 30%, transparent 20%, #0a0a0a 80%);
pointer-events: none;
z-index: 3;
}
.rays-content {
position: relative;
z-index: 10;
text-align: center;
color: #f1f5f9;
pointer-events: none;
}
.rays-title {
font-size: clamp(2rem, 5vw, 3.5rem);
font-weight: 800;
letter-spacing: -0.03em;
background: linear-gradient(135deg, #fef3c7 0%, #fbbf24 50%, #f59e0b 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 0.5rem;
text-shadow: none;
}
.rays-subtitle {
font-size: clamp(0.875rem, 2vw, 1.125rem);
color: rgba(148, 163, 184, 0.8);
font-weight: 400;
}// Light Rays — generates atmospheric volumetric rays
(function () {
"use strict";
const container = document.getElementById("rays-container");
if (!container) return;
const RAY_COUNT = 16;
const rays = [];
for (let i = 0; i < RAY_COUNT; i++) {
const ray = document.createElement("div");
ray.className = "ray";
// Spread rays from -60 to 60 degrees with some randomness
const baseAngle = -60 + (120 / (RAY_COUNT - 1)) * i;
const angle = baseAngle + (Math.random() - 0.5) * 8;
// Varied widths for organic feel
const width = Math.random() * 80 + 20;
const opacity = Math.random() * 0.5 + 0.2;
const delay = Math.random() * 4;
const blur = Math.random() * 8 + 2;
ray.style.setProperty("--ray-angle", angle + "deg");
ray.style.setProperty("--ray-width", width + "px");
ray.style.setProperty("--ray-opacity", opacity);
ray.style.setProperty("--ray-delay", delay + "s");
ray.style.setProperty("--ray-blur", blur + "px");
container.appendChild(ray);
rays.push(ray);
}
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Light Rays</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="rays-wrapper">
<div class="rays-container" id="rays-container"></div>
<div class="rays-overlay"></div>
<div class="rays-content">
<h1 class="rays-title">Light Rays</h1>
<p class="rays-subtitle">Atmospheric volumetric lighting effect</p>
</div>
</div>
<script src="script.js"></script>
</body>
</html>import { useMemo } from "react";
interface LightRaysProps {
rayCount?: number;
color?: string;
colorBright?: string;
glowColor?: string;
speed?: number;
intensity?: number;
className?: string;
}
interface RayData {
angle: number;
width: number;
opacity: number;
delay: number;
blur: number;
}
function generateRays(count: number, intensity: number): RayData[] {
const rays: RayData[] = [];
for (let i = 0; i < count; i++) {
const baseAngle = -60 + (120 / (count - 1)) * i;
rays.push({
angle: baseAngle + (Math.random() - 0.5) * 8,
width: Math.random() * 80 + 20,
opacity: (Math.random() * 0.5 + 0.2) * intensity,
delay: Math.random() * 4,
blur: Math.random() * 8 + 2,
});
}
return rays;
}
export function LightRays({
rayCount = 16,
color = "rgba(251, 191, 36, 0.4)",
colorBright = "rgba(251, 191, 36, 0.15)",
glowColor = "rgba(251, 191, 36, 0.3)",
speed = 4,
intensity = 1,
className = "",
}: LightRaysProps) {
const rays = useMemo(() => generateRays(rayCount, intensity), [rayCount, intensity]);
const keyframes = `
@keyframes rayPulse {
0%, 100% { opacity: var(--ray-opacity); }
50% { opacity: calc(var(--ray-opacity) * 0.3); }
}
`;
return (
<div
className={className}
style={{ position: "absolute", inset: 0, overflow: "hidden", pointerEvents: "none" }}
>
<style>{keyframes}</style>
<div
style={{
position: "absolute",
top: "-10%",
left: "50%",
transform: "translateX(-50%)",
width: "100%",
height: "120%",
}}
>
{/* Central glow */}
<div
style={{
position: "absolute",
top: "-5%",
left: "50%",
transform: "translateX(-50%)",
width: 300,
height: 300,
borderRadius: "50%",
background: `radial-gradient(circle, ${glowColor} 0%, ${glowColor.replace(/[\d.]+\)$/, "0.1)")} 30%, transparent 70%)`,
filter: "blur(40px)",
zIndex: 2,
}}
/>
{rays.map((ray, i) => (
<div
key={i}
style={{
position: "absolute",
top: 0,
left: "50%",
width: ray.width,
height: "100%",
transformOrigin: "top center",
transform: `translateX(-50%) rotate(${ray.angle}deg)`,
background: `linear-gradient(180deg, ${color} 0%, ${colorBright} 30%, transparent 80%)`,
opacity: ray.opacity,
animation: `rayPulse ${speed}s ease-in-out infinite`,
animationDelay: `${ray.delay}s`,
filter: `blur(${ray.blur}px)`,
["--ray-opacity" as any]: ray.opacity,
}}
/>
))}
</div>
{/* Edge fade overlay */}
<div
style={{
position: "absolute",
inset: 0,
background: "radial-gradient(ellipse 60% 60% at 50% 30%, transparent 20%, #0a0a0a 80%)",
pointerEvents: "none",
zIndex: 3,
}}
/>
</div>
);
}
// Demo usage
export default function LightRaysDemo() {
return (
<div
style={{
width: "100vw",
height: "100vh",
background: "#0a0a0a",
display: "grid",
placeItems: "center",
position: "relative",
fontFamily: "system-ui, -apple-system, sans-serif",
overflow: "hidden",
}}
>
<LightRays
rayCount={16}
color="rgba(251, 191, 36, 0.4)"
colorBright="rgba(251, 191, 36, 0.15)"
glowColor="rgba(251, 191, 36, 0.3)"
speed={4}
intensity={1}
/>
<div style={{ position: "relative", zIndex: 10, textAlign: "center", pointerEvents: "none" }}>
<h1
style={{
fontSize: "clamp(2rem, 5vw, 3.5rem)",
fontWeight: 800,
letterSpacing: "-0.03em",
background: "linear-gradient(135deg, #fef3c7 0%, #fbbf24 50%, #f59e0b 100%)",
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
backgroundClip: "text",
marginBottom: "0.5rem",
}}
>
Light Rays
</h1>
<p
style={{
fontSize: "clamp(0.875rem, 2vw, 1.125rem)",
color: "rgba(148, 163, 184, 0.8)",
}}
>
Atmospheric volumetric lighting effect
</p>
</div>
</div>
);
}<script>
export let rayCount = 16;
export let color = "rgba(251, 191, 36, 0.4)";
export let colorBright = "rgba(251, 191, 36, 0.15)";
export let glowColor = "rgba(251, 191, 36, 0.3)";
export let speed = 4;
export let intensity = 1;
function generateRays(count, intensity) {
const rays = [];
for (let i = 0; i < count; i++) {
const baseAngle = -60 + (120 / (count - 1)) * i;
rays.push({
angle: baseAngle + (Math.random() - 0.5) * 8,
width: Math.random() * 80 + 20,
opacity: (Math.random() * 0.5 + 0.2) * intensity,
delay: Math.random() * 4,
blur: Math.random() * 8 + 2,
});
}
return rays;
}
$: rays = generateRays(rayCount, intensity);
$: glowFaded = glowColor.replace(/[\d.]+\)$/, "0.1)");
</script>
<div
style="width: 100vw; height: 100vh; background: #0a0a0a; display: grid; place-items: center; position: relative; font-family: system-ui, -apple-system, sans-serif; overflow: hidden;"
>
<!-- Light Rays -->
<div style="position: absolute; inset: 0; overflow: hidden; pointer-events: none;">
<div
style="position: absolute; top: -10%; left: 50%; transform: translateX(-50%); width: 100%; height: 120%;"
>
<!-- Central glow -->
<div
style="position: absolute; top: -5%; left: 50%; transform: translateX(-50%); width: 300px; height: 300px; border-radius: 50%; background: radial-gradient(circle, {glowColor} 0%, {glowFaded} 30%, transparent 70%); filter: blur(40px); z-index: 2;"
></div>
{#each rays as ray, i}
<div
style="position: absolute; top: 0; left: 50%; width: {ray.width}px; height: 100%; transform-origin: top center; transform: translateX(-50%) rotate({ray.angle}deg); background: linear-gradient(180deg, {color} 0%, {colorBright} 30%, transparent 80%); opacity: {ray.opacity}; animation: rayPulse {speed}s ease-in-out infinite; animation-delay: {ray.delay}s; filter: blur({ray.blur}px); --ray-opacity: {ray.opacity};"
></div>
{/each}
</div>
<!-- Edge fade overlay -->
<div
style="position: absolute; inset: 0; background: radial-gradient(ellipse 60% 60% at 50% 30%, transparent 20%, #0a0a0a 80%); pointer-events: none; z-index: 3;"
></div>
</div>
<!-- Content -->
<div style="position: relative; z-index: 10; text-align: center; pointer-events: none;">
<h1
style="font-size: clamp(2rem, 5vw, 3.5rem); font-weight: 800; letter-spacing: -0.03em; background: linear-gradient(135deg, #fef3c7 0%, #fbbf24 50%, #f59e0b 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; margin-bottom: 0.5rem;"
>
Light Rays
</h1>
<p
style="font-size: clamp(0.875rem, 2vw, 1.125rem); color: rgba(148, 163, 184, 0.8);"
>
Atmospheric volumetric lighting effect
</p>
</div>
</div>
<style>
@keyframes rayPulse {
0%, 100% { opacity: var(--ray-opacity); }
50% { opacity: calc(var(--ray-opacity) * 0.3); }
}
</style><script setup>
import { computed } from "vue";
const props = defineProps({
rayCount: { type: Number, default: 16 },
color: { type: String, default: "rgba(251, 191, 36, 0.4)" },
colorBright: { type: String, default: "rgba(251, 191, 36, 0.15)" },
glowColor: { type: String, default: "rgba(251, 191, 36, 0.3)" },
speed: { type: Number, default: 4 },
intensity: { type: Number, default: 1 },
});
function generateRays(count, intensity) {
const rays = [];
for (let i = 0; i < count; i++) {
const baseAngle = -60 + (120 / (count - 1)) * i;
rays.push({
angle: baseAngle + (Math.random() - 0.5) * 8,
width: Math.random() * 80 + 20,
opacity: (Math.random() * 0.5 + 0.2) * intensity,
delay: Math.random() * 4,
blur: Math.random() * 8 + 2,
});
}
return rays;
}
const rays = computed(() => generateRays(props.rayCount, props.intensity));
const glowFaded = computed(() => props.glowColor.replace(/[\d.]+\)$/, "0.1)"));
</script>
<template>
<div class="light-rays-demo">
<!-- Light Rays -->
<div class="rays-container">
<div class="rays-inner">
<!-- Central glow -->
<div
class="central-glow"
:style="{
background: `radial-gradient(circle, ${props.glowColor} 0%, ${glowFaded} 30%, transparent 70%)`,
}"
></div>
<div
v-for="(ray, i) in rays"
:key="i"
class="ray"
:style="{
width: ray.width + 'px',
transform: `translateX(-50%) rotate(${ray.angle}deg)`,
background: `linear-gradient(180deg, ${props.color} 0%, ${props.colorBright} 30%, transparent 80%)`,
opacity: ray.opacity,
animation: `rayPulse ${props.speed}s ease-in-out infinite`,
animationDelay: ray.delay + 's',
filter: `blur(${ray.blur}px)`,
'--ray-opacity': ray.opacity,
}"
></div>
</div>
<!-- Edge fade overlay -->
<div class="edge-fade"></div>
</div>
<!-- Content -->
<div class="content">
<h1 class="title">Light Rays</h1>
<p class="subtitle">Atmospheric volumetric lighting effect</p>
</div>
</div>
</template>
<style scoped>
@keyframes rayPulse {
0%, 100% { opacity: var(--ray-opacity); }
50% { opacity: calc(var(--ray-opacity) * 0.3); }
}
.light-rays-demo {
width: 100vw;
height: 100vh;
background: #0a0a0a;
display: grid;
place-items: center;
position: relative;
font-family: system-ui, -apple-system, sans-serif;
overflow: hidden;
}
.rays-container {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.rays-inner {
position: absolute;
top: -10%;
left: 50%;
transform: translateX(-50%);
width: 100%;
height: 120%;
}
.central-glow {
position: absolute;
top: -5%;
left: 50%;
transform: translateX(-50%);
width: 300px;
height: 300px;
border-radius: 50%;
filter: blur(40px);
z-index: 2;
}
.ray {
position: absolute;
top: 0;
left: 50%;
height: 100%;
transform-origin: top center;
}
.edge-fade {
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 60% at 50% 30%, transparent 20%, #0a0a0a 80%);
pointer-events: none;
z-index: 3;
}
.content {
position: relative;
z-index: 10;
text-align: center;
pointer-events: none;
}
.title {
font-size: clamp(2rem, 5vw, 3.5rem);
font-weight: 800;
letter-spacing: -0.03em;
background: linear-gradient(135deg, #fef3c7 0%, #fbbf24 50%, #f59e0b 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 0.5rem;
}
.subtitle {
font-size: clamp(0.875rem, 2vw, 1.125rem);
color: rgba(148, 163, 184, 0.8);
}
</style>Light Rays
Stunning volumetric light rays that emanate from a central point, simulating sunlight breaking through clouds or a divine glow effect. Built purely with CSS using rotated gradient pseudo-elements.
How it works
- Multiple elongated gradient elements are rotated at different angles from a central origin
- Each ray has a different width, opacity, and animation delay for organic variation
- CSS
@keyframesanimate opacity to create a pulsing, breathing light effect - A radial gradient overlay softens the edges for a polished atmosphere
Customization
- Change
--ray-colorto adjust the light color (warm gold, cool blue, etc.) - Modify the number of rays and their rotation angles
- Adjust
--ray-intensityfor brighter or subtler effects - Control animation speed with
--ray-speed
When to use it
- Hero section dramatic backgrounds
- Spiritual / ethereal themed pages
- Product spotlight effects
- Atmospheric landing pages