Loading...

CSS Masking

Optional Advanced just poured newspaper columns. This session is a different optional tool: masking. You put a stencil on one box. Where the stencil is solid, you see the photo or color. Where it is empty, that box disappears. You are not splitting an article into streams, and you are not sliding icon tiles on a sprite sheet.

Direct answer

CSS masking hides parts of an element with mask-image — a PNG, an SVG, or a CSS gradient. Think of spray paint through a cut-out. mask-size, mask-repeat, and mask-position work like the background layer: cover, no-repeat, center. mask-mode chooses alpha (transparency of the mask file) or luminance (light vs dark). Use a mask when the edge should fade. Use clip-path when you want a hard circle or polygon. Keep labels and buttons fully visible.

Why a mask is not a column or a sprite

Multi-column fragments one text stream. A sprite sheet crops one bitmap with background-position so many icons share a file. A mask does neither. The element still occupies its box. Layout width and height do not shrink. You only change which pixels of that box are painted. Filters (blur(), grayscale()) change the look of every visible pixel; a mask decides which pixels remain.

A PNG as the stencil

mask-image takes url(...) the same way background-image does. A typical mask PNG is white (or opaque) where you want the photo, and transparent where you want a hole:

PNG mask with mask-image

css
.hero-photo {
width: 20rem;
height: 12rem;
background: url("/img/studio.jpg") center / cover no-repeat;
mask-image: url("/img/soft-blob.png");
mask-size: cover;
mask-repeat: no-repeat;
}

The photo is still a background (or an <img> with the same mask). The blob PNG is only the stencil. If the mask file is missing, the whole element can vanish — keep a fallback unmasked style, or test the URL.

Fade with a gradient

You do not need a second image file. A gradient mask is the everyday fade at the bottom of a card:

Fade with a gradient mask

css
.card-lead {
mask-image: linear-gradient(to bottom, #000 60%, transparent);
mask-repeat: no-repeat;
}

Black (or fully opaque) keeps the content; transparent punches a hole. The gradient lives in CSS, so there is no extra HTTP request — unlike a sprite sheet of icons. This is still not opacity on the whole card: text in the solid part stays crisp; only the masked region dissolves.

Size, repeat, and position

Treat the mask like a background layer you never see as a picture:

Size, repeat, and position

css
.badge {
mask-image: url("/img/star-mask.svg");
mask-size: 4rem 4rem;
mask-repeat: no-repeat;
mask-position: center;
}

mask-size: cover fills the box; contain keeps the stencil’s aspect ratio; lengths pin a small shape. repeat tiles the stencil (rare, and easy to overdo). Shorthand mask exists, but beginners stay safer with the longhands until the layer feels like background.

Alpha vs luminance

mask-mode answers: “Do I read transparency, or brightness?”

Luminance mask-mode on a photo

css
.portrait {
mask-image: url("/img/gray-sculpt.jpg");
mask-mode: luminance;
mask-size: cover;
}

alpha (often the default for PNG with transparency) uses the mask’s alpha channel. luminance uses light vs dark: white typically shows, black typically hides — useful when your stencil is a gray photo, not a transparent PNG. If a JPEG mask “does nothing,” you probably needed luminance. There is also mask-type on SVG <mask> elements; in CSS on HTML boxes, start with mask-mode.

Hard clip vs soft mask

clip-path cuts a geometry: circle, inset, polygon. The edge is sharp. A mask can be soft. They are not two names for one property:

Hard clip-path versus a soft mask

css
.avatar {
clip-path: circle(50%);
}

.poster {
mask-image: radial-gradient(circle, #000 55%, transparent 80%);
mask-repeat: no-repeat;
}

Avatars and tickets often want clip-path. Hero posters that dissolve into the page want a gradient mask. You can combine them, but one job per property is calmer. overflow: hidden with border-radius is a third tool: it clips descendants to the padding box. A mask paints the element itself, including its background, without rounding the layout box.

Safari and the WebKit prefix

Some Safari versions still want -webkit-mask-image (and friends) beside the standard properties:

WebKit prefix for Safari

css
.fade-photo {
-webkit-mask-image: linear-gradient(#000 70%, transparent);
mask-image: linear-gradient(#000 70%, transparent);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
}

Put the prefixed line and the standard line. Do not ship prefix-only forever. Feature queries (@supports) can wrap extras; the habit is: test the fade in Safari, not only in Chromium.

Mask vs clip vs overflow vs sprites

📊 Mask vs clip-path vs overflow vs sprites

Tool Job Poor fit
mask-image / mask-mode Soft holes, fades, shaped photos on one element Hiding a whole form; replacing column layout
clip-path Hard circle, polygon, inset silhouette Soft dissolves; newspaper columns
overflow: hidden Clip descendants to the box (often with radius) Painting a gradient stencil on the element’s own pixels
Sprites (background-position) Many icon tiles from one bitmap Stenciling a hero photo; flowing article text
Multi-column (column-*) Newspaper fill of one text stream Visibility holes in a picture

Optional Advanced already used sprites for icons and columns for prose. Masking is the stencil. Mixing all three on the same hero is how you get a photo that is both cropped like a sprite and sliced like a column.

What not to mask

Do not use a mask to “clean up” a messy layout. If a button or a label sits under the fade, people cannot read or tap it. Screen readers may still announce the text while sighted users see nothing — that is a bug, not a feature. Contrast still matters in the visible part. Decorative photos, cards, and icons are the usual targets; login fields are not.

Mask is not overflow or a sprite sheet

css
.ok-art {
mask-image: linear-gradient(to right, #000, transparent);
}

.not-a-layout-fix {
/* don't: mask the entire toolbar because it overflowed */
}

.icons {
/* sprites still use background-position — not mask-image */
background-image: url("/img/icons.png");
background-position: 0 -32px;
}

If the problem is a child sticking out of a card, overflow (or a smaller child) is the layout fix. If the problem is twelve HTTP icon files, a sprite or SVG set is the asset fix. Masking does not replace either.

Try it yourself

  1. Give a colored box mask-image: linear-gradient(to bottom, #000 50%, transparent) and watch the bottom dissolve.
  2. Swap in a small SVG or PNG stencil; set mask-size and mask-position: center.
  3. Try mask-mode: luminance on a gray JPEG mask vs alpha on a transparent PNG.
  4. Compare the same circle with clip-path: circle(50%) — notice the hard edge.
  5. Add -webkit-mask-image with the same gradient. Do not mask a form.

Common mistakes

  • Using a mask as a page layout or as overflow: hidden
  • Forgetting mask-size so a tiny stencil sits in the corner
  • Expecting a JPEG mask to punch holes without mask-mode: luminance
  • Skipping -webkit-mask-image and blaming “CSS masks” in Safari
  • Treating clip-path and mask-image as identical
  • Masking labels, captions, or buttons until contrast and hit targets die
  • Confusing this with sprites (tile offsets) or multi-column (text streams)

Summary

  • A mask is a stencil on one element: show here, hide there
  • mask-image takes a url or a gradient; size, repeat, and position mirror backgrounds
  • mask-mode: alpha vs luminance
  • clip-path is a hard geometric cut; masks can fade
  • Prefix for Safari; never mask away essential UI
  • Optional Advanced continues next with user interface (user-interface)

🧠 Test Your Knowledge

Ready to Start

Test Your Knowledge

Challenge yourself with this interactive quiz and see how well you understand the topic

❓
7
Questions
🎯
70%
To Pass
♾️
∞
Time
🔄
∞
Attempts

📝 Instructions

  • Read each question carefully
  • Select the best answer for each question
  • You can retake the quiz as many times as you want
  • Your progress will be shown at the top