Filters, blending and masks

Use filters, blend modes and masks — and know what each one costs.

21of 26
≈ 10Min read
8Parts

filter

A filter applies to the element and everything inside it, including its children and its text. That is often the surprise: a blur on a card blurs the card's text too.

the functions
filter: blur(8px);
filter: brightness(1.2);
filter: contrast(1.15);
filter: saturate(0.4);
filter: grayscale(1);
filter: sepia(0.3);
filter: hue-rotate(30deg);
filter: invert(1);
filter: opacity(0.6);
filter: drop-shadow(0 8px 16px rgb(0 0 0 / .3));

/* they compose, left to right */
filter: saturate(1.1) contrast(1.05) brightness(0.96);

drop-shadow versus box-shadow

For a transparent PNG or an inline SVG, box-shadow draws a rectangle behind it and drop-shadow traces the shape. It is the only way to shadow something that is not a box.

the difference is the shape
/* follows the rectangular box */
box-shadow: 0 8px 16px rgb(0 0 0 / .3);

/* follows the actual opaque pixels — a PNG cut-out, an SVG icon, a clipped shape */
filter: drop-shadow(0 8px 16px rgb(0 0 0 / .3));

backdrop-filter

backdrop-filter filters what is behind the element rather than the element itself. It is the whole of the frosted-glass look.

glass
.glass {
  background: rgb(255 255 255 / .06);
  backdrop-filter: blur(24px) saturate(160%);
  border: 1px solid rgb(255 255 255 / .12);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .11);
}
It is expensive

Every frame, the browser has to read back what is behind the element and blur it. One fixed header is fine. A backdrop-filter on a list of cards that scrolls will drop frames on a phone. And it needs something translucent in the background to be visible at all.

Blend modes

two different properties
/* blend this ELEMENT with what is painted behind it in its stacking context */
.overlay { mix-blend-mode: multiply; }

/* blend an element's own background LAYERS with each other */
.hero {
  background-image: url("/media/photo.jpg"), linear-gradient(#c2452d, #14161a);
  background-blend-mode: luminosity;
}
a duotone image, no editing
.duotone {
  background: linear-gradient(#f1521f, #1b2a6b);
}

.duotone img {
  mix-blend-mode: luminosity;
  opacity: 0.9;
}
ModeDoes
multiplyDarkens. White becomes transparent — good for ink on paper.
screenLightens. Black becomes transparent — good for light leaks and glows.
overlay / soft-lightAdds contrast, keeping highlights and shadows.
luminosityTakes the lightness of the top and the colour of the bottom — a duotone.
differenceInverts against the backdrop. The trick behind text that stays legible over anything.

isolation

mix-blend-mode blends with everything behind the element in its stacking context — which can mean the whole page background. isolation: isolate on the parent creates a new stacking context so the blending stops there.

keep the blend inside the card
.card {
  isolation: isolate;
}

.card .tint {
  mix-blend-mode: multiply;
}

clip-path

Anything outside the shape is not painted and not clickable. A clip-path also flattens 3D transforms on the same element.

cut a shape out of a box
clip-path: inset(10% 20% round 12px);
clip-path: circle(50% at 50% 50%);
clip-path: ellipse(40% 30% at 50% 40%);
clip-path: polygon(0 0, 100% 0, 100% 80%, 0 100%);   /* an angled bottom edge */

/* animatable, as long as both ends have the same shape function and point count */
.reveal { clip-path: inset(0 100% 0 0); transition: clip-path 600ms ease-out; }
.reveal.in { clip-path: inset(0 0 0 0); }

mask

A mask uses the alpha of the mask image: opaque areas show, transparent areas hide. Unlike an overlay it works over any background, because it removes pixels rather than painting on top of them.

fade an edge without a gradient overlay
.fade-bottom {
  mask-image: linear-gradient(#000 70%, transparent);
}

/* a spotlight that follows a custom property */
.lens {
  mask-image: radial-gradient(circle 12rem at var(--mx, 50%) var(--my, 50%), #000 60%, transparent);
}

What each one costs

  • filter and mask — paint, usually on the GPU. Fine on small or few elements.
  • backdrop-filter — read-back plus blur every frame. Use once or twice per page, on something that does not move much.
  • mix-blend-mode — forces the browser to keep the backdrop available; it disables some compositing optimisations.
  • All of them create a stacking context, and filter also creates a containing block for fixed children.
  • Animating filter: blur() is one of the most expensive things you can do. Animate opacity between a blurred and an unblurred copy instead.

Practice