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:
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
The main downside of mask-image
is that it also masks the selection style of your text. When selecting the text with the background-clip
technique it becomes clearly readable because the selection undoes the background clip. With mask-image
the selection is also faded, which makes the text harder to read.
The browser support for the unprefixed version is relatively new, so for backwards compatibility you also need the prefixed properties. 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!