Mesh Gradients in CSS: From Basic to Advanced in 10 Minutes
Mesh gradients are taking over UI design in 2026. Here's how to build them in pure CSS, React, and SVG — no Figma export required.
What Even Is a Mesh Gradient?
A mesh gradient isn't a single linear or radial sweep — it's multiple color blobs layered on top of each other, each with its own position, radius, and opacity. The result looks organic. Almost painted. It's the aesthetic behind those blurry colorful backgrounds you've been seeing everywhere since roughly 2023, and it hasn't slowed down.
Technically, CSS doesn't have a native mesh-gradient() function. So when devs talk about mesh gradients in CSS, they mean a composition of multiple radial-gradient() or radial blobs built with filter: blur() on absolutely-positioned divs. Two very different approaches, both totally valid.
In practice, the filter-blur method gives you the most control and is the one worth learning first. The radial-gradient stacking method is cleaner markup-wise but harder to finesse. We'll cover both.
The Quickest Possible Mesh Gradient (Pure CSS)
Stack three or four radial gradients on a single element. That's it. Each one contributes a color blob, and where they overlap, the browser blends them automatically.
Here's a minimal working example you can drop into any project right now:
.mesh-bg {
background-color: #0f0c29;
background-image:
radial-gradient(ellipse at 20% 30%, rgba(120, 80, 255, 0.6) 0%, transparent 50%),
radial-gradient(ellipse at 80% 10%, rgba(255, 80, 180, 0.5) 0%, transparent 45%),
radial-gradient(ellipse at 60% 80%, rgba(0, 200, 255, 0.4) 0%, transparent 55%),
radial-gradient(ellipse at 10% 90%, rgba(255, 160, 50, 0.4) 0%, transparent 40%);
width: 100%;
height: 100vh;
}Those percentage values in ellipse at X% Y% are where your blobs sit. Nudge them and watch the whole thing shift. Worth noting: adding a fifth or sixth blob rarely improves things — it usually just muddies the result. Three to four blobs is the sweet spot for readability.
Honestly, this is all most projects need. Save the fancy stuff for when you actually need it.
The Blur-Blob Method (More Control, More Flex)
If you want blobs that animate, interact, or overlap in ways the multi-gradient stacking can't pull off, the filter: blur() approach is the move. The idea is simple: absolutely position colored divs, crank the blur up to something like blur(80px) or blur(120px), and parent them inside a container with overflow: hidden.
export function MeshBackground() {
return (
<div className="relative w-full h-screen overflow-hidden bg-[#0d0d1a]">
<div
className="absolute top-[10%] left-[15%] w-[400px] h-[400px] rounded-full
bg-purple-500/50 blur-[120px]"
/>
<div
className="absolute top-[50%] left-[60%] w-[350px] h-[350px] rounded-full
bg-pink-500/40 blur-[100px]"
/>
<div
className="absolute top-[70%] left-[30%] w-[300px] h-[300px] rounded-full
bg-cyan-400/40 blur-[90px]"
/>
<div className="relative z-10">{/* your content here */}</div>
</div>
);
}Quick aside: that z-10 on the content wrapper matters. Without it, your text will render underneath the blobs in some browsers and you'll spend 20 minutes debugging what looks like a visibility issue but is actually a stacking context problem.
The blur values here — 90px to 120px — aren't arbitrary. Below about 60px the blobs start looking like smeared circles rather than a true mesh. Above 150px and you lose the color saturation. That 80–120px range is where it lives.
Animating Your Mesh Gradient
Static mesh backgrounds are fine. Animated ones are memorable. The simplest animation: move the blob positions on a loop using CSS keyframes or Framer Motion's animate prop.
@keyframes float-blob {
0%, 100% { transform: translate(0, 0) scale(1); }
33% { transform: translate(30px, -20px) scale(1.05); }
66% { transform: translate(-20px, 15px) scale(0.95); }
}
.blob {
animation: float-blob 8s ease-in-out infinite;
}
.blob:nth-child(2) {
animation-delay: -2.5s;
animation-duration: 10s;
}
.blob:nth-child(3) {
animation-delay: -5s;
animation-duration: 12s;
}Staggering the durations and delays is what prevents the synchronized bobbing that makes mesh animations look cheap. Each blob should feel like it's doing its own thing. That said, don't go overboard with speed — anything under 6s per cycle starts feeling frantic rather than ambient.
One more thing — if you're on a performance budget, prefer CSS animations over JS-driven ones here. CSS animations run on the compositor thread and won't block your main thread. A mesh gradient animating at 60fps with pure CSS is essentially free.
SVG feBlend: The Nuclear Option
If you want true mesh gradient behaviour — the kind where colors blend through luminosity or multiply modes, not just alpha transparency — SVG filters are your friend. It's more verbose, but it unlocks effects you literally cannot reproduce with CSS alone.
<svg width="0" height="0" style="position:absolute">
<defs>
<filter id="mesh-blur">
<feGaussianBlur stdDeviation="40" result="blur" />
<feColorMatrix type="saturate" values="3" />
</filter>
</defs>
</svg>
<div class="mesh-container" style="filter: url(#mesh-blur) contrast(1.2)">
<div class="blob blob-1"></div>
<div class="blob blob-2"></div>
<div class="blob blob-3"></div>
</div>The contrast() on the outer container combined with feGaussianBlur creates a gooey blob-merge effect. It's the same technique behind liquid metal and lava lamp UIs. Crank the contrast value past 3 and the blobs start eating each other, which is either cool or horrible depending on your color choices.
Look, most projects don't need SVG filters. But if you're building something where the background IS the feature — a landing page hero, a generative art piece, a vaporwave-inspired layout — this is what separates a polished result from a tutorial clone. The gradient generator at Empire UI handles a lot of this complexity for you if you'd rather not hand-code it.
Pairing Mesh Gradients With UI Styles
Mesh gradients don't exist in a vacuum — they work as a foundation for other effects. The most common pairing is with glassmorphism: put a mesh gradient behind a frosted-glass card and you get that depth-with-color combo that's been dominating SaaS dashboards since 2024. Check out the glassmorphism components to see how the two styles interact in practice.
Dark mesh backgrounds also work well under neumorphic elements if you desaturate the blobs heavily — around 20–30% saturation keeps things calm enough that the subtle neumorphic shadows don't fight the background noise. Bright, saturated mesh under neumorphism is a disaster.
For tools-based workflows, you can generate a base CSS gradient with the glassmorphism generator, grab the backdrop-filter values, then layer your mesh underneath it. The generator outputs ready-to-paste CSS, so the workflow is: generate the glass effect, write the mesh behind it, done in under five minutes.
Worth noting: mesh gradients on white backgrounds need a softer touch. Reduce opacity on each blob to around 25–35% and keep the hues analogous rather than complementary. Contrasting hues on white create a garishness that screams "2005 desktop wallpaper" rather than "2026 product design."
Performance and Accessibility Considerations
Blur filters are GPU-accelerated in modern browsers, but they're not free. Animating three blobs with blur(100px) on a 4K display can hit GPU memory limits on lower-end hardware. The fix is simple: cap your container size and use will-change: transform on each animated blob to hint the browser to promote them to their own compositor layer.
For accessibility, always check that text placed over a mesh gradient meets WCAG 2.1 AA contrast requirements — that's at least 4.5:1 for normal text, 3:1 for large text. The shifting colors mean one spot on the gradient might pass contrast while another fails. Test with both the lightest and darkest regions of the mesh.
Respect prefers-reduced-motion. Wrap your keyframe animations in a media query so users who've opted out of motion don't get perpetually floating blobs in their peripheral vision while trying to read your content. It's a two-line addition and it matters.
@media (prefers-reduced-motion: reduce) {
.blob {
animation: none;
}
}FAQ
Mostly, yes. The blur-blob technique maps directly to Tailwind utility classes like blur-[120px], bg-purple-500/50, and absolute positioning. For multi-radial-gradient stacking you'll need a bg-[image:...] arbitrary value or a brief custom CSS rule.
They can, especially when animated on large viewports. Use will-change: transform on animated blobs, keep blur values under 130px, and always wrap animations in prefers-reduced-motion guards to avoid hammering GPU on low-end devices.
A radial gradient is one color transition from a single origin point. A mesh gradient layers multiple overlapping blobs with independent positions and blending — the organic, multi-color result is what makes it distinct.
Not yet as of mid-2026. There's been informal discussion in the CSS working group but no active spec proposal. For now, the multi-radial or blur-blob approach is the standard workaround.