
How to Style Text with CSS Gradients and Background Clip?
- Sajjad
- Typography
- 01 Oct, 2026
The marketing team wants the hero headline to fade from violet to pink, like the one on that SaaS site everybody is copying this year. You add a linear-gradient as the background, set the text colour to transparent, and the headline disappears completely in one browser and shows a coloured rectangle behind invisible text in another. When you finally get it working, a teammate selects the text and it vanishes again, and the accessibility checker flags the lighter end of the gradient for failing contrast.
Gradient text is a pure CSS technique, but it combines several properties that interact in non-obvious ways. This article explains how background-clip: text works, the complete cross-browser recipe, how to keep gradient text readable and accessible, how to handle multi-line text and selection, how to animate it responsibly, and how to build it in Tailwind CSS v4.
How Gradient Text Works
CSS has no text-gradient property. Gradient text is a trick that combines three steps:
- Paint a gradient as the element's background with
background-image. - Clip the background to the shape of the text with
background-clip: text, so the gradient only appears where the glyphs are. - Make the text fill transparent so the clipped background shows through instead of the solid text colour.
.gradient-text {
background-image: linear-gradient(90deg, #7c3aed, #db2777);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
That is the whole technique. Every refinement below is about making it robust.
The Cross-Browser Recipe
background-clip: text is supported unprefixed in current Chrome, Edge, and Firefox. Safari has historically needed the -webkit- prefixed form, and keeping the prefix costs nothing, so always include both. Some developers also use -webkit-text-fill-color instead of color:
.gradient-text {
background-image: linear-gradient(90deg, #7c3aed, #db2777);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: #7c3aed; /* fallback colour if clipping is not applied */
}
The difference matters. With -webkit-text-fill-color: transparent, the color property remains a real colour, so currentColor (used by borders, SVG icons, and text decorations) still works, and you have a solid fallback colour defined in the same rule. With color: transparent, any underline or icon that inherits currentColor also becomes invisible.
A Safe Fallback with @supports
If a browser cannot clip the background to text, you end up with transparent text over a gradient rectangle, which is unreadable. Gate the effect behind a feature query so unsupported environments get solid colour:
.gradient-text {
color: #7c3aed;
}
@supports (background-clip: text) or (-webkit-background-clip: text) {
.gradient-text {
background-image: linear-gradient(90deg, #7c3aed, #db2777);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
}
This also protects you in email clients, RSS readers, and reader modes that strip or ignore some CSS.
Choosing Gradients That Stay Readable
Gradient text fails readability in predictable ways: one end is too light, the colours clash in the middle, or the gradient is so busy it competes with the letterforms.
Check Contrast at Every Stop
WCAG 1.4.3 requires a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (24px and up, or about 18.66px and up when bold). With a gradient, the text does not have one colour, so test the lowest-contrast part of the gradient against the background. If the lightest stop passes, the whole gradient passes.
| Gradient on white background | Light stop contrast | Large text (3:1) | Normal text (4.5:1) |
|---|---|---|---|
#7c3aed to #db2777 | About 4.6:1 (pink) | Pass | Pass |
#6366f1 to #22d3ee | About 1.8:1 (cyan) | Fail | Fail |
#1e3a8a to #7c3aed | About 5.7:1 (violet) | Pass | Pass |
Bright cyans, yellows, and limes are the usual culprits on light backgrounds. On dark backgrounds, deep blues and purples fail. The article on WCAG contrast requirements for text explains how to measure ratios precisely.
Use Gradients for Display Text Only
Reserve gradient text for headlines, hero titles, large numbers, and short emphasis. On body text, colour shifts across a line make reading slower and the effect looks cheap. Gradients also need a reasonably heavy weight to show; on a hairline weight, there is not enough ink area for the colour change to register. Weights of 600 and above work best.
Interpolate in a Perceptual Colour Space
Gradients between two saturated colours often pass through a muddy grey in the middle when interpolated in sRGB. Modern CSS lets you choose the interpolation space, and oklch usually produces cleaner, more vivid transitions:
.gradient-text {
background-image: linear-gradient(in oklch 90deg, #7c3aed, #db2777);
}
/* Go the long way around the hue wheel for a rainbow effect */
.rainbow-text {
background-image: linear-gradient(in oklch longer hue 90deg, #ef4444, #ef4444);
}
Sizing the Gradient to the Text
By default, the gradient covers the whole element box. For a block-level heading that spans the full container width but only has text on the left, most of the gradient is wasted, and the text shows only the first part of the colour range.
Make the element shrink to its text so the full gradient lands on the glyphs:
.gradient-text {
display: inline-block; /* or width: fit-content on a block */
}
h1.gradient-text {
display: block;
width: fit-content;
}
Multi-Line Text
For text that wraps, a block element applies one gradient across the entire box, so each line gets a different slice. Often that is what you want. If you want each line to get the full gradient, use an inline element with box-decoration-break: clone:
.gradient-inline {
background-image: linear-gradient(90deg, #7c3aed, #db2777);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
-webkit-box-decoration-break: clone;
box-decoration-break: clone;
}
<h1><span class="gradient-inline">Design systems that scale with your team</span></h1>
Safari still requires the prefixed -webkit-box-decoration-break for inline elements.
Clipped Descenders
With a tight line-height, letters like g, p, and y can extend beyond the element's background area, and the part outside is not painted, so descenders look cut off. Add a little padding at the bottom or loosen the line height:
.gradient-text {
line-height: 1.15;
padding-block-end: 0.1em;
}
Text Selection and Forced Colours
Selection
When a user selects gradient text, browsers highlight the selection background, but the transparent fill can make the selected text invisible against the highlight in some engines. Define an explicit selection style:
.gradient-text::selection {
-webkit-text-fill-color: #fff;
color: #fff;
background: #7c3aed;
}
Windows High Contrast and Forced Colours
In forced colours mode (Windows Contrast Themes), the browser replaces author colours with the user's system palette. Gradient text can end up invisible or illegible depending on how the browser treats transparent fills and background images. Reset the effect explicitly:
@media (forced-colors: active) {
.gradient-text {
background-image: none;
-webkit-text-fill-color: currentColor;
color: CanvasText;
}
}
That guarantees the text uses the user's chosen text colour. It is part of the broader practice covered in how to make website typography accessible.
Animating Gradient Text
A slowly moving gradient is a popular hero effect. Make the background larger than the element and animate its position:
.gradient-animated {
background-image: linear-gradient(90deg, #7c3aed, #db2777, #f59e0b, #7c3aed);
background-size: 300% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: gradient-shift 8s linear infinite;
}
@keyframes gradient-shift {
to {
background-position: 300% 0;
}
}
@media (prefers-reduced-motion: reduce) {
.gradient-animated {
animation: none;
}
}
Repeating the first colour at the end makes the loop seamless. Keep the duration long (6 seconds or more) and the colour change subtle. Motion that flashes or moves quickly near text is distracting, and WCAG 2.2.2 requires a way to pause any moving content that starts automatically and lasts more than five seconds alongside other content. Respecting prefers-reduced-motion is the minimum. For a deeper look at motion and type, see kinetic typography.
Animating background-position triggers repaints, not layout, so it is cheap for a single headline. Avoid animating dozens of gradient elements at once.
Animating with @property
You can also animate the gradient's colours or angle by registering a custom property, which tells the browser its type so it can interpolate it:
@property --angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.gradient-rotate {
background-image: linear-gradient(var(--angle), #7c3aed, #db2777);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: rotate-gradient 10s linear infinite;
}
@keyframes rotate-gradient {
to {
--angle: 360deg;
}
}
@property is supported in all current major browsers.
Beyond Linear Gradients
The same clipping trick works with any background image:
/* Radial glow */
.radial-text {
background-image: radial-gradient(circle at 30% 50%, #fde047, #f97316 60%);
}
/* Conic sweep */
.conic-text {
background-image: conic-gradient(from 90deg, #22d3ee, #7c3aed, #22d3ee);
}
/* Photo texture inside letters */
.image-text {
background-image: url("/images/texture.webp");
background-size: cover;
background-position: center;
}
Each needs the same background-clip, transparent fill, and fallback colour. Image-filled text needs a very bold, large typeface to stay legible, and the contrast of the image varies across the letters, which makes it a poor choice for anything other than a decorative headline.
Gradient Text in Tailwind CSS v4
Tailwind v4 renamed the gradient utilities: bg-gradient-to-r became bg-linear-to-r, and you can now specify angles and interpolation spaces directly.
<h1 class="bg-linear-to-r from-violet-600 to-pink-600 bg-clip-text text-5xl font-extrabold text-transparent">
Build faster with modern CSS
</h1>
<!-- Custom angle -->
<h2 class="bg-linear-45 from-indigo-600 to-violet-600 bg-clip-text text-transparent">
Angled gradient
</h2>
<!-- Interpolate in OKLCH instead of the default -->
<h2 class="bg-linear-to-r/oklch from-violet-600 to-pink-600 bg-clip-text text-transparent">
Perceptual gradient
</h2>
For a reusable, accessible version with a fallback, define your own utility using tokens from your theme:
@import "tailwindcss";
@theme {
--color-brand-start: oklch(0.55 0.25 290);
--color-brand-end: oklch(0.6 0.22 0);
}
@utility text-gradient-brand {
color: var(--color-brand-start);
@supports (background-clip: text) or (-webkit-background-clip: text) {
background-image: linear-gradient(in oklch 90deg, var(--color-brand-start), var(--color-brand-end));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
@media (forced-colors: active) {
background-image: none;
-webkit-text-fill-color: currentColor;
}
}
Then use text-gradient-brand on any heading. Defining colours in @theme keeps them consistent with the rest of your design tokens, which is covered in typography in Tailwind CSS v4.
Common Mistakes
- Forgetting the prefix. Without
-webkit-background-clip: text, older Safari versions show a gradient box with invisible text. - No fallback colour. If clipping fails, the text disappears. Always set a solid colour first.
- Using
color: transparentwith underlined links. The underline usescurrentColorand vanishes. Use-webkit-text-fill-colorinstead. - Gradient on body text. It hurts reading speed and rarely passes contrast at every stop.
- Gradient spread across the full container width. Use
inline-blockorfit-contentso the text sees the whole gradient. - Animation without a reduced-motion query. Always provide a static version.
Gradient Text FAQ
Yes, in all current major browsers. Chrome, Edge, and Firefox support the unprefixed property, and Safari has long supported the webkit prefixed version. Include both forms and a solid fallback colour so the text remains visible in any environment that does not apply the clip.
It can be, if every part of the gradient meets the WCAG contrast minimum against the background, which is 4.5 to 1 for normal text and 3 to 1 for large text. Screen readers ignore the visual effect entirely. Also reset the effect in forced colours mode and provide a reduced motion alternative for animated gradients.
Prefer the webkit text fill colour set to transparent. It hides the fill while keeping the color property as a real value, so underlines, borders, and icons that rely on currentColor still render, and the color property doubles as a fallback.
Descenders can extend beyond the element's background area when the line height is tight, and nothing is painted outside that area. Add a small amount of bottom padding or increase the line height slightly.
No. The text remains real, selectable HTML text, so search engines index it normally. That is a major advantage over putting gradient headlines in images.
Combine a linear gradient utility such as bg-linear-to-r with from and to colour utilities, then add bg-clip-text and text-transparent. For a reusable version with a fallback and forced colours handling, define a custom utility in your CSS file with the utility directive.
Conclusion
Gradient text is just a background, clipped to the glyphs, with a transparent fill. The robust version adds the -webkit- prefix, a solid fallback colour behind a feature query, -webkit-text-fill-color instead of color: transparent, and an element sized to its text so the whole gradient shows. With those in place, it works everywhere and degrades gracefully where it does not.
The design discipline matters as much as the code. Use gradients on large, heavy display text, check the lowest-contrast stop against WCAG minimums, reset the effect in forced colours mode, and keep any animation slow and optional. Used that way, gradient text adds personality to a hero section without costing readers anything.
Here are some useful references for going deeper on gradient text:
- MDN Web Docs: background-clip — values including text, with browser compatibility notes.
- MDN Web Docs: Using CSS gradients — linear, radial, and conic gradient syntax.
- W3C WAI: Understanding SC 1.4.3 Contrast (Minimum) — contrast requirements for normal and large text.
- Tailwind CSS: background-image — gradient utilities and interpolation modifiers in Tailwind v4.
- MDN Web Docs: forced-colors — how to adapt styles for Windows Contrast Themes.


