20 CSS Text Gradient Effects 08 / 20
Animated Moving Text Gradient Background CSS
A hero section where the gradient background-position animates continuously across a 300%-wide gradient, creating a shifting colour sweep through the text.
The code
<div class="tg-08">
<div class="tg-08__hero">
<div class="tg-08__tag">✦ New — Season 3</div>
<h1 class="tg-08__title">The Future<br>of <span class="tg-08__anim-grad">Interface</span><br>Design</h1>
<p class="tg-08__sub">Shifting gradients create motion and energy without a single line of JavaScript.</p>
</div>
</div> <div class="tg-08">
<div class="tg-08__hero">
<div class="tg-08__tag">✦ New — Season 3</div>
<h1 class="tg-08__title">The Future<br>of <span class="tg-08__anim-grad">Interface</span><br>Design</h1>
<p class="tg-08__sub">Shifting gradients create motion and energy without a single line of JavaScript.</p>
</div>
</div>.tg-08, .tg-08 *, .tg-08 *::before, .tg-08 *::after { margin:0; padding:0; box-sizing:border-box; }
.tg-08 ::selection { background:#7c3aed55; color:#fff; }
.tg-08 {
--c1: #f0abfc;
--c2: #818cf8;
--c3: #38bdf8;
--c4: #34d399;
--bg: #0a0a12;
--text: #f8fafc;
--muted: #94a3b8;
font-family: system-ui, -apple-system, sans-serif;
background: var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 48px 24px;
}
.tg-08__hero { text-align: center; max-width: 600px; }
.tg-08__tag {
display: inline-block;
font-size: .75rem;
font-weight: 700;
letter-spacing: .1em;
color: var(--muted);
margin-bottom: 24px;
}
.tg-08__title {
font-size: clamp(2.5rem, 8vw, 4.5rem);
font-weight: 900;
line-height: 1.08;
letter-spacing: -.04em;
color: var(--text);
margin-bottom: 20px;
}
/*
Moving gradient: background-size is 300% wide so the
gradient has room to travel. background-position animates
from 0% to 100%, which pans across the extended gradient.
background-clip:text exposes it through the letterforms.
*/
.tg-08__anim-grad {
background: linear-gradient(
90deg,
var(--c1) 0%,
var(--c2) 25%,
var(--c3) 50%,
var(--c4) 75%,
var(--c1) 100%
);
background-size: 300% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
animation: tg-08-shift 4s linear infinite;
}
@keyframes tg-08-shift {
0% { background-position: 0% 50%; }
100% { background-position: 100% 50%; }
}
.tg-08__sub {
font-size: 1rem;
color: var(--muted);
line-height: 1.7;
max-width: 480px;
margin: 0 auto;
}
@media (prefers-reduced-motion: reduce) {
.tg-08__anim-grad {
animation: none;
background-position: 0% 50%;
}
} .tg-08, .tg-08 *, .tg-08 *::before, .tg-08 *::after { margin:0; padding:0; box-sizing:border-box; }
.tg-08 ::selection { background:#7c3aed55; color:#fff; }
.tg-08 {
--c1: #f0abfc;
--c2: #818cf8;
--c3: #38bdf8;
--c4: #34d399;
--bg: #0a0a12;
--text: #f8fafc;
--muted: #94a3b8;
font-family: system-ui, -apple-system, sans-serif;
background: var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 48px 24px;
}
.tg-08__hero { text-align: center; max-width: 600px; }
.tg-08__tag {
display: inline-block;
font-size: .75rem;
font-weight: 700;
letter-spacing: .1em;
color: var(--muted);
margin-bottom: 24px;
}
.tg-08__title {
font-size: clamp(2.5rem, 8vw, 4.5rem);
font-weight: 900;
line-height: 1.08;
letter-spacing: -.04em;
color: var(--text);
margin-bottom: 20px;
}
/*
Moving gradient: background-size is 300% wide so the
gradient has room to travel. background-position animates
from 0% to 100%, which pans across the extended gradient.
background-clip:text exposes it through the letterforms.
*/
.tg-08__anim-grad {
background: linear-gradient(
90deg,
var(--c1) 0%,
var(--c2) 25%,
var(--c3) 50%,
var(--c4) 75%,
var(--c1) 100%
);
background-size: 300% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
animation: tg-08-shift 4s linear infinite;
}
@keyframes tg-08-shift {
0% { background-position: 0% 50%; }
100% { background-position: 100% 50%; }
}
.tg-08__sub {
font-size: 1rem;
color: var(--muted);
line-height: 1.7;
max-width: 480px;
margin: 0 auto;
}
@media (prefers-reduced-motion: reduce) {
.tg-08__anim-grad {
animation: none;
background-position: 0% 50%;
}
}How this works
The moving gradient uses an extended background-size: 300% 100% which makes the gradient three times wider than the element. The @keyframes tg-08-shift animation moves background-position from 0% 50% to 100% 50% linearly and infinitely. Because the gradient is cyclic (it repeats the first colour at 100%), the transition is seamless with no visible seam.
Only the background-position property is animated. This is a compositor-friendly operation — the browser can perform it without triggering layout or paint phases. Combined with -webkit-background-clip: text, the shifting position is visible only through the letterforms, creating the illusion of light sweeping through the text.
Customize
- Control animation speed by editing
4sinanimation: tg-08-shift 4s linear infinite— slower values (8s) feel meditative, faster (1.5s) feel energetic. - Add more colour stops to the gradient — insert hues between existing stops and expand
background-sizeto400%to keep the sweep speed consistent with more colours. - Change
lineartiming toease-in-outfor a breathing quality — the sweep accelerates into and out of the highlight band instead of moving at constant speed. - Apply the same animated gradient to a subtitle or tag by adding the
tg-08__anim-gradclass, adjusting the gradient stops to complementary colours for a multi-element sequence. - Pause the animation on hover by adding
.tg-08__anim-grad:hover { animation-play-state: paused; }so users can 'freeze' the gradient at their preferred colour point.
Watch out for
- The
background-positionanimation approach requiresbackground-size: 300% 100%— if you forget to set this, the gradient won't appear to move because the default 100% size leaves no room for the position to scroll. - Using
lineartiming on the position animation creates a seamless loop only when the gradient's first and last colour stops match. If they differ, the jump at the loop boundary will be visible — always make the final stop equal the initial stop. - On Safari, very long text strings with animated
background-positionandbackground-clip: textcan cause a brief repaint flash on loop iteration. Limit the animation to short display words (<10 characters) for the smoothest result.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 69+ | 12.1+ | 83+ | 69+ |
background-position animation on background-clip:text elements is reliable in modern browsers; avoid pairing with simultaneous transforms on the same element in Safari.