search

CSS mask-image: Gradients, Shapes & Creative Effects

The CSS clip-path property draws a hard boundary – everything outside disappears. But what if you want content to gradually fade, dissolve through a pattern, or reveal itself through a shaped window? That’s where mask-image comes in.

I use mask-image more and more in my projects. It gives you control over visibility using gradients, images, and SVGs as mask layers. The result is soft fades, creative shapes, and effects that clip-path simply cannot produce.

What mask-image Does

The mask-image property works through the alpha channel. You provide an image (gradient, SVG, PNG) and the browser uses its transparency to determine what stays visible.

Black (opaque) areas of the mask = fully visible. Transparent areas = hidden. Everything in between creates partial transparency.

The mask CSS shorthand property hides an element (partially or fully) by masking or clipping the image at specific points. It combines mask-image with other mask sub-properties like mask-size, mask-position, and mask-composite.

MDN Web Docs

The basic syntax looks like this:

.element {
    -webkit-mask-image: linear-gradient(black, transparent);
    mask-image: linear-gradient(black, transparent);
}

You can use any CSS gradient function as the mask source. No external files needed.

Gradient Masks

Gradients are the most practical mask source. You already know linear-gradient(), radial-gradient(), and conic-gradient() from backgrounds. They work identically as masks, but instead of painting color, they control transparency.

A linear-gradient mask fading from black to transparent creates a smooth fade-out. A radial-gradient creates a spotlight or vignette. A conic-gradient creates a pie-chart style reveal.

Play with the controls below to see how each type behaves:

Gradient Mask Playground
Angle 180°
Fade start 0%
Fade end 100%
Mask alpha

The dot-grid background behind the preview makes masked-away areas immediately obvious. Try switching to radial and pulling the fade-start slider up to create a vignette effect.

Image and SVG Masks

Gradients are convenient, but SVG and image masks let you create arbitrary shapes. Any SVG path becomes a mask source through an inline data URI or external file reference.

The key related properties:

  • mask-size – works like background-size (e.g., contain, cover, or pixel values)
  • mask-position – works like background-position
  • mask-repeat – set to no-repeat for single masks, or let it tile
