16 CSS Gradient Animations 09 / 16
CSS Animated Infinite Gradient Text
Multiple gradient text variants — a hero headline, sentence-level accents, animated stat counters, and a badge tag — all using background-clip: text with a sliding background-position keyframe to create a continuously flowing colour palette across the lettering.
The code
<div class="ga-09">
<div class="ga-09__tag-display">
<span class="ga-09__tag-icon">✦</span>
<span class="ga-09__tag-text ga-09__text">CSS background-clip gradient animation</span>
</div>
<div class="ga-09__hero">
<span class="ga-09__pre">Build the future</span>
<span class="ga-09__gradient-a ga-09__text">one pixel at a time.</span>
</div>
<p class="ga-09__sentence">
Gradient text is great for
<span class="ga-09__accent-b ga-09__text">hero headlines</span>,
key statistics, and
<span class="ga-09__accent-b ga-09__text" style="background-image:linear-gradient(90deg,#f97316,#eab308,#10b981,#f97316)">call-to-action</span>
words that need to pop without colour conflicts.
</p>
<div class="ga-09__stats">
<div class="ga-09__stat">
<span class="ga-09__stat-num ga-09__stat-c1 ga-09__text">4.2M</span>
<span class="ga-09__stat-label">Monthly users</span>
</div>
<div class="ga-09__stat">
<span class="ga-09__stat-num ga-09__stat-c2 ga-09__text">99.98%</span>
<span class="ga-09__stat-label">Uptime SLA</span>
</div>
<div class="ga-09__stat">
<span class="ga-09__stat-num ga-09__stat-c3 ga-09__text">18ms</span>
<span class="ga-09__stat-label">Avg. latency</span>
</div>
</div>
<div class="ga-09__controls">
<button class="ga-09__ctrl-btn" data-dur="12s">Slow</button>
<button class="ga-09__ctrl-btn active" data-dur="5s">Normal</button>
<button class="ga-09__ctrl-btn" data-dur="2.5s">Fast</button>
<button class="ga-09__ctrl-btn" data-dur="1s">Blazing</button>
</div>
</div> <div class="ga-09">
<div class="ga-09__tag-display">
<span class="ga-09__tag-icon">✦</span>
<span class="ga-09__tag-text ga-09__text">CSS background-clip gradient animation</span>
</div>
<div class="ga-09__hero">
<span class="ga-09__pre">Build the future</span>
<span class="ga-09__gradient-a ga-09__text">one pixel at a time.</span>
</div>
<p class="ga-09__sentence">
Gradient text is great for
<span class="ga-09__accent-b ga-09__text">hero headlines</span>,
key statistics, and
<span class="ga-09__accent-b ga-09__text" style="background-image:linear-gradient(90deg,#f97316,#eab308,#10b981,#f97316)">call-to-action</span>
words that need to pop without colour conflicts.
</p>
<div class="ga-09__stats">
<div class="ga-09__stat">
<span class="ga-09__stat-num ga-09__stat-c1 ga-09__text">4.2M</span>
<span class="ga-09__stat-label">Monthly users</span>
</div>
<div class="ga-09__stat">
<span class="ga-09__stat-num ga-09__stat-c2 ga-09__text">99.98%</span>
<span class="ga-09__stat-label">Uptime SLA</span>
</div>
<div class="ga-09__stat">
<span class="ga-09__stat-num ga-09__stat-c3 ga-09__text">18ms</span>
<span class="ga-09__stat-label">Avg. latency</span>
</div>
</div>
<div class="ga-09__controls">
<button class="ga-09__ctrl-btn" data-dur="12s">Slow</button>
<button class="ga-09__ctrl-btn active" data-dur="5s">Normal</button>
<button class="ga-09__ctrl-btn" data-dur="2.5s">Fast</button>
<button class="ga-09__ctrl-btn" data-dur="1s">Blazing</button>
</div>
</div>.ga-09, .ga-09 *, .ga-09 *::before, .ga-09 *::after {
margin: 0; padding: 0; box-sizing: border-box;
}
.ga-09 ::selection { background: rgba(168,85,247,.4); color: #fff; }
.ga-09 {
--bg: #060912;
--dur: 5s;
width: 100%;
min-height: 100vh;
background: var(--bg);
font-family: system-ui, -apple-system, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 48px;
padding: 48px 24px;
}
/* ── Core gradient text technique ── */
.ga-09__text {
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
background-size: 300% 100%;
animation: ga-09-slide var(--dur) linear infinite;
}
@keyframes ga-09-slide {
0% { background-position: 0% 50%; }
100% { background-position: -200% 50%; }
}
/* ── Variant A: hero headline ── */
.ga-09__hero {
text-align: center;
display: flex;
flex-direction: column;
gap: 8px;
}
.ga-09__pre {
font-size: clamp(2.4rem, 6vw, 4rem);
font-weight: 900;
line-height: 1.08;
letter-spacing: -.035em;
color: rgba(255,255,255,.9);
}
.ga-09__gradient-a {
font-size: clamp(2.4rem, 6vw, 4rem);
font-weight: 900;
line-height: 1.08;
letter-spacing: -.035em;
background-image: linear-gradient(90deg,
#a855f7, #ec4899, #f97316, #eab308, #10b981, #06b6d4, #6366f1, #a855f7
);
}
.ga-09__gradient-a.ga-09__text { animation-duration: calc(var(--dur) * 1.4); }
/* ── Variant B: word-level accents in a sentence ── */
.ga-09__sentence {
font-size: clamp(1.15rem, 2.5vw, 1.5rem);
font-weight: 600;
color: rgba(255,255,255,.55);
line-height: 1.6;
text-align: center;
max-width: 580px;
}
.ga-09__accent-b {
font-weight: 800;
background-image: linear-gradient(90deg,
#06b6d4, #818cf8, #ec4899, #f97316, #06b6d4
);
display: inline;
}
.ga-09__accent-b.ga-09__text { animation-duration: calc(var(--dur) * .8); }
/* ── Variant C: display counters / stats ── */
.ga-09__stats {
display: flex;
gap: 40px;
flex-wrap: wrap;
justify-content: center;
}
.ga-09__stat {
text-align: center;
display: flex;
flex-direction: column;
gap: 4px;
}
.ga-09__stat-num {
font-size: clamp(2rem, 4vw, 2.8rem);
font-weight: 900;
letter-spacing: -.03em;
line-height: 1;
}
.ga-09__stat-c1 { background-image: linear-gradient(90deg, #06b6d4, #6366f1, #a855f7, #06b6d4); }
.ga-09__stat-c2 { background-image: linear-gradient(90deg, #10b981, #06b6d4, #818cf8, #10b981); animation-delay: -.8s; }
.ga-09__stat-c3 { background-image: linear-gradient(90deg, #f97316, #ec4899, #a855f7, #f97316); animation-delay: -1.6s; }
.ga-09__stat-label {
font-size: .7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .1em;
color: rgba(255,255,255,.3);
}
/* ── Variant D: large display tag ── */
.ga-09__tag-display {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 8px 20px;
border-radius: 999px;
border: 1px solid rgba(255,255,255,.08);
background: rgba(255,255,255,.03);
}
.ga-09__tag-icon { font-size: .9rem; }
.ga-09__tag-text {
font-size: .8rem;
font-weight: 700;
letter-spacing: .05em;
background-image: linear-gradient(90deg, #c084fc, #818cf8, #67e8f9, #34d399, #c084fc);
}
/* Speed palette buttons */
.ga-09__controls {
display: flex;
gap: 8px;
flex-wrap: wrap;
justify-content: center;
}
.ga-09__ctrl-btn {
padding: 5px 14px;
border-radius: 7px;
font-size: .73rem;
font-weight: 700;
letter-spacing: .05em;
text-transform: uppercase;
border: 1px solid rgba(255,255,255,.1);
background: rgba(255,255,255,.05);
color: rgba(255,255,255,.4);
cursor: pointer;
transition: all .2s;
}
.ga-09__ctrl-btn.active,
.ga-09__ctrl-btn:hover {
background: rgba(168,85,247,.15);
border-color: rgba(168,85,247,.3);
color: #d8b4fe;
}
@media (prefers-reduced-motion: reduce) {
.ga-09__text { animation: none; background-position: 0% 50%; }
} .ga-09, .ga-09 *, .ga-09 *::before, .ga-09 *::after {
margin: 0; padding: 0; box-sizing: border-box;
}
.ga-09 ::selection { background: rgba(168,85,247,.4); color: #fff; }
.ga-09 {
--bg: #060912;
--dur: 5s;
width: 100%;
min-height: 100vh;
background: var(--bg);
font-family: system-ui, -apple-system, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 48px;
padding: 48px 24px;
}
/* ── Core gradient text technique ── */
.ga-09__text {
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
background-size: 300% 100%;
animation: ga-09-slide var(--dur) linear infinite;
}
@keyframes ga-09-slide {
0% { background-position: 0% 50%; }
100% { background-position: -200% 50%; }
}
/* ── Variant A: hero headline ── */
.ga-09__hero {
text-align: center;
display: flex;
flex-direction: column;
gap: 8px;
}
.ga-09__pre {
font-size: clamp(2.4rem, 6vw, 4rem);
font-weight: 900;
line-height: 1.08;
letter-spacing: -.035em;
color: rgba(255,255,255,.9);
}
.ga-09__gradient-a {
font-size: clamp(2.4rem, 6vw, 4rem);
font-weight: 900;
line-height: 1.08;
letter-spacing: -.035em;
background-image: linear-gradient(90deg,
#a855f7, #ec4899, #f97316, #eab308, #10b981, #06b6d4, #6366f1, #a855f7
);
}
.ga-09__gradient-a.ga-09__text { animation-duration: calc(var(--dur) * 1.4); }
/* ── Variant B: word-level accents in a sentence ── */
.ga-09__sentence {
font-size: clamp(1.15rem, 2.5vw, 1.5rem);
font-weight: 600;
color: rgba(255,255,255,.55);
line-height: 1.6;
text-align: center;
max-width: 580px;
}
.ga-09__accent-b {
font-weight: 800;
background-image: linear-gradient(90deg,
#06b6d4, #818cf8, #ec4899, #f97316, #06b6d4
);
display: inline;
}
.ga-09__accent-b.ga-09__text { animation-duration: calc(var(--dur) * .8); }
/* ── Variant C: display counters / stats ── */
.ga-09__stats {
display: flex;
gap: 40px;
flex-wrap: wrap;
justify-content: center;
}
.ga-09__stat {
text-align: center;
display: flex;
flex-direction: column;
gap: 4px;
}
.ga-09__stat-num {
font-size: clamp(2rem, 4vw, 2.8rem);
font-weight: 900;
letter-spacing: -.03em;
line-height: 1;
}
.ga-09__stat-c1 { background-image: linear-gradient(90deg, #06b6d4, #6366f1, #a855f7, #06b6d4); }
.ga-09__stat-c2 { background-image: linear-gradient(90deg, #10b981, #06b6d4, #818cf8, #10b981); animation-delay: -.8s; }
.ga-09__stat-c3 { background-image: linear-gradient(90deg, #f97316, #ec4899, #a855f7, #f97316); animation-delay: -1.6s; }
.ga-09__stat-label {
font-size: .7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .1em;
color: rgba(255,255,255,.3);
}
/* ── Variant D: large display tag ── */
.ga-09__tag-display {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 8px 20px;
border-radius: 999px;
border: 1px solid rgba(255,255,255,.08);
background: rgba(255,255,255,.03);
}
.ga-09__tag-icon { font-size: .9rem; }
.ga-09__tag-text {
font-size: .8rem;
font-weight: 700;
letter-spacing: .05em;
background-image: linear-gradient(90deg, #c084fc, #818cf8, #67e8f9, #34d399, #c084fc);
}
/* Speed palette buttons */
.ga-09__controls {
display: flex;
gap: 8px;
flex-wrap: wrap;
justify-content: center;
}
.ga-09__ctrl-btn {
padding: 5px 14px;
border-radius: 7px;
font-size: .73rem;
font-weight: 700;
letter-spacing: .05em;
text-transform: uppercase;
border: 1px solid rgba(255,255,255,.1);
background: rgba(255,255,255,.05);
color: rgba(255,255,255,.4);
cursor: pointer;
transition: all .2s;
}
.ga-09__ctrl-btn.active,
.ga-09__ctrl-btn:hover {
background: rgba(168,85,247,.15);
border-color: rgba(168,85,247,.3);
color: #d8b4fe;
}
@media (prefers-reduced-motion: reduce) {
.ga-09__text { animation: none; background-position: 0% 50%; }
}(function() {
const w = document.querySelector('.ga-09');
w.querySelectorAll('.ga-09__ctrl-btn').forEach(btn => {
btn.addEventListener('click', () => {
w.querySelectorAll('.ga-09__ctrl-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
w.style.setProperty('--dur', btn.dataset.dur);
});
});
})(); (function() {
const w = document.querySelector('.ga-09');
w.querySelectorAll('.ga-09__ctrl-btn').forEach(btn => {
btn.addEventListener('click', () => {
w.querySelectorAll('.ga-09__ctrl-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
w.style.setProperty('--dur', btn.dataset.dur);
});
});
})();How this works
The core technique requires three CSS declarations working together: background-image: linear-gradient(...) sets the colour palette, background-size: 300% 100% makes the gradient canvas three times wider than the text element, and the combination of background-clip: text plus -webkit-text-fill-color: transparent punches through the text shape to reveal the gradient beneath. A single shared @keyframes ga-09-slide moves background-position from 0% to -200%, which shifts the visible gradient window across the oversized canvas — because the canvas is 300% wide but the window is 100%, the journey covers two full gradient repetitions, creating a seamless infinite loop.
Different demo variants use different animation-duration values: the large headline runs at 1.4× the base --dur, while stat numbers use staggered animation-delay of 0s, -.8s, and -1.6s so they appear to be at different phases of the same cycle — giving the impression of independent living numbers without multiple keyframe declarations.
Customize
- Change the colour journey by editing the gradient stops in
background-image— repeating the first colour at the end (e.g....#a855f7, #a855f7) ensures a seamless loop with no colour jump at the boundary. - Control the flow speed globally via
--duron.ga-09— the JS speed control adjusts this single property, and all text variants scale their own durations via cascade. - Use the gradient text technique on a single word within a sentence by wrapping it in a
spanwithdisplay: inline; background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparentand the gradient background. - Increase contrast on mid-brightness backgrounds by adding
text-shadow: 0 0 30px rgba(168,85,247,.3)— note thattext-shadowworks on-webkit-text-fill-color: transparentelements in all browsers, adding a soft glow behind the clipped gradient. - For a static gradient text (no animation) simply remove the
animationproperty and set a fixedbackground-position: 0% 50%— this gives a clean diagonal gradient title without any motion.
Watch out for
-webkit-text-fill-color: transparentis the property that actually makes text transparent to reveal the clipped background —color: transparentalone does not work in WebKit/Blink; always include bothcolor: transparent(as fallback) and-webkit-text-fill-color: transparent.- Text selection highlighting becomes invisible when
-webkit-text-fill-color: transparentis set in some browsers — always add a scoped::selection { background: rgba(...); color: #fff }rule so selected text remains legible. - SVG text does not support the
background-clip: texttechnique — if you need gradient text inside an SVG, use alinearGradientfill on thetextelement instead.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 57+ | 8+ | 110+ | 57+ |
background-clip: text is stable everywhere; -webkit-text-fill-color: transparent has been supported in WebKit/Blink since 2014. Firefox added full support in v110 (2023); prior versions need the -webkit- prefix version.