Original link: https://ishadeed.com/article/css-masking/
In the design world, masking is a popular technique for achieving unique design effects. As a designer, I have used it many times, but I have rarely used CSS masking on the web. I think the reason I don't use CSS masking is due to browser support; they only support some features in Blink browsers (Chrome and Edge), while Safari and Firefox have full support.
The good news is that CSS masking will be part of Interop 2023, which means we should expect cross-browser support (yay!!).
In this article, I will introduce what CSS masking is, how it works, and provide some use cases and examples that incorporate it.
Let's get started.
What is masking?#
Simply put, masking works by partially hiding an element without erasing it.
Refer to the image below:
We have an image and a mask. In design applications like Photoshop, we can insert the image into a gray shape, resulting in a masked image.
The way it works is by hiding certain parts of the image instead of erasing it (they're still there, just hidden).
This is the core concept of masking: using a shape to show and hide parts of an element. We can further explore more unique masking content.
For example, we can create a gradient mask like the one shown below.
In the gradient, there are filled and transparent pixels. The filled part is the visible area of the element, while the transparent part is the hidden area.
In Photoshop, we can add a layer mask to a group of layers, and the contents within that group will be masked. It works by using the brush tool to hide part of the group, thus achieving the mask.
The masked content is not erased but hidden (note the group items).
Alright, that's all theoretical. In the following sections, I will introduce how to use CSS masking, how it works, and some use cases and examples.
How to use masking in CSS#
In CSS, there are several ways to mask elements:
mask
propertyclip-path
property- SVG
<mask>
The main difference between the mask
property and clip-path
is that the former is used for images and gradients, while the latter is used for paths. This article will focus on the mask
property.
In CSS, we have the mask
shorthand property, similar to the background
property. Yes, you read that right. This is why I think its syntax is easy to remember, as it is identical to the background
property but has a few additional properties.
Instead of listing all the CSS masking properties, I will gradually add an example of a masking feature so you can visually see the differences.
The CSS background looks like this:
.card__thumb {
background-image: url('hero-cool.png');
}
The CSS mask looks like this:
.card__thumb {
mask-image: url('hero-cool.png');
}
Cool, right? This makes it easier for you to understand and master CSS masking.
Now, let's redo the initial example with CSS.
First, I need to export the shape as a PNG image.
Suppose I want to apply the mask to that image.
<img src="ahmad-shadeed-web-directions.jpg" alt="" />
img {
mask-image: url("shape.png");
}
Can you predict the result? By default, the mask will repeat, and its size will equal the mask image itself, as shown in the image below:
To fix this, we need to set mask-repeat
to no-repeat
, just like with CSS background images.
img {
mask-image: url("shape.png");
mask-repeat: no-repeat;
}
Awesome! Note that the mask is positioned at the top left corner, and it can be changed using mask-position
. Again, note that the syntax is exactly the same as for CSS background image configurations!
img {
mask-image: url("shape.png");
mask-repeat: no-repeat;
mask-position: center;
}
In addition to position, we can also change the size of the mask, which is useful for making the mask image responsive to the size of the element.
img {
mask-image: url("shape.png");
mask-repeat: no-repeat;
mask-position: center;
mask-size: 60%;
}
There are some other mask properties, but I don't want to overwhelm you with them right now; I will introduce them later with practical use cases.
Using CSS gradients for masking#
CSS masking is not just about using images; we can also leverage gradients to create powerful and useful masking effects.
I will show some useful use cases later, but for now, I want to focus on the basic principles of how gradients work with masks.
In the following example, the mask-image
consists of a CSS linear gradient from solid black to transparent.
img {
mask-image: linear-gradient(#000, transparent);
}
According to MDN:
By default, this means that the alpha channel of the mask image will be multiplied by the alpha channel of the element, which can be controlled using the mask-mode property.
This means you can use any color other than black, and the mask will still work because the default mask mode is set to alpha
(I will elaborate on this later).
img {
mask-image: linear-gradient(red, transparent);
}
Similarly, the concept of masking is that transparent pixels will be hidden. Here is a simplified example with a gradient that has hard color stops:
img {
mask-image: linear-gradient(#000 50%, transparent 0);
}
So cool! Now that the core masking concept is clear (I hope), let's explore some practical use cases for CSS masking.
Practical use cases and examples#
Fading an image#
One interesting use of masking is to fade an image and blend it with the background beneath it.
Consider the following image:
css-masking-use-case-fade-image-light.png
At first glance, you might want to add a gradient that matches the background color. Like this:
.hero__thumb:after {
position: absolute;
inset: 0;
background: linear-gradient(to top, #f1f1f1, transparent)
}
While this might work, it will fail when the main background color changes, and you will notice a jarring effect on the homepage banner:
Using CSS masking, we can cover the homepage banner so that it works with any background color.
.hero__thumb {
mask-image: linear-gradient(#000, transparent);
}
Just like that! Now the fading transition effect is real and won't break when changing the main page background. See the example below:
Masking text content: Example 1#
When we want to display long text but there isn't enough space to show it fully, the solution is to fade the text at the beginning and end, allowing the text to animate in either direction to reveal the remaining content.
Consider the following image:
Again, using a gradient hack won't work because the background beneath the content is changed, which can be a solid color or an image.
To achieve this in CSS, we need to add a gradient mask to fade the content at the beginning and end.
I like to do this in a CSS gradient and see the result before applying it as a mask, which helps visualize the gradient before using it as a mask.
.c-card__footer {
background-image: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent 100%);
}
With the above content, we can apply it as a mask.
.c-card__footer {
mask-image: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent 100%);
}
Not perfect yet? We can fine-tune the gradient values until the result is perfect.
Masking text content: Example 2#
This is similar to the previous example but applied vertically. I have seen this in live videos on Instagram.
Consider the following image:
Notice how the content is faded out from the top? This small area can have feed comments, actions, and other content. Using CSS masking is perfect for this.
First, let's look at the gradient.
In CSS, it might look like this:
.whatever-the-class-name {
mask-image: linear-gradient(to bottom, transparent, #000);
}
Masking a list#
I saw this cool example while researching CSS masking. The idea is that we have a series of features, courses, or anything else, and we want to fade the text to make users more curious about the content within.
Consider the following example:
You can see a list on the left, fading at the bottom. Using CSS masking is perfect for this because it can blend with the background below, whether it's an image or a dark background.
Let's take a look at the mask gradient to understand how it works.
In CSS, it looks like this:
.list {
mask-image: linear-gradient(to bottom, #000, transparent 95%);
}
Fun image effects#
The possibilities for creating visual effects using CSS masking and gradients are endless. Here’s a simple example of how we can create a visual effect for an image.
This is a quick design I made for this demonstration.
The image effect you see includes 5 linear gradients. By adding different gradient positions for each gradient, we can achieve a similar effect.
Let’s take a closer look at the gradients:
.thumb {
mask-image: linear-gradient(to bottom, #000, #000),
linear-gradient(to bottom, #000, #000),
linear-gradient(to bottom, #000, #000),
linear-gradient(to bottom, #000, #000),
linear-gradient(to bottom, #000, #000);
mask-size: 18% 70%;
mask-position: 0 100%, 25% 25%, 50% 50%, 75% 0, 100% 50%;
mask-repeat: no-repeat;
}
Visually, the mask looks like this:
To create a fade effect on each rectangle's mask, we need to update each gradient and include the "transparent" keyword.
.thumb {
mask-image: linear-gradient(to bottom, transparent, #000),
linear-gradient(to bottom, #000, transparent),
linear-gradient(to bottom, transparent, #000),
linear-gradient(to bottom, #000, transparent),
linear-gradient(to bottom, transparent, #000);
mask-size: 18% 70%;
mask-position: 0 100%, 25% 25%, 50% 50%, 75% 0, 100% 50%;
mask-repeat: no-repeat;
}
We can also animate the mask size or position on hover.
This is powerful; imagine combining it with scroll-based animations, and things could get out of control (in a good way).
Rounded tabs#
I considered trying CSS mask for a UI effect called rounded tabs.
The idea is that we want to round the sides of an element in a way that blends with the element's border-radius
.
In this blog post, Chris Coyier explains a technique achieved using multiple pseudo-elements. A more dynamic solution is to use CSS mask.
First, let’s take a closer look at the shape I want to achieve.
The shape consists of a square and a circle, and what we need is their intersection.
How to achieve this? We can use multiple masks and perform compositing operations on them using the mask-composite
property.
First, we need to create an element to hold the mask.
.nav-item.active:before {
content: "";
position: absolute;
left: 100%;
bottom: 0;
width: 24px;
height: 24px;
background-color: var(--active-bg);
}
In this space, we need to draw a circle and a square to combine them. Fortunately, we can achieve this by blending linear and radial gradients.
.nav-item.active:before {
content: "";
position: absolute;
left: 100%;
bottom: 0;
width: 24px;
height: 24px;
background-color: var(--active-bg);
background-image: linear-gradient(to top, #000, #000),
radial-gradient(circle 15px at center, #000 80%, transparent 81%);
background-size: 12px 12px, 100%;
background-position: bottom left, center;
background-repeat: no-repeat, repeat;
}
Note the following:
- I added
12px 12px
as the size for the square. - The square is positioned at the bottom left.
- The square shape does not need to repeat.
The above is just to visually illustrate the effect of the two gradients. The next step is to implement them! In CSS masking, we can use the mask-composite
property to combine the two shapes.
.nav-item.active:before {
content: "";
position: absolute;
left: 100%;
bottom: 0;
width: 24px;
height: 24px;
background-color: var(--active-bg);
mask-image: linear-gradient(to top, red, red),
radial-gradient(circle 15px at center, green 80%, transparent 81%);
mask-size: 12px 12px, 100%;
mask-position: bottom left, center;
mask-repeat: no-repeat, repeat;
mask-composite: subtract;
}
This is the CSS for the shape above on the right side. For the other, we just need to change the mask-position
to flip it.
.nav-item.active:after {
/* other styles */
mask-position: bottom right, center;
}
Multiple avatar cropping#
In my article on cut-out effects, I explored different methods of creating cut-out effects using CSS.
One of the examples is perfect for CSS masking.
Using CSS masking, we can achieve this effect using a radial gradient.
.avatar {
-webkit-mask-image: radial-gradient(ellipse 54px 135px at 11px center, #0000 30px, #000 0);
}
I highly recommend reading this article as it contains many detailed examples like this one.
Conclusion#
When I started learning CSS masking, resources were limited, and more importantly, there were very few practical use cases that we could use in our daily workflow. I hope this article helps you know where to use CSS masking in your next project.
By the way, I haven't delved into properties like mask-mode
because, to be honest, I haven't found problems to solve with them (until now). When I have a more compelling example of using this property, I will update this article.
More resources#
If you want to improve your skills with more CSS masking examples, you must check out the following:
- A fancy hover effect for your avatar by Temani Afif
- Mask Compositing: A Crash Course by Ana Tudor
- Speech bubble created using only CSS by Temani Afif
- Cool gradient border hover effect by Temani Afif
Thank you for reading.