CSS can't interpolate gradient colours directly — transition: background has no effect on gradients. Here are three practical techniques for animating CSS gradients.
Why Transitions Don't Work on Gradients
background: linear-gradient(...) is treated as an image value by CSS, not a colour. You can't transition between two image values, so the browser just snaps. Here are the workarounds.
Technique 1: Background-Position Shift (Pure CSS)
Make the gradient oversized and animate its position:
``css
.animated {
background: linear-gradient(270deg, #6c63ff, #f43f8a, #68d391, #6c63ff);
background-size: 400% 400%;
animation: shift 6s ease infinite;
}
@keyframes shift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
`
The gradient is 4× the element width. Animating the position creates a smooth looping effect. Duplicate the start colour at the end for seamless looping.
Good for: Hero sections, button hovers, card backgrounds.
Technique 2: Pseudo-Element Crossfade
Layer two gradients on pseudo-elements and fade between them:
`css
.crossfade { position: relative; }
.crossfade::before,
.crossfade::after {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
}
.crossfade::before {
background: linear-gradient(135deg, #6c63ff, #f43f8a);
}
.crossfade::after {
background: linear-gradient(135deg, #f43f8a, #68d391);
animation: fade 3s ease-in-out infinite alternate;
}
@keyframes fade {
0% { opacity: 0; }
100% { opacity: 1; }
}
`
This looks like a true colour transition. No JavaScript needed, but uses both pseudo-elements.
Technique 3: CSS Custom Properties + JavaScript
For full control, use CSS variables and update them each frame:
<code>css
<p>.dynamic {</p>
<p>background: linear-gradient(</p>
<p>var(--angle, 135deg),</p>
<p>var(--c1, #6c63ff),</p>
<p>var(--c2, #f43f8a)</p>
<p>);</p>
<p>}</p>
</code>
`javascript
let hue = 0;
const el = document.querySelector('.dynamic');
function tick() {
hue = (hue + 0.5) % 360;
el.style.setProperty('--c1', hsl(${hue}, 70%, 60%));
el.style.setProperty('--c2', hsl(${(hue + 120) % 360}, 70%, 60%));
requestAnimationFrame(tick);
}
tick();
`
This gives you complete control over all gradient parameters every frame. Requires JavaScript but works in all modern browsers.
Animating the Angle (Houdini)
To animate the gradient angle with pure CSS, register the custom property:
`css
@property --angle {
syntax: "";
initial-value: 0deg;
inherits: false;
}
.rotating {
background: conic-gradient(from var(--angle), #6c63ff, #f43f8a, #6c63ff);
animation: spin 4s linear infinite;
}
@keyframes spin {
to { --angle: 360deg; }
}
`
Without @property, the animation jumps instead of rotating smoothly. Currently supported in Chrome and Edge only.
Performance Tips
Gradient animations trigger repaints. Keep them fast:
- Use will-change: background` on animated elements
- Limit to a few animated elements per page
- Prefer Technique 1 for simple loops — it's the most GPU-friendly
Start with the gradient CSS from the CSS Gradient Generator at SnappyTools, then wrap it in the animation code above.