To paint a gradient across text in CSS you put the gradient on the element's background, clip that background to the shape of the glyphs with background-clip: text, and then make the text itself transparent so the clipped background shows through. That is the whole trick, and as of 2026 it works with no vendor prefix in every current browser:
.gradient-text {
background: linear-gradient(90deg, #6366f1, #ec4899);
background-clip: text;
color: transparent;
}That is four lines and it is real, selectable, accessible-tree text. There is no SVG, no canvas, no background image. The letters are still letters; you are only changing how they are painted.
Why the text has to be transparent
background-clip: text clips the element's painted background to the union of its text glyphs. But by default the glyphs are also filled with the color, and that fill paints on top of the clipped background, hiding it completely. So you need to remove the text fill to let the gradient underneath show through. Setting color: transparent does this in every current browser.
There is an older, more targeted property for the same job, -webkit-text-fill-color, which sets the glyph fill independently of color. It is still the safest choice if you want a sensible color to survive as a fallback (more on that below), because it overrides the fill without touching color:
.gradient-text {
background: linear-gradient(90deg, #6366f1, #ec4899);
background-clip: text;
-webkit-text-fill-color: transparent;
}Use one or the other. color: transparent is the simpler, fully standard line; -webkit-text-fill-color: transparent is the one that pairs nicely with a fallback color.
The cross-browser-clean recipe
background-clip: text is now Baseline: unprefixed support landed in Firefox years ago (49), in Safari 15.5, and finally in Chrome and Edge 120 (late 2023), which is when Chromium stopped requiring the -webkit- prefix. Before Chrome 120 you had to write -webkit-background-clip: text, and a lot of old tutorials still tell you it is mandatory. It is not, anymore. But there is no cost to keeping the prefixed line for anyone on an older Safari or pre-120 Chromium, so the belt-and-suspenders version is:
.gradient-text {
background: linear-gradient(90deg, #6366f1, #ec4899);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
}Order matters: put the prefixed -webkit-background-clip before the unprefixed background-clip so the standard property wins where it is understood.
Make it accessible, or it disappears
Transparent text is invisible text. If a browser applies your background but not the clip, you get a solid-color block where your heading should be; if it applies neither, you get nothing at all. So a fallback is not optional.
The clean way is to set a real color first as the fallback, then only blank the fill with -webkit-text-fill-color (which the same browsers that understand background-clip: text also understand). Wrap the transparent line in a feature query so it only fires where clipping actually works:
.gradient-text {
color: #6366f1; /* solid fallback: a readable color from the gradient */
}
@supports (background-clip: text) or (-webkit-background-clip: text) {
.gradient-text {
background: linear-gradient(90deg, #6366f1, #ec4899);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
}Two more things that are easy to forget:
- Contrast is judged on the gradient, not on one color. WCAG contrast ratios assume a solid foreground, and a gradient has no single value. Pick endpoints that both clear your target ratio against the background, and treat the lightest point along the gradient as the worst case. A gradient that fades to near-white on a white page fails for the same reason light gray text does.
- Keep gradient text for display type. Big, bold headings and short labels are fine. Body copy in clipped-gradient text is harder to read and a contrast liability across its whole run, so do not do it.
Animate the gradient
A static gradient is paint-once. To make it move, give the background more room than the element (background-size larger than 100%) and animate background-position so the visible slice of the gradient slides across the glyphs:
.gradient-text-animated {
background: linear-gradient(90deg, #6366f1, #ec4899, #6366f1);
background-size: 200% auto;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
animation: te-text-shimmer 4s linear infinite;
}
@keyframes te-text-shimmer {
to { background-position: 200% center; }
}Repeating the first color at the end of the gradient makes the loop seamless. Always honor the user's motion preference, which is a one-line guard:
@media (prefers-reduced-motion: reduce) {
.gradient-text-animated { animation: none; }
}Clip an image instead of a gradient
background-clip: text does not care that the background is a gradient. Any background works, including a raster image or a conic gradient, so you can pour a photo or a texture through your letters:
.image-text {
background-image: url("/textures/holographic.jpg");
background-size: cover;
background-position: center;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
}This is the same mechanism as the gradient version; the only change is what sits in the background. If you want the clipped text itself to read as more of an effect than a fill, put a filter: drop-shadow(...) on a wrapper element, rather than text-shadow (which paints on the now-transparent fill and will not show).
See also
- CSS calc(): dynamic values in your stylesheet: handy for sizing the oversized
background-sizeused in the animated version. - 8-digit hex colors and alpha transparency: set the gradient stops, and any fallback, with per-color opacity baked into the hex value.
- CSS feature queries with @supports: the mechanism behind the accessible fallback shown above.
Sources
Authoritative references this article was fact-checked against.
- background-clip (MDN, official)developer.mozilla.org
- -webkit-text-fill-color (MDN, official)developer.mozilla.org
- background-clip: text browser support (Can I Use)caniuse.com





