CSS Gradient Text Generator
Paint a gradient through text with background-clip, in the declaration order that leaves legible text when a browser does not support it.
Enable JavaScript to change the gradient; the CSS below is for the defaults.
Used by linear and conic gradients. A radial one is positioned rather than angled.
Leave empty for a two-colour gradient.
Written before the transparent one, so a browser without background-clip: text shows solid text rather than nothing.
Headlines take a gradient well
The same gradient at body size, which is where it has to earn its place.
A contrast checker cannot measure this: the text is two or three colours at once. Pick a gradient whose darkest and lightest points both pass against the background.
.headline {
/* A browser without background-clip: text stops here, with legible text. */
color: rgb(30 86 236);
background-image: linear-gradient(92deg, rgb(30 86 236), rgb(124 58 237), rgb(226 69 107));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
display: inline-block;
}
Gradient text is four declarations and one idea: paint a gradient as the element’s background, clip that background to the shape of the glyphs, and make the text itself transparent so the background shows through.
The order of those declarations is the part that gets skipped. A solid colour has to come before the
transparent one, because a browser that does not understand background-clip: text will otherwise render
transparent text over a background it has also painted, which is invisible text. Written in the right
order, the fallback is legible text in a solid colour.
The other thing worth saying plainly: no contrast checker can measure this. The text is two or three colours at once. Pick a gradient whose darkest and lightest points both pass against the background, and check them separately.
How to use
- Choose the gradient kind and its colours. Leave the middle colour empty for a two-stop gradient.
- Set the fallback colour. It is the one people see if the clip is not supported, so make it one of the gradient’s own colours.
- Keep “inline-block” on for anything that might wrap, unless the element is already a block.
Example
.headline {
/* A browser without background-clip: text stops here, with legible text. */
color: rgb(30 86 236);
background-image: linear-gradient(92deg, rgb(30 86 236), rgb(124 58 237), rgb(226 69 107));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
display: inline-block;
}
Two color declarations in one rule is not a mistake. The first is the fallback and the second overrides
it wherever the clip works, which is how the technique degrades safely.
Pitfalls
Order is the whole technique. Solid colour first, transparent second. Reversed, an unsupporting browser shows nothing.
Safari needs -webkit-text-fill-color. Setting color: transparent alone is not enough there, and the
prefixed background-clip is still worth shipping.
Inline elements clip per line. A gradient across a heading that wraps restarts on every line, because
an inline box is really several boxes. display: inline-block gives it one box to work with.
It cannot be contrast checked. Tools measure one foreground colour. Test the darkest and lightest points of the gradient against the background separately, and keep both above the ratio you need.
Selection and highlight look odd. Selected text usually keeps the transparent fill, so the selection
rectangle appears with nothing legible in it. Set a ::selection colour if that matters.
It is a headline effect. At body size a gradient mostly reads as inconsistent weight. The preview shows both sizes for exactly that reason.
Keep it off long text. The effect costs a paint of the background box behind every glyph, and applied to a page of copy it is both slow and hard to read.
Compatibility
background-clip: text is supported unprefixed in Chrome 120, Safari 14 and Firefox 49, with the
-webkit- prefix working further back in every WebKit and Blink browser. Both are emitted.
Older browsers land on the fallback colour, which is the first color declaration, and see ordinary solid
text. That is the entire point of the ordering, and the test suite asserts the solid declaration appears
before the transparent one rather than trusting it to stay that way.
The gradient is built by one function, mirrored in PHP for the first paint, and a case file compares the two on every build. One case has a blank colour field, which is what caught a difference between the languages: JavaScript threw on it and PHP silently produced black, so both now fall through to the next colour that is set.
A single-stop gradient is invalid CSS, so a blank field can never produce one: the tool repeats the remaining colour instead.
Frequently asked questions
Why are there two color declarations?
background-clip: text; the second makes the text
transparent so the gradient shows. A browser that understands the clip applies both and the second wins.Why does my gradient restart on every line?
display: inline-block, which the tool does for you, or apply the effect to a
block-level element.Is gradient text accessible?
Can I animate the gradient?
background-position on a larger gradient, which is the usual trick. Animating the colour
stops themselves needs @property registration, which is supported in current browsers and not in older
ones.Does this work on a <button> or an input?
<input>, generally not: the value text is rendered by the widget rather than as
a normal text node, and WebKit does not clip it.