A white section that looks down a square tunnel. The floor, ceiling and both walls are grids drawn in perspective, and blocks of colour shoot along them from the far end toward the viewer, like light trails at warp speed. A waitlist card with an email field sits in the centre. The tunnel is built from four tilted boxes with CSS only. On phones the card fills the width and the tunnel keeps its depth.
// Adapted from Magic UI (https://magicui.design/docs/components/warp-background)
// Copyright (c) Magic UI. MIT License: https://we.inc/components/licenses
import { useState } from "react";
import type { CSSProperties, FormEvent } from "react";
import { motion, useReducedMotion } from "framer-motion";
/** Tunnel settings. */
const PERSPECTIVE = 120; // pixels: smaller is a deeper, faster-looking tunnel
const CELL = 5; // grid cell and beam width, as a percent of each wall
const BEAMS_PER_SIDE = 6;
const BEAM_SECONDS = 3;
const GRID_COLOR = "rgba(15, 23, 42, 0.16)";
/** Small repeatable random, so the server and the browser place the same beams. */
function seeded(seed: number) {
let s = seed;
return () => {
s = (s * 16807) % 2147483647;
return (s - 1) / 2147483646;
};
}
function makeBeams(seed: number) {
const rand = seeded(seed);
const cells = Math.floor(100 / CELL);
const step = cells / BEAMS_PER_SIDE;
return Array.from({ length: BEAMS_PER_SIDE }, (_, i) => ({
x: Math.floor(i * step + rand() * (step - 1)) * CELL,
hue: Math.floor(rand() * 360),
ratio: 3 + Math.floor(rand() * 8),
delay: -rand() * BEAM_SECONDS,
}));
}
const walls = [
{ name: "top", beams: makeBeams(11) },
{ name: "bottom", beams: makeBeams(23) },
{ name: "left", beams: makeBeams(37) },
{ name: "right", beams: makeBeams(51) },
];
function WarpBackground() {
return (
<div
aria-hidden="true"
className="warp-tunnel-background-stage"
style={{ "--warp-perspective": `${PERSPECTIVE}px`, "--warp-cell": `${CELL}%`, "--warp-grid": GRID_COLOR, "--warp-seconds": `${BEAM_SECONDS}s` } as CSSProperties}
>
{walls.map((wall) => (
<div key={wall.name} className={`warp-tunnel-background-wall warp-tunnel-background-${wall.name}`}>
{wall.beams.map((b, i) => (
<span
key={i}
className="warp-tunnel-background-beam"
style={{ left: `${b.x}%`, aspectRatio: `1 / ${b.ratio}`, background: `linear-gradient(hsl(${b.hue} 90% 58%), transparent)`, animationDelay: `${b.delay}s` }}
/>
))}
</div>
))}
</div>
);
}
export default function WarpTunnelBackgroundWithRisingColourBeams() {
const reduce = useReducedMotion();
const [email, setEmail] = useState("");
const [joined, setJoined] = useState(false);
const submit = (e: FormEvent) => {
e.preventDefault();
if (email.includes("@")) setJoined(true);
};
return (
<section className="relative flex min-h-[640px] items-center justify-center overflow-hidden bg-white px-4 py-20 sm:px-6">
<style>{`
@keyframes warp-tunnel-background-rise {
from { transform: translate(-50%, 100cqmax); }
to { transform: translate(-50%, -100%); }
}
.warp-tunnel-background-stage {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
container-type: size;
clip-path: inset(0);
perspective: var(--warp-perspective);
transform-style: preserve-3d;
}
.warp-tunnel-background-wall {
position: absolute;
height: 100cqmax;
container-type: inline-size;
transform-style: preserve-3d;
background:
linear-gradient(var(--warp-grid) 0 1px, transparent 1px var(--warp-cell)) 50% -0.5px / var(--warp-cell) var(--warp-cell),
linear-gradient(90deg, var(--warp-grid) 0 1px, transparent 1px var(--warp-cell)) 50% 50% / var(--warp-cell) var(--warp-cell);
}
.warp-tunnel-background-top { width: 100cqi; transform-origin: 50% 0%; transform: rotateX(-90deg); z-index: 2; }
.warp-tunnel-background-bottom { top: 100%; width: 100cqi; transform-origin: 50% 0%; transform: rotateX(-90deg); }
.warp-tunnel-background-left { top: 0; left: 0; width: 100cqh; transform-origin: 0% 0%; transform: rotate(90deg) rotateX(-90deg); }
.warp-tunnel-background-right { top: 0; right: 0; width: 100cqh; transform-origin: 100% 0%; transform: rotate(-90deg) rotateX(-90deg); }
.warp-tunnel-background-beam {
position: absolute;
top: 0;
width: var(--warp-cell);
transform: translate(-50%, 100cqmax);
animation: warp-tunnel-background-rise var(--warp-seconds) linear infinite;
}
@media (prefers-reduced-motion: reduce) {
.warp-tunnel-background-beam { animation: none; transform: translate(-50%, 30cqmax); }
}
`}</style>
<WarpBackground />
{/* Soft white pool so the far end of the tunnel does not show through the card edges. */}
<div aria-hidden="true" className="pointer-events-none absolute inset-0" style={{ background: "radial-gradient(ellipse 40% 48% at 50% 50%, #fff 45%, rgba(255,255,255,0) 100%)" }} />
<motion.div
initial={reduce ? false : { opacity: 0, scale: 0.94, y: 16 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
transition={{ type: "spring", stiffness: 150, damping: 18 }}
className="relative w-full max-w-md rounded-3xl bg-white p-7 text-center shadow-2xl shadow-slate-900/15 ring-1 ring-slate-900/10 sm:p-9"
>
<p className="inline-flex items-center gap-2 rounded-full bg-slate-900 px-3 py-1 text-xs font-semibold uppercase tracking-widest text-white">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-400" aria-hidden="true" />
Private beta
</p>
<h1 className="mt-5 text-3xl font-semibold tracking-tight text-slate-900 sm:text-4xl">Deploys that land before you look up</h1>
<p className="mt-3 text-slate-600">Slipstream builds only what changed and ships it to 34 regions in under nine seconds. Invites go out every Thursday.</p>
{joined ? (
<p role="status" className="mt-7 rounded-2xl bg-emerald-50 px-4 py-4 text-sm font-medium text-emerald-800">
You are on the list. We will write to {email} when your invite is ready.
</p>
) : (
<form onSubmit={submit} className="mt-7 flex flex-col gap-2.5 sm:flex-row">
<label htmlFor="warp-tunnel-background-email" className="sr-only">Work email</label>
<input
id="warp-tunnel-background-email"
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="[email protected]"
className="h-12 w-full min-w-0 flex-none rounded-xl sm:w-auto sm:flex-1 border border-slate-300 bg-white px-4 text-sm text-slate-900 placeholder:text-slate-500 focus:border-slate-900 focus:outline-none focus:ring-2 focus:ring-slate-900/20"
/>
<button type="submit" className="h-12 rounded-xl bg-slate-900 px-5 text-sm font-semibold text-white transition-colors hover:bg-slate-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-900 focus-visible:ring-offset-2">
Request invite
</button>
</form>
)}
<p className="mt-4 text-xs text-slate-600">2,180 teams are ahead of you. No card needed.</p>
</motion.div>
</section>
);
}
Install with one command: npx shadcn@latest add https://we.inc/r/warp-tunnel-background-with-rising-colour-beams.json
The stage has a CSS `perspective`. Inside it are four long boxes, each hinged on one edge of the stage and rotated ninety degrees away from the viewer, so they become the ceiling, floor and two walls. Each box carries a grid drawn with two repeating gradients.
They are container query length units. The stage is declared as a size container, so `100cqi` is its width and `100cqh` its height. This lets the walls take their size from the section, whatever its shape, without JavaScript.
Each beam is a gradient block on a wall. A keyframe animation slides it from the far end of the wall to past the near end. Because the wall is tilted in 3D, the browser draws that straight slide as a rush toward the viewer.
It needs container query units and 3D transforms, which current versions of Chrome, Edge, Safari and Firefox all have. In an old browser the section shows a plain background with the card.
Yes. `WarpBackground` fills any `relative` parent with `overflow-hidden`. Place it first and put your content after it with `relative` so it sits on top.
Adapted from Warp Background in Magic UI, used under its open-source licence. Licences and credits
Related: Animated Grid Background with Travelling Light Beams, Starfield Background with Depth and Shooting Stars, Newsletter Signup with Benefit List.
More animated backgrounds or all free UI components. Start for free to have a full site built around it.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.