The CSS filter property applies visual effects directly to elements: blurring images, adjusting brightness, shifting colours, adding drop shadows, and more. All of this is done entirely in CSS with no image editing software needed. This guide covers every filter function in the list below, backdrop-filter, filter: url(), Tailwind's filter classes, and practical patterns like blurring only a background image.

FunctionExampleNo-op valueWhat it does
blur()filter: blur(4px)blur(0)Gaussian blur
brightness()filter: brightness(1.2)brightness(1)Lightens or darkens
contrast()filter: contrast(1.5)contrast(1)Widens or flattens light/dark range
grayscale()filter: grayscale(1)grayscale(0)Converts to greyscale
saturate()filter: saturate(2)saturate(1)Boosts or mutes colour intensity
hue-rotate()filter: hue-rotate(90deg)hue-rotate(0deg)Shifts colours around the colour wheel
invert()filter: invert(1)invert(0)Flips colours to their opposite
opacity()filter: opacity(0.6)opacity(1)Transparency, combinable with other filters
sepia()filter: sepia(1)sepia(0)Warm vintage tone
drop-shadow()filter: drop-shadow(2px 2px 4px #000)noneShadow that follows the element's alpha shape
url()filter: url(#custom-filter)noneReferences a custom SVG <filter>

Every function in the table above works with filter. The same list also works with backdrop-filter, which is covered in its own section further down, and applies the effect to whatever is visible behind an element instead of the element itself.

How the CSS filter property works

The filter property accepts one or more filter functions and applies them to the element and everything inside it, including text, borders, and child elements. It works on any HTML element, not just images.

/* Basic syntax */
.element {
  filter: function(value);
}

/* Multiple filters */
.element {
  filter: brightness(1.2) contrast(1.1) saturate(1.3);
}

Like transform, filter creates a new stacking context and is GPU-accelerated, which means it performs well in animations and transitions.

blur()

Applies a Gaussian blur to the element. The value is a length (in px, rem, etc.): the larger the value, the stronger the blur. A value of 0 produces no blur.

/* Subtle blur */
filter: blur(2px);

/* Heavy blur */
filter: blur(10px);

/* No blur */
filter: blur(0);

Common use cases:

  • Blurring a background image behind a card or modal
  • Hiding spoiler content until hover
  • Creating depth-of-field effects in hero sections
  • Loading state placeholders (blur the image, then remove the filter when loaded)
/* Spoiler reveal on hover */
.spoiler img {
  filter: blur(12px);
  transition: filter 0.4s ease;
}

.spoiler:hover img {
  filter: blur(0);
}

How to blur a background image without blurring the content

A frequent mistake: applying filter: blur() straight to a container that also holds text or other content. Since filter affects the element and everything inside it, the text gets blurred too. To blur only the background, put the image on its own layer behind the content.

/* Blurred background image, sharp text on top */
.hero {
  position: relative;
  overflow: hidden;
}

.hero::before {
  content: "";
  position: absolute;
  inset: -20px; /* slightly oversized so blurred edges stay off-screen */
  background-image: url("hero.jpg");
  background-size: cover;
  filter: blur(10px);
}

.hero-content {
  position: relative; /* stacks above the ::before layer */
  z-index: 1;
}

Oversizing the blurred layer with a negative inset (or top/right/bottom/left) stops the soft, semi-transparent edge that a Gaussian blur produces from showing at the container's border.

If the background is a separate positioned element rather than an image on the same box, backdrop-filter is usually the simpler tool: it blurs whatever is visible behind an element without needing a separate pseudo-element layer. See the backdrop-filter section below for that approach.

brightness()

Adjusts how bright the element appears. A value of 1 is the original brightness. Values above 1 make it brighter, values below 1 make it darker. A value of 0 renders the element completely black.

/* Original brightness */
filter: brightness(1);

/* 20% brighter */
filter: brightness(1.2);

/* 50% darker */
filter: brightness(0.5);
/* Dim an image on hover to show overlay text */
.card img {
  transition: filter 0.3s ease;
}

.card:hover img {
  filter: brightness(0.6);
}

contrast()

Adjusts the difference between the lightest and darkest parts of the element. A value of 1 is unchanged. Values above 1 increase contrast. Values below 1 flatten contrast. A value of 0 produces a solid grey image.

/* Unchanged */
filter: contrast(1);

/* Higher contrast */
filter: contrast(1.5);

/* Washed out, low contrast */
filter: contrast(0.5);

brightness and contrast are often combined. Increasing brightness slightly and boosting contrast is a common photo enhancement technique: filter: brightness(1.1) contrast(1.2).

grayscale()

Converts the element to greyscale. A value of 0 is the original colour, 1 (or 100%) is fully greyscale. Values in between give a partial desaturation effect.

/* Fully greyscale */
filter: grayscale(1);

/* 50% desaturated */
filter: grayscale(0.5);

/* Original colour */
filter: grayscale(0);
/* Greyscale partner logos that colour on hover */
.partner-logo {
  filter: grayscale(1);
  opacity: 0.6;
  transition: filter 0.3s ease, opacity 0.3s ease;
}

.partner-logo:hover {
  filter: grayscale(0);
  opacity: 1;
}

saturate()

Controls the intensity of colours. A value of 1 is unchanged. Values above 1 make colours more vivid. Values below 1 desaturate towards grey. A value of 0 is fully greyscale, the same result as grayscale(1).

/* Unchanged */
filter: saturate(1);

/* Vivid, oversaturated colours */
filter: saturate(2);

/* Muted, desaturated look */
filter: saturate(0.4);

hue-rotate()

Rotates all colours of the element around the colour wheel by a given angle in degrees. A rotation of 0deg is unchanged. A rotation of 360deg is also unchanged (full circle).

/* No change */
filter: hue-rotate(0deg);

/* Shift colours 90 degrees */
filter: hue-rotate(90deg);

/* Shift colours 180 degrees (inverts hue) */
filter: hue-rotate(180deg);
/* Animated rainbow hue shift */
@keyframes hue-cycle {
  from { filter: hue-rotate(0deg); }
  to   { filter: hue-rotate(360deg); }
}

.rainbow-icon {
  animation: hue-cycle 3s linear infinite;
}

invert()

Inverts the colours of the element. A value of 0 is unchanged, 1 is fully inverted. This swaps each colour to its opposite on the colour wheel: white becomes black, red becomes cyan, and so on.

/* Full colour inversion */
filter: invert(1);

/* Partial inversion */
filter: invert(0.5);
/* Simple dark mode icon switcher, no image swap needed */
@media (prefers-color-scheme: dark) {
  .logo-img {
    filter: invert(1);
  }
}

opacity()

Works the same as the opacity CSS property: it sets the transparency of the element from 0 (invisible) to 1 (fully visible). The difference is that as a filter function, it can be combined with other filters in a single declaration.

/* As a standalone property */
opacity: 0.5;

/* As a filter, useful when combining with other filters */
filter: grayscale(1) opacity(0.6);

sepia()

Applies a sepia tone, a warm brownish tint associated with old photographs. A value of 0 is unchanged, 1 is fully sepia.

/* Full sepia effect */
filter: sepia(1);

/* Subtle vintage warm tone */
filter: sepia(0.3) contrast(1.1);

drop-shadow()

Applies a shadow that follows the actual shape of the element, including transparency. This is the key difference from box-shadow, which always follows the element's rectangular bounding box.

/* Syntax: drop-shadow(offset-x offset-y blur-radius colour) */
filter: drop-shadow(4px 4px 8px rgba(0, 0, 0, 0.3));

/* No spread radius: unlike box-shadow, drop-shadow has no 4th size value */
filter: drop-shadow(2px 2px 4px #000);
/* Shadow follows the shape of the PNG, not its bounding box */
.product-image {
  filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.25));
}

url(): custom effects with an SVG filter

url() is the one filter function that does not come with a built-in effect. Instead it points to a <filter> element defined in SVG, letting you build effects the standard functions cannot produce, such as film grain, noise, or a custom blur matrix.

/* Reference an SVG filter by id */
.grainy {
  filter: url(#grain);
}
<!-- Inline SVG defining a film-grain / noise filter -->
<svg width="0" height="0">
  <filter id="grain">
    <feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="2" />
    <feColorMatrix type="saturate" values="0" />
    <feComponentTransfer><feFuncA type="linear" slope="0.15" /></feComponentTransfer>
    <feComposite operator="over" in2="SourceGraphic" />
  </filter>
</svg>

There is no native CSS grain, noise, or multiply filter function. Grain and noise are built with an SVG <feTurbulence> filter referenced through url(), as shown above. A multiply effect is not a filter at all, it comes from mix-blend-mode: multiply on the element instead.

Combining multiple filters

You can chain multiple filter functions in a single filter declaration. They are applied in order from left to right. Never write two separate filter declarations: the second will overwrite the first.

/* Instagram-style vintage effect */
.vintage {
  filter: sepia(0.4) contrast(1.1) brightness(1.05) saturate(0.8);
}

/* Faded, washed-out look */
.faded {
  filter: brightness(1.1) contrast(0.85) saturate(0.7);
}

/* Dark moody look */
.moody {
  filter: brightness(0.85) contrast(1.2) saturate(0.9);
}

backdrop-filter: blurring what is behind an element

backdrop-filter works like filter, but instead of affecting the element itself, it affects everything visible behind the element. This is how frosted glass effects are made in CSS.

/* Frosted glass card */
.glass-card {
  background: rgba(255, 255, 255, 0.15);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px); /* Safari prefix */
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 12px;
}

For backdrop-filter to work, the element must have a background that is at least partially transparent. Always include the -webkit-backdrop-filter prefix for Safari support.

/* Navigation bar with blur background */
.navbar {
  position: sticky;
  top: 0;
  background: rgba(255, 255, 255, 0.8);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}

Tailwind CSS filter utilities

Tailwind CSS ships utility classes for every function in the table at the top of this guide. Add a base class like filter is not required in v3+, class names apply the filter directly:

<!-- Blur, brightness and grayscale -->
<img class="blur-sm brightness-110 grayscale hover:grayscale-0 transition" />

<!-- Combine several filter utilities -->
<img class="contrast-125 saturate-150 hue-rotate-15" />

<!-- Remove all filters, e.g. on a breakpoint -->
<img class="blur-md invert md:filter-none" />

<!-- Frosted glass card with backdrop-blur-* -->
<div class="bg-white/15 backdrop-blur-md border border-white/20 rounded-xl" >...</div>

Available scale steps mirror the CSS values in the reference table: blur-xs through blur-3xl, brightness-0 through brightness-200, contrast-*, grayscale (on/off), hue-rotate-*, invert (on/off), saturate-*, sepia (on/off) and drop-shadow-*. Every one has a matching backdrop-* version for backdrop-filter. For a value outside the scale, use the arbitrary syntax filter-[blur(6px)] or backdrop-filter-[blur(6px)].

Frequently asked questions

What is the difference between filter and backdrop-filter?

filter applies effects to the element itself and all its contents. backdrop-filter applies effects to the area behind the element, the content it is sitting on top of. For a frosted glass card, you use backdrop-filter. For a greyscale image on hover, you use filter.

What is the difference between filter: drop-shadow() and box-shadow?

box-shadow always casts a shadow based on the element's rectangular bounding box. filter: drop-shadow() follows the actual visible shape of the element, including transparent cut-outs in PNG images. Use drop-shadow() when you need the shadow to follow a non-rectangular shape.

Does CSS filter work on all elements, not just images?

Yes. filter can be applied to any HTML element, including divs, text, buttons, SVGs, and videos. When applied to a container, it affects everything inside it, including all child elements and text.

Can I animate CSS filters?

Yes. CSS filters are animatable with both transition and @keyframes. The most common pattern is transitioning filter on hover, for example changing from grayscale(1) to grayscale(0) smoothly. Filters are GPU-accelerated and perform well in animations.

Why is my backdrop-filter not working?

Two common reasons: first, the element's background may be fully opaque, and backdrop-filter only works if the background is at least partially transparent. Second, Safari requires the -webkit-backdrop-filter prefix. Always include both prefixed and unprefixed versions.

Does filter affect z-index and stacking?

Yes. Applying a filter (even filter: blur(0)) creates a new stacking context on the element, similar to opacity or transform. This can affect how z-index works on child elements relative to elements outside the filtered container. If you notice unexpected layering after adding a filter, this is likely the cause.

What does filter: url() do?

filter: url() points to a <filter> element defined in SVG, letting you build custom effects that the built-in filter functions cannot produce, such as noise, turbulence, or a custom blur matrix. It has wide browser support but requires writing SVG filter primitives, so most projects only reach for it when the standard filter functions are not enough.

Do I still need the -webkit- prefix for filter?

No. The unprefixed filter property has been supported in all major browsers, including Safari, since 2017. The -webkit- prefix is only still required for backdrop-filter in older Safari versions, not for filter itself.

Is there a CSS filter for multiply, like Photoshop's Multiply blend mode?

No, multiply is not one of the filter functions. That effect comes from a different property, mix-blend-mode: multiply (or background-blend-mode for background layers), which combines an element's colours with whatever is behind it the same way Photoshop's Multiply blend mode does.

How do I blur just a background image, not the text or content on top of it?

Put the image on its own absolutely positioned pseudo-element or wrapper behind the content, then apply filter: blur() to that layer only. Applying blur() directly to a container blurs its text and children too, since filter affects the element and everything inside it. See the background-blur pattern above for the full code.

Are there Tailwind CSS classes for CSS filters?

Yes. Tailwind ships utilities like blur-sm, brightness-125, contrast-125, grayscale, hue-rotate-90, invert, saturate-150, sepia and drop-shadow-lg for the filter property, plus a matching backdrop-blur-*, backdrop-brightness-* and similar set for backdrop-filter.

Build CSS filters visually

Use our free CSS Filter Generator to adjust blur, brightness, contrast, grayscale and backdrop-filter with live preview, then copy the CSS.

Open CSS Filter Generator →