Fade edges with a mask
Fade a moving edge with mask-image, not a hard overflow cut.
Emil Kowalski@emilkowalskia simple overflow: hidden wouldn't work as it would break the animation. But today I had an epiphany that we could just use a simple CSS mask and fade the items out.
From emil-design-eng; demo 1 rebuilds Linear’s nested menu.
Fade only the scrollable side
Scroll the strips sideways, or add a tab.
overflow-x: autoBad
mask-image: noneMask, scrollable sideGood
--left --right Selected: Overview.
.tabs {
overflow-x: auto;
mask-image: linear-gradient(
to right,
transparent 0,
black var(--left, 0px),
black calc(100% - var(--right, 0px)),
transparent 100%
);
}
tabs.addEventListener("scroll", () => {
const max = tabs.scrollWidth - tabs.clientWidth;
tabs.style.setProperty("--left", `${Math.min(tabs.scrollLeft, 32)}px`);
tabs.style.setProperty("--right", `${Math.min(max - tabs.scrollLeft, 32)}px`);
}, { passive: true });
Why not a gradient overlay?
Switch the background, then scroll both lists.
Gradient overlayBad
linear-gradient(transparent, var(--card))mask-imageGood
linear-gradient(transparent, black)Background: dot grid.
/* Overlay: only correct when the background is one flat color. */
.list-wrap::after {
background: linear-gradient(transparent, var(--card));
}
/* Mask: fades the content itself, whatever is behind it. */
.list {
overflow-y: auto;
mask-image: linear-gradient(
to bottom,
transparent 0,
black var(--top, 0px),
black calc(100% - var(--bottom, 0px)),
transparent 100%
);
}
Why it matters, pros and cons
Why it matters
A hard edge slices glyphs mid-letter. A mask fades them before the edge.
Pros
- One declaration, no extra DOM.
- Works over images, gradients and patterns.
- A faded side hints "more this way".
Cons
- Shadows and focus rings near the edge fade too.
- Fades content at rest; keep it inside the padding.
- Nothing crosses the edge? Use
text-overflow: ellipsis.