Evenly spaced vertical guide lines with glowing light beams sweeping down each one at staggered, deterministic speeds, rendered with CSS transforms only.
Preview
Signal
Beams in the grid
Customize
3 props8
1
Installation
Copies the component source and its CSS Module into your project.
npx motionfoundry add beam-linesWrites components/ui/beam-lines.tsx and components/styles/beam-lines.module.css.
Usage
Updates live as you change the controls — only props that differ from the defaults are included.
example.tsx
1import { BeamLines } from "@motionfoundry/ui";2 3export function Example() {4 return (5 <BeamLines />6 );7}Props
Accessibility
The beam field is aria-hidden and non-interactive; children render in a normal content layer. Timings use a seeded rand(n) for hydration-safe markup. prefers-reduced-motion stops the sweep and parks each beam mid-line.
Toggle prefers-reduced-motion in your OS or browser devtools (Rendering → Emulate CSS media feature) to verify the reduced-motion state.
Source
components/ui/beam-lines.tsx
1"use client";2 3import * as React from "react";4import styles from "../styles/beam-lines.module.css";5 6export interface BeamLinesProps {7 /** Number of vertical guide lines */8 lines?: number;9 /** Beam glow color */10 beamColor?: string;11 /** Speed multiplier (1 = base 2.5–4.5s per sweep) */12 speed?: number;13 /** Foreground content rendered above the beams */14 children?: React.ReactNode;15 /** Optional className merged onto the root */16 className?: string;17}18 19const rand = (n: number) => {20 const x = Math.sin(n * 9301 + 49297) * 233280;21 return x - Math.floor(x);22};23 24export function BeamLines({25 lines = 8,26 beamColor = "#38bdf8",27 speed = 1,28 children,29 className = "",30}: BeamLinesProps) {31 const safeSpeed = speed > 0 ? speed : 1;32 const total = Math.max(0, Math.round(lines));33 const styleVars = { "--mf-bl-color": beamColor } as React.CSSProperties;34 35 return (36 <div style={styleVars} className={`${styles.root} ${className}`.trim()}>37 <div aria-hidden="true" className={styles.field}>38 {Array.from({ length: total }, (_, i) => (39 <div key={i} className={styles.line} style={{ left: `${((i + 1) / (total + 1)) * 100}%` }}>40 <span41 className={styles.beam}42 style={{43 animationDuration: `${((2.5 + rand(i) * 2) / safeSpeed).toFixed(2)}s`,44 animationDelay: `-${(rand(i + 1) * 3).toFixed(2)}s`,45 }}46 />47 </div>48 ))}49 </div>50 <div className={styles.content}>51 {children ?? (52 <div className={styles.copy}>53 <span className={styles.eyebrow}>Signal</span>54 <h3 className={styles.title}>Beams in the grid</h3>55 </div>56 )}57 </div>58 </div>59 );60}