Filters & Blend Modes

CSS · Lesson 38 of 55 7 min read Updated

Photo apps on your phone can blur, brighten, fade to black and white or tint a picture. CSS can do the same to any element on a web page, live in the browser, without editing the image file. Filters change how an element itself looks, and blend modes decide how an element mixes with whatever is behind it.

In this lesson you will use the filter property, build a frosted-glass panel with backdrop-filter, and mix colours and images with mix-blend-mode and background-blend-mode.

The filter property

filter takes one or more filter functions. Each function changes the rendered pixels of the element, including all its children.

FunctionEffectExample
blur()Softens the elementblur(4px)
brightness()Lighter or darker; 1 is normalbrightness(1.2)
contrast()More or less contrast; 1 is normalcontrast(1.5)
grayscale()Removes colour; 1 is fully greygrayscale(1)
sepia()Old-photo brown tonesepia(0.8)
saturate()More or less colour strengthsaturate(2)
hue-rotate()Shifts every colour around the colour wheelhue-rotate(90deg)
invert()Reverses the coloursinvert(1)
opacity()Transparency, like the opacity propertyopacity(0.5)
drop-shadow()A shadow that follows the shapedrop-shadow(0 4px 6px black)

Most functions also accept percentages, so grayscale(100%) is the same as grayscale(1). Write several functions separated by spaces; they are applied from left to right.

A popular effect is a gallery where photos are grey until you point at them:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Filter gallery</title>
  <style>
    body { font-family: system-ui, sans-serif; margin: 16px; }
    .gallery {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
      gap: 12px;
    }
    .gallery figure { margin: 0; }
    .gallery img { width: 100%; display: block; border-radius: 8px; }
    .gallery figcaption { font-size: 0.85rem; }

    .grey   { filter: grayscale(1); transition: filter 300ms; }
    .grey:hover { filter: grayscale(0); }
    .old    { filter: sepia(0.9) contrast(1.1); }
    .bright { filter: brightness(1.3) saturate(1.6); }
    .soft   { filter: blur(3px); }
  </style>
</head>
<body>
  <div class="gallery">
    <figure><img class="grey" src="https://course.ethicscomputer.in/assets/samples/landscape-400.svg" alt="Hills and a river at sunrise"><figcaption>grayscale (hover me)</figcaption></figure>
    <figure><img class="old" src="https://course.ethicscomputer.in/assets/samples/landscape-400.svg" alt="Hills and a river at sunrise"><figcaption>sepia + contrast</figcaption></figure>
    <figure><img class="bright" src="https://course.ethicscomputer.in/assets/samples/landscape-400.svg" alt="Hills and a river at sunrise"><figcaption>brightness + saturate</figcaption></figure>
    <figure><img class="soft" src="https://course.ethicscomputer.in/assets/samples/landscape-400.svg" alt="Hills and a river at sunrise"><figcaption>blur</figcaption></figure>
  </div>
</body>
</html>

The same image file is used four times. Only the CSS differs, so the visitor downloads it once.

drop-shadow() versus box-shadow

You met box-shadow in Borders, Radius & Shadows. It always draws a shadow around the element's box, a rectangle with optional rounded corners. filter: drop-shadow() instead follows the visible shape, so a star icon gets a star-shaped shadow and a PNG or SVG with a transparent background gets a shadow only under the drawn parts.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>drop-shadow vs box-shadow</title>
  <style>
    body { font-family: system-ui, sans-serif; margin: 24px; display: flex; gap: 48px; }
    svg { width: 120px; height: 120px; }
    .box-shadow  { box-shadow: 6px 6px 8px rgb(0 0 0 / 0.4); }
    .drop-shadow { filter: drop-shadow(6px 6px 4px rgb(0 0 0 / 0.4)); }
  </style>
</head>
<body>
  <figure>
    <svg class="box-shadow" viewBox="0 0 100 100" aria-hidden="true">
      <polygon points="50,5 61,38 95,38 67,58 78,92 50,72 22,92 33,58 5,38 39,38" fill="#f59e0b"/>
    </svg>
    <figcaption>box-shadow</figcaption>
  </figure>
  <figure>
    <svg class="drop-shadow" viewBox="0 0 100 100" aria-hidden="true">
      <polygon points="50,5 61,38 95,38 67,58 78,92 50,72 22,92 33,58 5,38 39,38" fill="#f59e0b"/>
    </svg>
    <figcaption>drop-shadow()</figcaption>
  </figure>
</body>
</html>

drop-shadow() takes an x offset, a y offset, a blur and a colour. Unlike box-shadow, it has no spread value and no inset option.

backdrop-filter: frosted glass

backdrop-filter applies filters to whatever is behind an element, not to the element itself. Combined with a semi-transparent background, it creates the "frosted glass" look used on many app bars and pop-ups.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Frosted glass</title>
  <style>
    body { margin: 0; font-family: system-ui, sans-serif; }
    .hero {
      min-height: 320px;
      display: grid;
      place-items: center;
      background: url("https://course.ethicscomputer.in/assets/samples/landscape.svg") center / cover, #0f766e;
    }
    .glass {
      max-width: 280px;
      padding: 20px;
      border: 1px solid rgb(255 255 255 / 0.4);
      border-radius: 16px;
      background: rgb(255 255 255 / 0.25);
      backdrop-filter: blur(10px) saturate(1.4);
      color: #0f172a;
    }
    .glass h1 { margin: 0 0 8px; font-size: 1.4rem; }
  </style>
