Levix

Levix's zone

x
telegram

CSS Masking - CSS Masking

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 fully supporting it in Safari and Firefox.

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:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/eeb44a70603e4ebb925dab0869a72ea7~tplv-k3u1fbpfcp-zoom-1.image

We have an image and a mask. In design applications like Photoshop, we can insert the image into a gray shape, which creates a masked image.

It works by hiding certain parts of the image instead of erasing it (they're still there, just hidden).

image

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.

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a641d661248f4f41a73a0c4383b785c1~tplv-k3u1fbpfcp-zoom-1.image

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 part.

image

In Photoshop, we can add a layer mask to a group of layers, and the content within that group will be masked. It works by using the brush tool to hide part of the group, thus achieving the mask.

image

The masked content is not erased but hidden (note the group items).

Alright, the above is mostly 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 property
  • clip-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');
}

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7ad1e12dcd0b41759bce5edb160ea5ba~tplv-k3u1fbpfcp-zoom-1.image

Cool, right? This makes understanding and mastering CSS masking much easier.

Now, let's redo the initial example with CSS.

First, I need to export the shape as a png image.

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/37895d18f5fd462ab7f15b9c2fcde9c7~tplv-k3u1fbpfcp-zoom-1.image

Assuming 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 be equal to the mask image itself, as shown in the image below:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dfffd42b05e74891a90c3f25f3eeaaae~tplv-k3u1fbpfcp-zoom-1.image

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;
}

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5ee977ebbb714383a2831c21f320580c~tplv-k3u1fbpfcp-zoom-1.image

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 CSS background image configuration!

img {
    mask-image: url("shape.png");
    mask-repeat: no-repeat;
    mask-position: center;
}

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e3aa31aaa8ad4fc4880c16ad786f32c2~tplv-k3u1fbpfcp-zoom-1.image

In addition to position, we can also change the mask size, 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%;
}

image

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 you 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);
}

image

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);
}

image

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);
}

image

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 underneath.

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:

image

Using CSS masking, we can mask the homepage banner to work 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 fail when changing the main page background. See the example below:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7b134dee8bca4b839d927dad17813c5b~tplv-k3u1fbpfcp-zoom-1.image

Masking text content: Example 1#

When we want to display long text but there isn't enough space to show it fully, a solution is to fade the text at the beginning and end, then the text will animate in either direction to reveal the remaining content.

Consider the following image:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3fb3cc8f15ad4464a7c7438a3228a72a~tplv-k3u1fbpfcp-zoom-1.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.

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; this 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%);
}

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d098ef27933847d9aa0060ae8ad7fbb2~tplv-k3u1fbpfcp-zoom-1.image

With the above, 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, which I have seen in live videos on Instagram.

Consider the following image:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e430dce52b4e45809ac307323f2d5a16~tplv-k3u1fbpfcp-zoom-1.image

Notice how the content is faded out from the top? This little area can have feed comments, actions, and other content. Using CSS masking is great for doing this.

First, let's look at the gradient.

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6b61045352c6478ca4c241fcd2340887~tplv-k3u1fbpfcp-zoom-1.image

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:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/49cd372a6e0949d4be4f7c4a6625960d~tplv-k3u1fbpfcp-zoom-1.image

You can see a list on the left that fades at the bottom. Using CSS masking is great for this because it can blend with the background below, whether it’s an image or a dark background.

image

Let's take a look at the mask gradient to understand how it works.

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/766121f339d74105b00f1ad3092e02d9~tplv-k3u1fbpfcp-zoom-1.image

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.

image

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:

image

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;
}

image

We can also animate the mask size or position on hover.

css-mask-image-effect.mp4

This is powerful; imagine combining it with scroll-based animations, and things could get out of control (but 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.

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/259a4b1c180743168feaf25e855022ab~tplv-k3u1fbpfcp-zoom-1.image

In this blog post, Chris Coyier explains a technique implemented 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.

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3d51e7bdf9024e9392e7f4daebcccc10~tplv-k3u1fbpfcp-zoom-1.image

The shape consists of a square and a circle, and we need 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);
}

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/50781a08b2d64b2ead3f7dba3feaed74~tplv-k3u1fbpfcp-zoom-1.image

In this space, we need to draw a circle and a square for compositing. Fortunately, we can achieve this by mixing 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 points:

  • 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.

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4721c2f91ce64fcfa61fbb57fa1f5882~tplv-k3u1fbpfcp-zoom-1.image

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. 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;
}

Demo

Multiple avatar cropping#

In my article on cut-out effects, I explored different ways to create cut-out effects using CSS.

One of the examples is perfect for CSS masking.

Using CSS masking, we can achieve this effect with a radial gradient.

.avatar {
    -webkit-mask-image: radial-gradient(ellipse 54px 135px at 11px center, #0000 30px, #000 0);
}

I highly recommend reading that article, as there are 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 workflows. I hope this article makes you aware of where you can use CSS masking in your next project.

By the way, I didn't delve into properties like mask-mode because, to be honest, I haven't found compelling problems to solve with them (until now). When I have a more convincing 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:

Thank you for reading.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.