.element {
    -webkit-mask-image: url('mask-shape.svg');
    mask-image: url('mask-shape.svg');
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

Click a shape below to apply it as a mask, then experiment with mask-size and mask-repeat to see how the sub-properties affect the result:

SVG Shape Mask Gallery

Each shape is a simple SVG <path> or <polygon> encoded as a data URI. The gradient underneath bleeds through whatever the SVG shape defines as solid. This technique works well for avatars, decorative sections, and product image treatments.

Combining Multiple Masks with mask-composite

You can stack multiple mask layers, just like multiple backgrounds. The mask-composite property controls how those layers interact. Four modes are available:

  • add – union of both masks (default). All masked areas from both layers are visible.
  • subtract – the second mask cuts away from the first
  • intersect – only the overlap between both masks remains visible
  • exclude – only non-overlapping areas are visible

The standard mask-composite values (add, subtract, intersect, exclude) differ from the -webkit-mask-composite values (source-over, source-out, source-in, xor). Always include both for cross-browser compatibility.

Switch between modes below to see how a radial gradient mask and a diagonal stripe mask combine:

mask-composite Explorer
Layer 1: Circle
Layer 2: Stripes
=
mask-composite: add

The intersect mode is especially useful for creating complex shapes from simple gradients. Two basic gradients combined with intersect can produce shapes that would be tedious to draw manually.

Fade-Out Scroll Edges

Fading the edges of scrollable containers is probably the mask-image use case I run into most. Instead of a hard cut-off, content dissolves at the boundary, hinting that more is available above or below.

Apply a linear-gradient mask that goes from transparent at the edges to black in the middle. The gradient stop positions control how far the fade extends.

.scrollable {
    -webkit-mask-image: linear-gradient(
        to bottom,
        transparent 0px,
        black 40px,
        black calc(100% - 40px),
        transparent 100%
    );
    mask-image: linear-gradient(
        to bottom,
        transparent 0px,
        black 40px,
        black calc(100% - 40px),
        transparent 100%
    );
}

Try adjusting the fade size and position in the terminal-styled demo below:

Scroll Fade
1// server.js - Node application entry point
2const express = require('express');
3const app = express();
4
5// Middleware
6app.use(express.json());
7app.use(express.static('public'));
8
9// Routes
10app.get('/api/status', (req, res) => {
11 res.json({ status: 'ok', uptime: process.uptime() });
12});
13
14app.get('/api/users', async (req, res) => {
15 const users = await db.query('SELECT * FROM users');
16 res.json(users);
17});
18
19app.post('/api/users', async (req, res) => {
20 const { name, email } = req.body;
21 const result = await db.query(
22 'INSERT INTO users (name, email) VALUES ($1, $2)',
23 [name, email]
24 );
25 res.json({ id: result.insertId });
26});
27
28// Error handler
29app.use((err, req, res, next) => {
30 console.error(err.stack);
31 res.status(500).json({ error: 'Something went wrong' });
32});
33
34app.listen(3000, () => {
35 console.log('Server running on port 3000');
36});
Fade size 40px

Navigation menus, dropdown lists, chat windows, sidebars. It works anywhere content overflows a container. No JavaScript needed for the mask itself, just CSS.

Image Hover Reveal Effect

One of my favorite mask-image tricks is the flashlight reveal. A grayscale layer sits underneath, and a color layer on top is masked by a radial-gradient that follows the mouse cursor. The result: a spotlight that reveals the full-color version only where you hover.

The JavaScript updates mask-image on every mousemove, repositioning the radial gradient to follow the cursor:

container.addEventListener('mousemove', function(e) {
    var rect = container.getBoundingClientRect();
    var x = ((e.clientX - rect.left) / rect.width) * 100;
    var y = ((e.clientY - rect.top) / rect.height) * 100;

    colorLayer.style.maskImage =
        'radial-gradient(circle 120px at ' + x + '% ' + y + '%, black 0%, transparent 100%)';
});

Move your mouse over the area below. Toggle between Flashlight and Wipe modes, and adjust the reveal size:

Hover Reveal
Hover to reveal
Size 120px

The amber glow ring at the edge of the reveal circle is itself a mask trick. A separate layer uses a ring-shaped radial gradient mask, creating a glowing border effect without any box-shadow or filter.

Text Knockout with mask-image

Text reveals are where mask-image really shines. A diagonal gradient mask sweeps across, progressively revealing characters. The trick: set mask-size: 300% 100% and animate mask-position from one end to the other.

The shimmer trail you see behind the sweep edge is a second layer. It uses a narrow gradient on the same animation timing, blended with mix-blend-mode: screen.

.text-reveal {
    -webkit-mask-image: linear-gradient(110deg, black 0%, black 0%, transparent 10%, transparent 100%);
    mask-image: linear-gradient(110deg, black 0%, black 0%, transparent 10%, transparent 100%);
    -webkit-mask-size: 300% 100%;
    mask-size: 300% 100%;
    animation: sweep 3s ease-in-out forwards;
}

@keyframes sweep {
    from { mask-position: 100% 0; }
    to   { mask-position: 0% 0; }
}

Hit play to see the cinematic text reveal in action:

Cinematic Text Reveal
Unmask Your Creativity
Speed 3.0s
Scrub 0%
Mask

I’ve used this on hero sections and portfolio headers. The gradient text underneath (background-clip: text) adds another layer of depth.

mask-image vs clip-path

Both properties hide parts of an element. They solve different problems, though:

Aspectmask-imageclip-path
Edge typeSoft fades and gradientsHard edges only
SourceGradients, images, SVGShapes, polygons, SVG path
Multi-layerYes, with mask-compositeNo
AnimationGradient positions, mask-sizeShape points, inset values
TransparencyPartial transparency supportedBinary: visible or hidden
Use caseFades, reveals, texture masksGeometric shapes, cutouts

Use clip-path when you need crisp geometric shapes. Use mask-image when you need soft transitions, pattern-based visibility, or multiple composited layers.

Browser Support

mask-image reached Baseline status in December 2023. All modern browsers support it fully.

Property
Chrome
Firefox
Safari
Edge
mask-image
120+
53+
15.4+
120+
mask-composite
120+
53+
15.4+
120+

Always include both the -webkit-mask-image prefixed version and the standard mask-image property. Older Safari versions (before 15.4) only support the prefixed variant. The same applies to mask-size, mask-position, mask-repeat, and mask-composite (which uses -webkit-mask-composite with different values like source-over instead of add).

FAQs

Common questions about the CSS mask-image property:

What is the difference between mask-image and clip-path?
mask-image uses gradients or images to control element visibility through the alpha channel, allowing soft fades and partial transparency. clip-path uses geometric shapes to create hard-edged cutouts. Use mask-image for gradual transitions and clip-path for crisp shapes.
Do I need the -webkit-mask-image prefix?
Yes. While the unprefixed mask-image is supported in Chrome 120+, Firefox 53+, Safari 15.4+, and Edge 120+, older Safari versions require the -webkit-mask-image prefix. Always include both for maximum compatibility.
Can I use CSS gradients as mask-image values?
Yes. linear-gradient(), radial-gradient(), and conic-gradient() all work as mask-image values. Black areas in the gradient make the element visible, and transparent areas hide it. This is the most common approach since it requires no external files.
How does mask-composite work?
mask-composite controls how multiple mask layers combine. The four standard values are add (union), subtract (cut second from first), intersect (overlap only), and exclude (non-overlapping areas only). Note that -webkit-mask-composite uses different value names: source-over, source-out, source-in, and xor.
Can I animate mask-image?
You cannot directly animate the mask-image value itself. However, you can animate mask-position and mask-size to create smooth reveal effects, or update mask-image via JavaScript on events like mousemove for interactive effects.
What is the difference between mask-mode alpha and luminance?
The mask-mode property determines how the mask source is interpreted. With alpha (the default for images and gradients), the alpha channel controls visibility. With luminance, bright areas (white) are visible and dark areas (black) are hidden. Luminance mode is useful when working with SVG masks referenced via url().

Summary

mask-image fills a gap that no other CSS property covers: gradient-based, partial-transparency control over what the user sees. I reach for it whenever a hard-edged clip-path would look too blunt. Scroll fades, hover spotlights, text sweeps, pattern overlays.

Browser support has been solid since December 2023. Include the -webkit- prefix alongside the standard property, match the -webkit-mask-composite value names, and you are set.

Join the Discussion
0 Comments  ]

Leave a Comment

To add code, use the buttons below. For instance, click the PHP button to insert PHP code within the shortcode. If you notice any typos, please let us know!

Savvy WordPress Development official logo