Domphy

lightRays

A Backgrounds block/component from Magic UI — clean-room reimplemented for Domphy (see methodology). Call lightRays() with no arguments for a working demo, or edit the code below live.

Implementation notes

Pure CSS @keyframes implementation (per this package's documented convention for continuous effects, e.g. meteors()/dottedMap()) rather than the motion() patch, because motion() only supports a single initial->animate 2-keyframe transition and can't express a multi-stop (0%/50%/100%) infinite pulse+sway. Each ray gets two independent per-instance @keyframes (opacity pulse, rotation sway) with randomized left position, base tilt, width, swing amplitude, delay and duration (baked in at generation time, hashString-named per the meteors()/dottedMap() unique-animation-name pattern), applied together via the CSS animation shorthand's comma-separated multi-value syntax on the same element so they run in perfect lockstep — a legitimate reading of the spec's 'two coupled infinite-loop animations'. Two static (non-animated) radial-gradient glow blobs pinned to the top corners via themeColor, blurred; mix-blend-mode: screen on both rays and glows for the additive-style blend described. Ray shape uses clip-path: polygon(...) to taper the beam (spec: 'tapered gradient shape') rather than upstream's likely SVG/canvas approach — a reasonable CSS-only equivalent. color default mapped to Domphy's 'primary' ThemeColor family (spec's literal default was 'a soft translucent light blue', which isn't an available literal in this framework — themed instead, per project convention of never using raw hex/rgb). Demo wrapper is a dark (dataTone shift-15) self-sized panel with default heading+paragraph, matching this package's zero-arg-demo convention. doctor CLI: 0 diagnostics.

Status: ported · Reference: Magic UI original

// magicui "Light Rays" — clean-room reimplementation from the public
// behavior/visual spec only (no upstream source viewed or copied). An
// ambient full-container effect of several soft, blurred light beams
// streaming down from the top and gently swaying and pulsing forever.
//
// Continuous per-ray animation is pure CSS `@keyframes` (the same
// block-port convention already used by `meteors()`/`dottedMap()` in this
// package): two coupled keyframe animations (an opacity pulse and a
// rotation sway) sharing the same randomized duration/delay play on the
// same element via the CSS `animation` shorthand's comma-separated
// multi-value syntax, so they stay in lockstep without any JS animation
// loop. Each ray's horizontal position, base tilt, width, swing amplitude,
// delay, and duration are randomized once at generation time so the set
// never looks synchronized.

import type { DomphyElement, StyleObject } from "@domphy/core";
import { hashString } from "@domphy/core";
import { heading, paragraph } from "@domphy/ui";
import { type ThemeColor, themeColor, themeSpacing } from "@domphy/theme";

export interface LightRaysProps {
  /** Number of rays. Defaults to `7`. */
  count?: number;
  /** Theme color family for the ray tint. Defaults to `"primary"` (reads as a soft
   * translucent blue against a dark backdrop). */
  color?: ThemeColor;
  /** Blur radius, in px. Defaults to `36`. */
  blur?: number;
  /** Peak ray opacity, 0–1. Defaults to `0.65`. */
  opacity?: number;
  /** Seconds per animation cycle. Defaults to `14`. */
  speed?: number;
  /** Ray height, any CSS length. Defaults to `"70vh"`. */
  length?: string;
  /** Foreground content layered above the rays. Defaults to a small demo panel. */
  children?: DomphyElement | DomphyElement[];
  /** Passthrough style merged onto the outer container. */
  style?: StyleObject;
}

let lightRaysInstanceCounter = 0;

function randomBetween(min: number, max: number): number {
  return min + Math.random() * (max - min);
}

function glowBlob(key: string, corner: "left" | "right", color: ThemeColor): DomphyElement {
  return {
    div: null,
    _key: key,
    ariaHidden: "true",
    // Decorative ambient glow with no text of its own — exempt from the
    // missing-color contract (mirrors meteors()/dottedMap() in this package).
    _doctorDisable: "missing-color",
    style: {
      position: "absolute",
      top: 0,
      left: corner === "left" ? 0 : "auto",
      right: corner === "right" ? 0 : "auto",
      width: "45%",
      height: "45%",
      pointerEvents: "none",
      mixBlendMode: "screen",
      backgroundImage: (listener) =>
        `radial-gradient(circle, ${themeColor(listener, "shift-10", color)} 0%, transparent 70%)`,
      filter: "blur(48px)",
    } as StyleObject,
  } as DomphyElement;
}

/**
 * Ambient field of soft, blurred light beams streaming down from the top of
 * the container, each independently swaying and pulsing forever. Purely
 * ambient — pointer events disabled, no user interaction. Call with no
 * arguments for a working demo — seven staggered rays over a dark panel
 * behind a heading.
 */
function lightRays(props: LightRaysProps = {}): DomphyElement<"div"> {
  const instanceId = ++lightRaysInstanceCounter;
  const count = Math.max(1, Math.round(props.count ?? 7));
  const color = props.color ?? "primary";
  const blur = props.blur ?? 36;
  const peakOpacity = props.opacity ?? 0.65;
  const speed = props.speed ?? 14;
  const length = props.length ?? "70vh";

  const rayElements: DomphyElement[] = Array.from({ length: count }, (_unused, index) => {
    const leftPercent = ((index + 0.5) / count) * 100 + randomBetween(-1, 1) * (50 / count);
    const baseAngle =
      count > 1 ? -28 + (index / (count - 1)) * 56 + randomBetween(-5, 5) : randomBetween(-8, 8);
    const widthPercent = randomBetween(4, 10);
    const swingAmplitude = randomBetween(6, 16);
    const delaySeconds = randomBetween(0, speed);
    const durationSeconds = speed * randomBetween(0.85, 1.15);

    const opacityKeyframes = {
      "0%": { opacity: 0 },
      "50%": { opacity: peakOpacity },
      "100%": { opacity: 0 },
    };
    const rotateKeyframes = {
      "0%": { transform: `rotate(${(baseAngle - swingAmplitude).toFixed(2)}deg)` },
      "50%": { transform: `rotate(${(baseAngle + swingAmplitude).toFixed(2)}deg)` },
      "100%": { transform: `rotate(${(baseAngle - swingAmplitude).toFixed(2)}deg)` },
    };
    const opacityAnimationName = `light-ray-opacity-${hashString(`${instanceId}-${index}-${JSON.stringify(opacityKeyframes)}`)}`;
    const rotateAnimationName = `light-ray-rotate-${hashString(`${instanceId}-${index}-${JSON.stringify(rotateKeyframes)}`)}`;

    return {
      div: null,
      _key: `ray-${instanceId}-${index}`,
      ariaHidden: "true",
      // Decorative light beam with no text of its own — exempt from the
      // missing-color contract.
      _doctorDisable: "missing-color",
      style: {
        position: "absolute",
        top: 0,
        left: `${leftPercent}%`,
        width: `${widthPercent}%`,
        height: length,
        transformOrigin: "top center",
        clipPath: "polygon(35% 0%, 65% 0%, 100% 100%, 0% 100%)",
        pointerEvents: "none",
        mixBlendMode: "screen",
        filter: `blur(${blur}px)`,
        backgroundImage: (listener) =>
          `linear-gradient(to bottom, ${themeColor(listener, "shift-11", color)} 0%, transparent 100%)`,
        animation: `${opacityAnimationName} ${durationSeconds.toFixed(2)}s ease-in-out ${delaySeconds.toFixed(2)}s infinite, ${rotateAnimationName} ${durationSeconds.toFixed(2)}s ease-in-out ${delaySeconds.toFixed(2)}s infinite`,
        [`@keyframes ${opacityAnimationName}`]: opacityKeyframes,
        [`@keyframes ${rotateAnimationName}`]: rotateKeyframes,
      } as StyleObject,
    } as DomphyElement;
  });

  const defaultChildren: DomphyElement[] = [
    { h2: "Light Rays", $: [heading()] } as DomphyElement,
    {
      p: "Soft, blurred beams sway and pulse behind your content.",
      $: [paragraph()],
    } as DomphyElement,
  ];
  const contentChildren = props.children
    ? Array.isArray(props.children)
      ? props.children
      : [props.children]
    : defaultChildren;

  return {
    div: [
      glowBlob(`glow-left-${instanceId}`, "left", color),
      glowBlob(`glow-right-${instanceId}`, "right", color),
      ...rayElements,
      { div: contentChildren, style: { position: "relative", zIndex: 1 } },
    ],
    dataTone: "shift-15",
    style: {
      position: "relative",
      overflow: "hidden",
      borderRadius: themeSpacing(4),
      padding: themeSpacing(8),
      minHeight: themeSpacing(64),
      backgroundColor: (listener) => themeColor(listener, "inherit"),
      color: (listener) => themeColor(listener, "shift-9"),
      ...(props.style ?? {}),
    } as StyleObject,
  };
}

export { lightRays };

← Back to Magic UI catalog