Aurora UI: How to Build Gradient Aurora Effects in CSS & React
Learn how to build stunning aurora gradient effects in CSS and React — animated, performant, and actually usable in production UI in 2026.
What Aurora UI Actually Is
Aurora UI is a design style that pulls directly from the northern lights — slow-moving, layered gradients in deep teals, purples, greens, and blues that shift and blend over time. Not flashy. Not neon. It's moody, ambient, and when done right, it makes a dark-mode interface feel genuinely alive.
The aesthetic took off around 2023 but really hit critical mass in 2025 when tools like Figma started shipping aurora-inspired default themes and major SaaS companies — Linear, Vercel, Supabase — leaned hard into it for their marketing pages. If you've spent any time on those dashboards, you already know the vibe.
Honestly, what separates aurora UI from generic gradient abuse is restraint. You're not slapping a rainbow on a div. You're layering two or three translucent color blobs that breathe slowly behind your content. The motion should be almost imperceptible — more drifting than animating.
You can explore a curated set of pre-built aurora styles over at Empire UI's aurora collection if you want a reference point before you start rolling your own.
The Core CSS Pattern: Layered Radial Gradients
Every aurora effect starts with radial gradients. Not linear. Radial gives you those soft circular bloom shapes that you can stack, offset, and animate. The key technique is setting them on a pseudo-element or a dedicated background layer so you're not fighting with your actual content.
Here's the base pattern most people start with — a static version before any animation touches it:
.aurora-bg {
position: relative;
background: #050510;
overflow: hidden;
}
.aurora-bg::before {
content: '';
position: absolute;
inset: 0;
background:
radial-gradient(ellipse 80% 60% at 20% 50%, rgba(120, 80, 255, 0.35) 0%, transparent 70%),
radial-gradient(ellipse 60% 80% at 80% 30%, rgba(0, 200, 180, 0.3) 0%, transparent 70%),
radial-gradient(ellipse 70% 50% at 50% 90%, rgba(80, 120, 255, 0.25) 0%, transparent 70%);
filter: blur(40px);
z-index: 0;
}The blur(40px) is doing a lot of heavy lifting there. Drop it and the gradients look cheap. Worth noting: you want that blur on the layer itself, not on individual gradients — it's cheaper and gives a more unified look. Keep your content in a sibling element with position: relative; z-index: 1 so it sits above the aurora layer.
One more thing — the ellipse shape parameters matter a lot. Something like 80% 60% gives you a wide flat bloom. Narrow it to 40% 80% and you get tall vertical shafts of light. Mix both in the same background declaration for depth.
Adding Movement: CSS Keyframe Animation
Static aurora looks decent. Animated aurora looks *intentional*. The motion should be slow — we're talking 8–15 second cycles, not 1-second bounces. Think of it less as an animation and more as ambient breathing.
The cleanest approach is animating background-position or using a @keyframes that shifts the gradient positions via custom properties. Here's a production-ready version:
@keyframes aurora-shift {
0% { transform: translate(0px, 0px) scale(1); }
33% { transform: translate(30px, -20px) scale(1.05); }
66% { transform: translate(-20px, 15px) scale(0.97); }
100% { transform: translate(0px, 0px) scale(1); }
}
.aurora-bg::before {
/* ...same gradient as above... */
animation: aurora-shift 12s ease-in-out infinite;
will-change: transform;
}That will-change: transform is important for performance. You're telling the browser to promote this element to its own compositing layer, which keeps the animation on the GPU and off the main thread. On mobile, you might want to cut the blur down to 20px and the animation duration up to 20s — subtlety wins on smaller screens.
In practice, adding a second ::after pseudo-element with a slightly different gradient set and a longer animation duration (say, 18s) creates a much richer parallax feel. The two layers drift at different speeds, which is exactly what you see in real aurora footage.
Building a Reusable React Component
CSS-only is fine for static pages. But if you're working in React, you want a component you can drop anywhere without copying gradient strings into every file. Here's a minimal AuroraBackground component that takes children and a className prop:
// AuroraBackground.jsx
import React from 'react';
import './aurora.css'; // the CSS from above
export function AuroraBackground({ children, className = '' }) {
return (
<div className={`aurora-bg ${className}`}>
<div className="aurora-layer" />
<div className="aurora-content">
{children}
</div>
</div>
);
}/* aurora.css */
.aurora-bg {
position: relative;
background: #050510;
overflow: hidden;
}
.aurora-layer {
position: absolute;
inset: -20%;
background:
radial-gradient(ellipse 80% 60% at 20% 50%, rgba(120, 80, 255, 0.35) 0%, transparent 70%),
radial-gradient(ellipse 60% 80% at 80% 30%, rgba(0, 200, 180, 0.3) 0%, transparent 70%),
radial-gradient(ellipse 70% 50% at 50% 90%, rgba(80, 120, 255, 0.25) 0%, transparent 70%);
filter: blur(40px);
animation: aurora-shift 12s ease-in-out infinite;
will-change: transform;
}
.aurora-content {
position: relative;
z-index: 1;
}
@keyframes aurora-shift {
0% { transform: translate(0px, 0px) scale(1); }
33% { transform: translate(30px, -20px) scale(1.05); }
66% { transform: translate(-20px, 15px) scale(0.97); }
100% { transform: translate(0px, 0px) scale(1); }
}The inset: -20% on .aurora-layer is a trick to prevent the gradient edges from being visible when the element translates. Without it, you'll see the background color peeking in on the edges during animation. That 20% overflow acts as a buffer.
Quick aside: if you're using Tailwind, you can skip the CSS file entirely and put the gradient in an style prop, using Tailwind for layout. But the keyframe animation still needs a real CSS file or a <style> tag — Tailwind's JIT doesn't generate arbitrary keyframes.
Color Palette and Combinations That Actually Work
The classic aurora palette is purple-to-teal. But you've got a lot of room to move. Here are three combinations that work well in production without looking like a rejected album cover from 2014:
Deep Space — rgba(120, 80, 255, 0.35) purple + rgba(0, 200, 180, 0.3) teal. The one you've seen everywhere. Works best on near-black backgrounds (#050510 or similar).
Sunset Dusk — rgba(255, 80, 150, 0.3) pink + rgba(255, 140, 60, 0.25) amber. Warmer, more editorial. Great for landing pages that want energy without being aggressive.
Arctic — rgba(60, 180, 255, 0.3) ice blue + rgba(100, 240, 200, 0.25) mint. Minimal and clean. Pairs well with white typography and lots of whitespace.
Look, the mistake most devs make is cranking opacity too high — anything above 0.4 on a single gradient starts to feel synthetic. Keep each layer at 0.25–0.35 and let the blur do the blending. Also check out the gradient generator tool to experiment with color stops before committing them to code.
Performance Considerations You Can't Skip
Aurora effects are GPU-friendly when done right, but they can absolutely destroy your Core Web Vitals if you're careless. The main traps: animating filter: blur() directly (causes layout recalculation on every frame), using too many gradient layers on mobile, and forgetting prefers-reduced-motion.
Always wrap your animation in a prefers-reduced-motion media query. Some users have vestibular disorders and moving backgrounds can be genuinely disorienting. It's not a nice-to-have:
@media (prefers-reduced-motion: reduce) {
.aurora-layer {
animation: none;
}
}That said, the static version still looks great — you're not degrading the experience, just removing the motion. Worth noting: browsers since Chrome 88 handle will-change: transform + CSS animations on pseudo-elements pretty well, but if you're targeting older Android WebViews, test there specifically. The filter: blur() can still cause jank on low-end devices even with GPU compositing.
If you need maximum performance, consider replacing the CSS blur with an SVG feGaussianBlur filter defined once in a <defs> block. It sounds weird but it's faster in some browsers because the GPU path is different. Benchmark it — don't just assume.
When to Use Aurora vs Other UI Styles
Aurora works beautifully for dark-mode SaaS dashboards, AI product landing pages, developer tools, and anything where you want to signal "premium" without going full glassmorphism. It doesn't work well for content-heavy pages (the background competes with text), light-mode interfaces, or anything with lots of small UI elements where the gradient creates visual noise.
If you want glass cards floating over aurora, that's a natural pairing. The glassmorphism components in Empire UI are specifically designed to layer over dark gradient backgrounds — they use backdrop-filter: blur() which reads from whatever's behind, so the aurora colors bleed through the glass naturally.
Compare that to neumorphism or neobrutalism, which both rely on flat, single-color backgrounds. You can't really mix those with aurora without the whole thing collapsing. Aurora wants darkness and depth. The other styles want flatness and clarity. Pick your lane.
If you want a quick comparison before committing to a style direction, browse the components and flip between the aurora, glassmorphism, and cyberpunk sections side by side. Seeing them at full scale tells you more than any description.
FAQ
Yes, but only partially. You can define the radial gradients using Tailwind's style prop or arbitrary values, but the keyframe animation still needs a real CSS file or a global stylesheet. Tailwind's JIT doesn't generate custom keyframes.
Not if you use will-change: transform and animate only transform properties. Avoid animating filter or opacity inside the keyframes themselves — that triggers paint operations. Keep the blur static and animate the transform only.
Near-black works best — something between #04040e and #0a0a1a. Pure #000000 tends to make the gradients look painted on rather than luminous. The slight blue-dark tint in the base color makes the purples and teals pop more naturally.
Technically yes, but it rarely looks good. Aurora relies on dark backgrounds to make the gradient luminosity readable. On white or light-gray backgrounds the effect just looks like a faint pastel smudge — most designers who try it end up switching to a softer approach like the gradient wash patterns from the aurora page.