</head>
<body>
  <section class="hero">
    <div class="glass">
      <h1>Munnar Tea Trails</h1>
      <p>Two-day guided walks from Kochi, every weekend.</p>
    </div>
  </section>
</body>
</html>

The panel's background is only 25% white, so the image shows through, but the blur makes it soft enough for the text to stay readable. Two things to remember:

  • The effect only shows if the element's own background is at least partly transparent.
  • Make sure the text is readable even without the blur. backdrop-filter works in all current major browsers, but older Safari versions needed the prefixed -webkit-backdrop-filter and older browsers without support simply ignore it, so choose a background colour strong enough on its own.

Blend modes

A blend mode decides how the colours of two layers mix, exactly like the layer modes in Photoshop or GIMP.

  • mix-blend-mode blends an element with everything behind it on the page.
  • background-blend-mode blends an element's own background layers (images, gradients and the background colour) with each other.
ModeResult
normalNo mixing (the default)
multiplyDarkens; white disappears
screenLightens; black disappears
overlayIncreases contrast
darken / lightenKeeps the darker or lighter colour of each pixel
differenceSubtracts colours; white on white becomes black
color / luminosityTakes the colour or the brightness from the top layer
HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Blend modes</title>
  <style>
    body { margin: 16px; font-family: system-ui, sans-serif; }
    .tinted {
      height: 180px;
      border-radius: 12px;
      background-color: #7c3aed;
      background-image: url("https://course.ethicscomputer.in/assets/samples/landscape.svg");
      background-size: cover;
      background-blend-mode: luminosity;
    }
    .split {
      margin-top: 16px;
      padding: 24px;
      border-radius: 12px;
      background: linear-gradient(90deg, #111827 50%, #f9fafb 50%);
    }
    .split h2 {
      margin: 0;
      font-size: 2.5rem;
      text-align: center;
      color: white;
      mix-blend-mode: difference;
    }
  </style>
</head>
<body>
  <div class="tinted" role="img" aria-label="Hills at sunrise, tinted purple"></div>
  <div class="split"><h2>Rohtak Robotics</h2></div>
</body>
</html>

The first box keeps the brightness of the photo but takes its colour from the purple background colour, giving a one-colour "duotone" look that matches a brand. In the second box, difference makes the white heading turn black over the light half and stay white over the dark half, so it is readable on both.

If a blend affects more than you want, add isolation: isolate to a parent. Blending then stops at that parent instead of mixing with the whole page behind it.

Performance and accessibility

  • Large blur() or backdrop-filter values on big areas take a lot of work to draw and can make scrolling stutter on budget phones. Keep blurred areas small and avoid animating them.
  • Text on top of images and glass panels must still meet contrast guidelines. Check it in both light and dark themes.
  • Do not use filter: invert(1) as a quick dark mode. It also inverts photos and logos. See Dark Mode & Theming for a better way.

Common mistakes

  • Expecting filter to affect only the background. It affects the element and all its children, including text. Use backdrop-filter or put the effect on a separate element.
  • A backdrop-filter that does nothing. The element's background must be partly transparent, and there must be something behind it to blur.
  • Using box-shadow on icons. It draws a rectangle. Use drop-shadow() for shaped images.
  • Blending more than intended. mix-blend-mode mixes with everything behind it; use isolation: isolate to limit it.

Key points

  • filter applies effects such as blur(), grayscale(), brightness() and drop-shadow() to an element and its children.
  • Functions are separated by spaces and applied from left to right.
  • drop-shadow() follows the visible shape; box-shadow follows the box.
  • backdrop-filter blurs or tints what is behind a semi-transparent element, for frosted-glass effects.
  • mix-blend-mode blends an element with the page behind it; background-blend-mode blends its own background layers.
  • Keep heavy effects small, check text contrast, and never rely on invert() for dark mode.

Exercise

Create a sale banner for a clothes shop in Lucknow. Use the sample landscape image (landscape.svg from the examples above) as a background image, tint it orange with background-blend-mode: multiply and a background colour, and place a white text panel on top that uses backdrop-filter: blur(6px) with a semi-transparent background. Then add a small "50% OFF" badge with a filter: drop-shadow().

Show solution
HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Sale banner</title>
  <style>
    .banner {
      display: grid;
      place-items: center;
      min-height: 260px;
      background: url("https://course.ethicscomputer.in/assets/samples/landscape.svg") center / cover, #f97316;
      background-blend-mode: multiply;
    }
    .panel {
      padding: 16px 24px;
      border-radius: 12px;
      background: rgb(255 255 255 / 0.3);
      backdrop-filter: blur(6px);
      text-align: center;
    }
    .badge {
      display: inline-block;
      padding: 4px 10px;
      background: #dc2626;
      color: white;
      filter: drop-shadow(0 3px 3px rgb(0 0 0 / 0.4));
    }
  </style>
</head>
<body>
  <section class="banner">
    <div class="panel">
      <h1>Winter Sale in Lucknow</h1>
      <span class="badge">50% OFF</span>
    </div>
  </section>
</body>
</html>

multiply darkens the image with the orange background colour, so the white parts turn orange. The panel blurs the tinted image behind it, and the badge gets a shadow that follows its shape.

Quick Quiz

1. What does filter: grayscale(1) do?

2. A star-shaped SVG icon needs a shadow that follows the star. What should you use?

3. Why might backdrop-filter: blur(10px) show no visible effect?

4. What is the difference between mix-blend-mode and background-blend-mode?

Learn CSS with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call