---
name: fluid-gradient
description: Use when you want "Elegant, modern, creative" - Creates organic flowing gradient movement.
---

# Fluid Gradient

> **Category:** Background  -  **Personality:** Elegant, modern, creative
>
> **Best use:** Landing pages, creative apps
>
> **Ratings:** Professional 4/5 - Casual 4/5 - Exotic 4/5 - Visual impact 5/5 - Difficulty 4/5 - Performance cost 4/5

## What it is

A Fluid Gradient is a backdrop where several soft, blurred blobs of colour slowly **drift, scale, rotate AND change shape** while overlapping — so the colour regions look like they are continuously flowing into one another, like liquid or ink in water. Unlike a *static* mesh gradient (fixed colour stops), every blob is independently animated and the blobs are composited with `mix-blend-mode: screen`, so wherever two collide they mix into a brand-new hue that is always shifting. It is pure CSS — no canvas, no WebGL — which keeps it lightweight while feeling alive, making it a favourite for creative landing pages and design-forward apps.

## Dependencies / CDN

None — pure CSS animation. The demo only loads display/body fonts (optional):

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Spectral:ital,wght@0,500;0,600;0,700;0,800;1,600&display=swap" rel="stylesheet">
```

## HTML

A stage that clips the effect, a field of blobs, a grain + scrim for polish/legibility, then your content on top:

```html
<div class="stage">
  <div class="field" aria-hidden="true">
    <span class="blob b1"></span><span class="blob b2"></span><span class="blob b3"></span>
    <span class="blob b4"></span><span class="blob b5"></span>
  </div>
  <div class="grain" aria-hidden="true"></div>
  <div class="scrim" aria-hidden="true"></div>
  <div class="content">
    <h2>Identities that <em class="accent">flow</em>, never freeze.</h2>
    <p>Living, fluid identities for companies that refuse to stand still.</p>
  </div>
