mask-image: Fade Out Edges Without Baking Gradients into Assets
You know the "read more" fade - article text dissolving into transparency above the expand button. The common implementation is a white gradient overlay div, which works exactly until dark mode ships and the white smear glows. The correct tool fades the element itself:
.collapsed-article {
max-height: 220px;
overflow: hidden;
mask-image: linear-gradient(to bottom, black 60%, transparent 100%);
}
A mask maps alpha: where the mask is opaque (black), the element shows; where transparent, it vanishes; the gradient between is the fade. Because it's true transparency, any background shows through - dark mode, photos, patterns, no overlay color to maintain. Delete the gradient div.
The scrollable-row hint
Horizontally scrolling tabs and chip rows need to communicate "there's more" - fading the clipped edges does it wordlessly:
.chip-row {
overflow-x: auto;
mask-image: linear-gradient(to right,
transparent, black 24px, black calc(100% - 24px), transparent);
}
Both edges soften; content slides under the fade as you scroll. (The refined version toggles each fade off at the scroll extremes with a few lines of JS - or scroll-driven animations can do it CSS-only in Chromium.) Pairs beautifully with scroll-snap rows.
Icon systems: one SVG, every color
.icon {
width: 20px; height: 20px;
background: currentColor; /* the color comes from text color */
mask-image: url(icons/download.svg);
mask-size: contain;
mask-repeat: no-repeat;
}
The SVG's shape becomes a stencil over a currentColor block - the icon inherits text color through hovers, themes and states with zero SVG editing, no font-icon hacks, no inline-SVG templating. This is how several design systems ship icons now.
Layering and the fine print
- Masks stack like backgrounds: comma-separate to combine (fade bottom AND right), with mask-composite controlling how layers combine when you need intersection rather than union.
- Sizing follows background conventions: mask-size, mask-position, mask-repeat all work as expected.
- Unprefixed support is universal since late 2023 (Chromium 120); before that Chromium wanted -webkit-mask-*. If your audience skews old-Chrome, ship both spellings - the prefixed ones are harmless duplicates.
- Distinguish from clip-path: clips are hard-edged geometry, masks are soft alpha. Fades = mask; shapes = clip-path.
Three recipes, one property, and a whole category of overlay-div hacks retired. The fade you add to truncated content today will survive every future theme unchanged - that's the sign you picked the right layer for the effect.
Full-stack web developer sharing practical tutorials and building tools that ship.
Got something on your mind?
My inbox is open - no forms disappearing into the void here.
- Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
- Hire me for a project Have something custom in mind? Let's talk scope and timelines.
- Product support Bought something here? I'll help you get it running.
I usually reply within 1-2 business days.