Drifting Gradient
Three soft colour fields slide past each other behind a headline, so the background feels alive without pulling focus.
Quietly moving
Make a calm animated gradient background in CSS for a React hero. Inside a container with overflow hidden, place three absolutely positioned blurred circles (filter: blur(40px), 60–70% of the container size) in three brand colours at about 70% opacity. Give each its own keyframe that moves it with translate() between three positions over 12s, 15s and 18s, ease-in-out, infinite alternate, so the combination never visibly repeats. Animate transform only. Lay a fine noise or a 4% dark overlay on top to keep text contrast, and put the headline "Quietly moving" above everything. Under prefers-reduced-motion, stop the animation and keep the circles where they are.
Source
'use client'
import s from './demo.module.css'
export default function GradientDrift({ labels }: { labels: Record<string, string> }) {
return (
<div className={s.field}>
<span className={`${s.blob} ${s.a}`} aria-hidden="true" />
<span className={`${s.blob} ${s.b}`} aria-hidden="true" />
<span className={`${s.blob} ${s.c}`} aria-hidden="true" />
<p className={s.text}>{labels.text}</p>
</div>
)
}
.field {
--blob-a: oklch(66% 0.13 55);
--blob-b: oklch(72% 0.1 85);
--blob-c: oklch(48% 0.09 25);
position: absolute;
inset: 0;
display: grid;
place-items: center;
overflow: hidden;
background: var(--stage-bg);
}
.blob {
position: absolute;
width: 65%;
aspect-ratio: 1;
border-radius: 50%;
filter: blur(40px);
opacity: 0.7;
will-change: transform;
}
.a { background: var(--blob-a); top: -20%; left: -10%; animation: a 12s ease-in-out infinite alternate; }
.b { background: var(--blob-b); top: 20%; right: -20%; animation: b 15s ease-in-out infinite alternate; }
.c { background: var(--blob-c); bottom: -35%; left: 20%; animation: c 18s ease-in-out infinite alternate; }
@keyframes a { 50% { transform: translate(30%, 20%); } 100% { transform: translate(10%, 45%); } }
@keyframes b { 50% { transform: translate(-35%, 10%); } 100% { transform: translate(-15%, -30%); } }
@keyframes c { 50% { transform: translate(25%, -25%); } 100% { transform: translate(-20%, -10%); } }
.text {
position: relative;
margin: 0;
font: 700 var(--text-2xl) / 1.1 var(--font-display);
letter-spacing: -0.02em;
color: var(--stage-ink);
white-space: nowrap;
}
@media (prefers-reduced-motion: reduce) {
.blob { animation: none; }
}