</div>
```

## CSS

```css
/* 1) Stage — clips the effect and provides the dark base the colours glow against */
.stage{position:relative; overflow:hidden; border-radius:28px; min-height:620px;
  isolation:isolate;                 /* contain the blend modes inside the stage */
  background:radial-gradient(130% 120% at 50% 16%,#0c0820,#070410 68%,#040208);}

/* 2) The flowing field of blobs */
.field{position:absolute; inset:-10%; z-index:0; transform:translateZ(0);} /* bleed past edges */
.blob{position:absolute; mix-blend-mode:screen;  /* overlaps ADD to new hues */
  opacity:.82; filter:blur(56px); will-change:transform,border-radius;}

.b1{width:62%;height:62%;top:-14%;left:-10%;background:#ff3d7f;
  border-radius:42% 58% 63% 37%/41% 44% 56% 59%;animation:m1 24s ease-in-out infinite}
.b2{width:68%;height:68%;bottom:-18%;right:-12%;background:#7c5cff;
  border-radius:60% 40% 38% 62%/63% 38% 62% 37%;animation:m2 31s ease-in-out infinite -6s}
.b3{width:52%;height:52%;top:-8%;right:0;background:#1fd1c6;animation:m3 27s ease-in-out infinite -12s}
.b4{width:50%;height:50%;bottom:-14%;left:4%;background:#ff9d3d;animation:m4 34s ease-in-out infinite -3s}
.b5{width:46%;height:46%;bottom:-20%;left:33%;background:#d24bff;animation:m5 21s ease-in-out infinite -9s}

/* 3) Each blob translates, rotates, scales AND morphs its border-radius (= the "liquid" feel) */
@keyframes m1{
  0%,100%{transform:translate(0,0) rotate(0deg) scale(1);   border-radius:42% 58% 63% 37%/41% 44% 56% 59%}
  33%    {transform:translate(18%,14%) rotate(45deg) scale(1.15);border-radius:62% 38% 44% 56%/58% 44% 56% 42%}
  66%    {transform:translate(8%,26%) rotate(-28deg) scale(.9); border-radius:36% 64% 58% 42%/52% 62% 38% 48%}}
/* m2…m5 follow the same shape — different directions, speeds and negative delays so nothing syncs */

/* 4) Optional polish: film grain (kills banding) + a soft scrim so text stays readable */
.grain{position:absolute;inset:0;z-index:1;pointer-events:none;opacity:.13;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}
.scrim{position:absolute;inset:0;z-index:2;pointer-events:none;
  background:linear-gradient(180deg,rgba(6,3,14,.34),rgba(6,3,14,.06) 34%,rgba(6,3,14,.10) 64%,rgba(6,3,14,.50))}
.content{position:relative;z-index:3}

/* 5) Bonus — make the accent word "flow" too, by panning a gradient through clipped text */
.accent{font-style:italic;
  background:linear-gradient(105deg,#ff7eb3,#d8a6ff 30%,#86f5e9 60%,#ffb877 90%);
  background-size:230% 100%;-webkit-background-clip:text;background-clip:text;
  color:transparent;-webkit-text-fill-color:transparent;animation:textflow 9s ease-in-out infinite}
@keyframes textflow{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}

/* 6) Reduced motion = a frozen, still-pretty frame */
@media (prefers-reduced-motion:reduce){
  .blob,.accent{animation:none!important}
  .accent{background-position:0% 50%}
}
```

## JavaScript

Not required — the effect is 100% CSS. The demo adds one optional flourish: nudge the whole field a few px toward the pointer for parallax (gated behind reduced-motion, throttled with `requestAnimationFrame`):

```js
(function(){
  var stage=document.querySelector('.stage'), field=document.querySelector('.field');
  if(!stage||!field) return;
  if(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches) return;
  var raf=0,px=0,py=0;
  function apply(){raf=0; field.style.transform='translate3d('+px.toFixed(1)+'px,'+py.toFixed(1)+'px,0)';}
  stage.addEventListener('pointermove',function(e){
    var r=stage.getBoundingClientRect();
    px=((e.clientX-r.left)/r.width-.5)*30;
    py=((e.clientY-r.top)/r.height-.5)*30;
    if(!raf) raf=requestAnimationFrame(apply);
  });
  stage.addEventListener('pointerleave',function(){ field.style.transform='translate3d(0,0,0)'; });
})();
```

## How it works

- **Blur turns hard circles into soft colour clouds.** `filter: blur(56px)` on each blob removes every edge, so they read as gradient regions rather than shapes.
- **`mix-blend-mode: screen` is what makes it "fluid".** Screen is additive over a dark base, so where two blobs overlap their colours combine into a *third* colour. As the blobs drift through each other, those mixed regions are constantly born and dissolved — that perpetual re-mixing is the flow.
- **Animating `border-radius` morphs the silhouette.** Each keyframe sets a different 8-value radius (`42% 58% 63% 37% / 41% 44% 56% 59%`), so the blob wobbles and stretches like liquid instead of just sliding around — the key difference from a static mesh.
- **Desync everything.** Different `width/height`, durations (21s–34s), directions and **negative delays** mean the blobs never line up, so the loop never looks like a loop.
- **`isolation: isolate`** on the stage keeps the blend modes from leaking onto the rest of the page; **`translateZ(0)`** on the field promotes it to its own GPU layer for smooth compositing.

## Customization

- **Mood / palette:** swap the five blob `background` colours. Analogous hues (e.g. all blues→purples) feel calm and corporate; complementary hues (rose + teal + amber) feel vivid and creative.
- **Softness:** larger `blur()` and bigger blob `width/height` = dreamier, more diffuse; smaller/sharper = more distinct "lava" lobes.
- **Energy:** shorten the animation durations and increase the `translate`/`scale` ranges for a livelier flow; lengthen them for a slow, premium drift.
- **Contrast over the gradient:** tune the `.scrim` alpha, or drop it and rely on `text-shadow`. For glass cards on top, this pairs perfectly with `backdrop-filter: blur()`.
- **Intensity of mixing:** `mix-blend-mode: plus-lighter` mixes brighter than `screen`; `lighten` keeps colours more separate.

## Accessibility & performance

- **Honour `prefers-reduced-motion`.** Set `animation:none` so the blobs rest at their (deliberately spread-out) start positions — a clean static gradient — and bail out of the JS parallax via `matchMedia`.
- **Cost is GPU compositing + blur,** not layout. Keep it cheap: animate only `transform`/`border-radius` (never `width`/`top`/`left`), keep blob count modest (~5), and never animate the `blur` radius. The demo also shrinks the blur on small screens.
- **Legibility:** the gradient under live text shifts, so guarantee contrast with a scrim and/or `text-shadow`; don't rely on a lucky moment in the animation.
- **`aria-hidden="true"`** on the field/grain/scrim — they are pure decoration and must not reach the accessibility tree.

## Gotchas

- **Nothing happens without `mix-blend-mode`** plus a **dark base** behind it — `screen` over black glows; `screen` over white washes out to nothing.
- **`border-radius` only morphs a filled box.** Use a solid `background` colour (the demo does); a `radial-gradient(...,transparent)` fill fades before the box edge, so the morphing radius becomes invisible.
- **Set `overflow:hidden` AND `isolation:isolate` on the stage,** or the blurred blobs spill past the rounded corners and the blend modes bleed onto siblings.
- **An ancestor `filter` or `transform` creates a new containing block** and can quietly re-scope the blending — keep the blobs and their stage as direct, sibling-level structure.
- **8-value `border-radius` is two slash-separated sets** (horizontal radii `/` vertical radii). Every keyframe must supply all eight values or the morph snaps instead of tweening.
