Apply effects to images with CSS's mask-image property

CSS masking gives you the option of using an image as a mask layer. This means that you can use an image, an SVG, or a gradient as your mask, to create interesting effects without an image editor.

When you clip an element using the clip-path property the clipped area becomes invisible. If instead you want to make part of the image opaque or apply some other effect to it, then you need to use masking. This post explains how to use the mask-image property in CSS, which lets you specify an image to use as a mask layer. This gives you three options. You can use an image file as your mask, an SVG, or a gradient.

Browser compatibility

Most browsers only have partial support for the standard CSS masking property. You will need to use the -webkit- prefix in addition to the standard property in order to achieve the best browser compatibility. See Can I use CSS Masks? for full browser support information.

Browser Support

  • Chrome: 120.
  • Edge: 120.
  • Firefox: 53.
  • Safari: 15.4.

Source

While browser support using the prefixed property is good, when using masking to make text on top of an image visible take care of what will happen if masking is unavailable. It may be worth using feature queries to detect support for mask-image or -webkit-mask-image and providing a readable fallback before adding your masked version.

@supports(-webkit-mask-image: url(#mask)) or (mask-image: url(#mask)) {
  /* code that requires mask-image here. */
}

Masking with an image

The mask-image property works in a similar way to the background-image property. Use a url() value to pass in an image. Your mask image needs to have a transparent or semi-transparent area.

A fully transparent area will cause the part of the image under that area to be invisible. Using an area which is semi-transparent however will allow some of the original image to show through. You can see the difference in the Glitch below. The first image is the original image of balloons with no mask. The second image has a mask applied which has a white star on a fully transparent background. The third image has a white star on a background with a gradient transparency.

In this example I am also using the mask-size property with a value of cover. This property works in the same way as background-size. You can use the keywords cover and contain or you can give the background a size using any valid length unit, or a percentage.

You can also repeat your mask just as you might repeat a background image, in order to use a small image as a repeating pattern.

Masking with SVG

Rather than using an image file as the mask, you could use SVG. There are a couple of ways this can be achieved. The first is to have a <mask> element inside the SVG and reference the ID of that element in the mask-image property.

<svg width="0" height="0" viewBox="0 0 400 300">
  <defs>
    <mask id="mask">
      <rect fill="#000000" x="0" y="0" width="400" height="300"></rect>
      <circle fill="#FFFFFF" cx="150" cy="150" r="100" />
      <circle fill="#FFFFFF" cx="50" cy="50" r="150" />
    </mask>
  </defs>
</svg>

<div class="container">
    <img src="balloons.jpg" alt="Balloons">
</div>
.container img {
  height: 100%;
  width: 100%;
  object-fit: cover;
  -webkit-mask-image: url(#mask);
  mask-image: url(#mask);
}
An example of using an SVG mask

The advantage of this approach is that the mask could be applied to any HTML element, not just an image. Unfortunately Firefox is the only browser that supports this approach.

All is not lost however, as for the most common scenario of masking an image, we can include the image in the SVG.

Masking with a gradient

Using a CSS gradient as your mask is an elegant way of achieving a masked area without needing to go to the trouble of creating an image or SVG.

A simple linear gradient used as a mask could ensure that the bottom part of an image will not be too dark underneath a caption, for example.

You can use any of the supported gradient types, and get as creative as you like. This next example uses a radial gradient to create a circular mask to illuminate behind the caption.

Using multiple masks

As with background images you can specify multiple mask sources, combining them to get the effect that you want. This is particularly useful if you want to use a pattern generated with CSS gradients as your mask. These typically will use multiple background images and so can be translated easily into a mask.

As an example, I found a nice checkerboard pattern in this article. The code, using background images, looks like this:

background-image:
  linear-gradient(45deg, #ccc 25%, transparent 25%),
  linear-gradient(-45deg, #ccc 25%, transparent 25%),
  linear-gradient(45deg, transparent 75%, #ccc 75%),
  linear-gradient(-45deg, transparent 75%, #ccc 75%);
background-size:20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;

To turn this, or any other pattern designed for background images, into a mask, you will need to replace the background-* properties with the relevant mask properties, including the -webkit prefixed ones.

-webkit-mask-image:
  linear-gradient(45deg, #000000 25%, rgba(0,0,0,0.2) 25%),
  linear-gradient(-45deg, #000000 25%, rgba(0,0,0,0.2) 25%),
  linear-gradient(45deg, rgba(0,0,0,0.2) 75%, #000000 75%),
  linear-gradient(-45deg, rgba(0,0,0,0.2) 75%, #000000 75%);
-webkit-mask-size:20px 20px;
  -webkit-mask-position: 0 0, 0 10px, 10px -10px, -10px 0px;

There are some really nice effects to be made by applying gradient patterns to images. Try remixing the Glitch and testing out some other variations.

Along with clipping, CSS masks are a way to add interest to images and other HTML elements without needing to use a graphics application.

Photo by Julio Rionaldo on Unsplash.