Fields of violet, blue and cyan light drift and blend under swaying aurora ribbons.
Northern lights
Customize
Usage
import { AuroraMesh } from "@/components/motionkit/aurora-mesh";
<AuroraMesh
className="h-[32rem] rounded-3xl"
>
<h1>Your hero</h1>
</AuroraMesh>Motion
The exact curves this component runs, plotted with Motion's own maths.
Field drift
Runs on its own
Ribbon sway
Runs on its own
Works in the App Router as is. Requires Tailwind CSS v4.
npm install motionSave as components/motionkit/aurora-mesh.tsx.
"use client";
import type { ReactNode } from "react";
import { motion, useReducedMotion } from "motion/react";
type AuroraMeshProps = {
children?: ReactNode;
/** Two to five light colours. The first two also tint the ribbons. */
colors?: string[];
/** Playback speed. 1 is a slow drift; 2 is twice as fast. */
speed?: number;
/** Strength of the light, 0 to 1. */
intensity?: number;
/** Softness in pixels. Higher blends the fields into one another. */
blur?: number;
className?: string;
};
// each field wanders its own loop, at its own pace, so the mesh never visibly repeats
const fields = [
{ left: "-10%", top: "-20%", size: "70%", x: ["0%", "18%", "6%", "0%"], y: ["0%", "12%", "24%", "0%"], rotate: [0, 40, 90], scale: [1, 1.15, 0.95, 1], seconds: 22 },
{ left: "40%", top: "-10%", size: "65%", x: ["0%", "-20%", "-8%", "0%"], y: ["0%", "18%", "6%", "0%"], rotate: [0, -50, -100], scale: [1, 0.9, 1.1, 1], seconds: 26 },
{ left: "10%", top: "35%", size: "60%", x: ["0%", "22%", "-6%", "0%"], y: ["0%", "-14%", "-4%", "0%"], rotate: [0, 60, 120], scale: [1, 1.2, 1, 1], seconds: 30 },
{ left: "55%", top: "40%", size: "55%", x: ["0%", "-16%", "10%", "0%"], y: ["0%", "-20%", "-8%", "0%"], rotate: [0, -30, -80], scale: [1, 1.05, 0.9, 1], seconds: 34 },
{ left: "25%", top: "10%", size: "45%", x: ["0%", "12%", "-14%", "0%"], y: ["0%", "16%", "-10%", "0%"], rotate: [0, 80, 160], scale: [1, 0.85, 1.1, 1], seconds: 38 },
];
export function AuroraMesh({
children,
colors = ["#7C3AED", "#A855F7", "#3B82F6", "#22D3EE"],
speed = 1,
intensity = 0.7,
blur = 80,
className = "",
}: AuroraMeshProps) {
const reduceMotion = useReducedMotion();
const pace = Math.max(speed, 0.05);
const palette = colors.slice(0, fields.length);
return (
<div className={`relative isolate overflow-hidden bg-[#0B0A12] ${className}`}>
{/* one blur over the whole layer is far cheaper than blurring every field, and blends them together */}
<div aria-hidden className="absolute inset-0 -z-10" style={{ filter: `blur(${blur}px)`, opacity: intensity }}>
{palette.map((color, index) => {
const field = fields[index];
return (
<motion.div
key={index}
className="absolute rounded-[45%] mix-blend-screen"
style={{
left: field.left,
top: field.top,
width: field.size,
height: field.size,
background: `radial-gradient(ellipse at center, ${color}, transparent 68%)`,
}}
animate={reduceMotion ? undefined : { x: field.x, y: field.y, rotate: field.rotate, scale: field.scale }}
transition={{ duration: field.seconds / pace, repeat: Infinity, repeatType: "mirror", ease: "easeInOut" }}
/>
);
})}
{/* the aurora ribbons: tall, skewed curtains of light that sway slowly from side to side */}
{[0, 1].map((index) => (
<motion.div
key={`ribbon-${index}`}
className="absolute top-[-20%] h-[90%] w-[28%] mix-blend-screen"
style={{
left: index ? "52%" : "18%",
background: `linear-gradient(to bottom, transparent, ${palette[index] ?? palette[0]} 45%, transparent)`,
skewX: index ? 18 : -18,
opacity: 0.55,
}}
animate={reduceMotion ? undefined : { x: index ? ["0%", "-40%", "0%"] : ["0%", "45%", "0%"], scaleY: [1, 1.15, 1] }}
transition={{ duration: (index ? 19 : 16) / pace, repeat: Infinity, ease: "easeInOut" }}
/>
))}
</div>
{/* a soft vignette keeps content readable in the middle and the edges calm */}
<div aria-hidden className="absolute inset-0 -z-10 bg-[radial-gradient(ellipse_at_center,transparent_30%,rgb(11_10_18/0.75))]" />
{children}
</div>
);
}
| Prop | Type | Default | Description |
|---|---|---|---|
| colors | string[] | ["#7C3AED", "#A855F7", "#3B82F6", "#22D3EE"] | Two to five light colours. |
| speed | number | 1 | Playback speed multiplier. |
| intensity | number | 0.7 | Strength of the light, 0 to 1. |
| blur | number | 80 | Softness in pixels. |
| children | ReactNode | — | Your content, layered above. |
Ambient by default
Aurora Background
Drifting, blurred color fields for hero sections and empty states.
Warm, textured, alive
Grain Gradient
Slow, drifting colour with a fine film grain, for warm editorial heroes.
Look up
Star Field
A slow-drifting night sky with twinkling stars, for space, launch and AI product heroes.