Skip to contentSkip to footer

If you want to be kept up to date with new articles, CSS resources and tools, join our newsletter.

Two days ago Amit Merchant wrote a blog post on a technique to fade text out using an overlaid element based on the design of Trunk.io. He walks through how he came to this solution so go check out the article to see how it's built. Here's the end result:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec non pharetra lorem, ac feugiat nunc. Maecenas ac aliquet turpis. Pellentesque interdum turpis tortor, ac rhoncus nisi egestas et. Sed in ante diam. Proin ac vehicula velit. Ut fringilla mauris quis lobortis efficitur.

The effect looks great but it has two downsides:

  • The text behind the gradient is no longer selectable
  • The effect only works on solid background colors.

Both of these are also present in the Trunk.io design that Amit based this on. The first issue is easy to fix: you add this to your overlaid element:

user-select: none;

That will prevent the overlaid element from capturing mouse events, so you can select words behind the gradient as well.

The second downside requires a different approach if we want the same effect to work with a non-solid background like an image or gradient. To make it work there, we need to actually make the text fade, rather than make it look like it fades.

Modern CSS gives us not one, but two different approaches to achieve this. They have a slightly different result, so we'll go over each one and discuss their differences.

For both approaches, we're going to use the same HTML structure:

<p>
  <span> Lorem ipsum dolor sit amet consectetur [...] </span>
</p>

For this to work, we need an additional wrapper element around the text. In this case we're using a p and span, but if you already have a different container element around your p you could use that instead.

To make the effect easy to see, we'll give our p some default styling and add a gaudy background gradient:

p {
  background: linear-gradient(to right, blue, red);

  max-width:30rem;
  padding: 20px;
  border-radius: 20px;

  font-family: system-ui;
  font-weight: bold;
  color: white;
}

The end result looks like this:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec non pharetra lorem, ac feugiat nunc. Maecenas ac aliquet turpis. Pellentesque interdum turpis tortor, ac rhoncus nisi egestas et. Sed in ante diam. Proin ac vehicula velit. Ut fringilla mauris quis lobortis efficitur.

With that as our backdrop, let's look at the two different ways to achieve faded content: background-clip and mask-image.

Technique one: background-clip

The background-clip CSS property lets you define where your background should be restricted to: the entire element (border-box, which is the default), inside the border with padding-box or sit only behind the content with content-box.

But background-clip has a fourth value of text. Setting this will restrict the background to be visible only behind the text.

Just doing that doesn't actually change the way it looks, because the text also has a color that overlays the background. When using background-clip: text you want to combine it with color: transparent.

Lastly to make that text fade, we're going to set the background to a linear gradient that fades from our text color (white) to transparent.

Combined, this is what that looks like:

span {
  display: block;
  background: linear-gradient(to bottom, white, transparent);
  color: transparent;
  background-clip: text;
}

And here is the result:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec non pharetra lorem, ac feugiat nunc. Maecenas ac aliquet turpis. Pellentesque interdum turpis tortor, ac rhoncus nisi egestas et. Sed in ante diam. Proin ac vehicula velit. Ut fringilla mauris quis lobortis efficitur.

Notice how the text fades into the background evenly across the gradient, and how each word remains selectable because we're not overlaying anything on top of the text.

Browser support

background-clip: text is supported in all evergreen browsers and has been for quite a while (support in all evergreen browsers since 2016 when it landed in Firefox) so it's save to use.

Until fairly recently, Safari (2020) and Chromium (December 2023) required you to use -webkit-background-clip so for backwards compatibility you will probably also include the prefixed version:

span {
  display: block;
  background: linear-gradient(to bottom, white, transparent);
  color: transparent;
  -webkit-background-clip: text;
  background-clip: text;
}

Make sure to put the prefixed version before the standard declaration. In CSS the last declaration wins, so the standard will overwrite the prefixed version and be used instead

Downsides

This technique works well, but it has a downside. Because the text is now transparent, it takes on the color of the background. If your content had different colors for elements like links or other elements like emoji, those colors are either replaced or they don't fade:

Emojis: 😬🫣😱🚀✨Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem My hotpink link ipsum dolor sit amet, consectetur

If you want to keep those as well as fade the content out, we need a different technique.

Technique two: mask-image

mask-image lets you use an image (or SVG or gradient) as a mask layer, similar to how that works in graphics editors.

Usually the alpha channel of the mask image is used to determine which parts of your element are visible and which ones aren't, but you can set mask-mode to luminance and use a black-white mask instead. For our purpose though, the alpha channel is fine.

Here's what the CSS looks like for that:

span {
  display: block;
  mask-image: linear-gradient(to bottom, white, transparent);
}

As well as the rendered result:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec non pharetra lorem, ac feugiat nunc. Maecenas ac aliquet turpis. Pellentesque interdum turpis tortor, ac rhoncus nisi egestas et. Sed in ante diam. Proin ac vehicula velit. Ut fringilla mauris quis lobortis efficitur.

Like the background-clip technique the words are still individually selectable, and text fades into the background evenly across the gradient. Because this is a mask across your element however, the specific colors of your text are preserved.

Emojis: 😬🫣😱🚀✨Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem My hotpink link ipsum dolor sit amet, consectetur adipiscing elit. Donec non pharetra lorem

Browser support

mask-image has been supported across evergreen browsers since 2017, so a little less than background-clip. The earlier mentioned mask-mode has only been available since December 2023 when support landed in Chromium (both Safari and Firefox had support for it since 2022 and 2017 respectively). Currently for Edge support you still need to include the prefixed -webkit- version:

span {
  display: block;
  -webkit-mask-image: linear-gradient(to bottom, white, transparent);
  mask-image: linear-gradient(to bottom, white, transparent);
}

I'm not sure why Edge specifically has this, as it's running the same version of Chromium as Chrome and Polypane, which don't need it. So that's a little complication you have to be aware of.

Downsides

Other than the relatively recent browser support for the unprefixed version, this technique doesn't have any downsides. It's a little more flexible than background-clip because it doesn't change the color of your text, though that might be a downside if you do want to change the color of your text.

Fading text content

So that's two ways to fade text content. mask-image is a little more flexible, but background-clip is a little more widely supported. Both are great ways to fade text content, and I hope you can use them in your projects!

Build your next project with Polypane

  • Use all features on all plans
  • On Mac, Window and Linux
  • 14-day free trial – no credit card needed
Try for free
Polypane screenshot