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, how to combine them, real-world use cases, and the difference between filter and backdrop-filter.
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);
}
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);
/* Completely black */
filter: brightness(0);
/* 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 (makes light areas lighter and dark areas darker). 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);
/* Flat grey */
filter: contrast(0);
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);
filter: grayscale(100%);
/* 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 — same result as grayscale(1).
/* Unchanged */
filter: saturate(1);
/* Vivid, oversaturated colours */
filter: saturate(2);
/* Muted, desaturated look */
filter: saturate(0.4);
/* Greyscale */
filter: saturate(0);
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). This is useful for colour-shifting effects without editing the original image.
/* 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);
filter: invert(100%);
/* Partial inversion */
filter: invert(0.5);
One practical use: a simple dark mode icon switcher. If you have a black icon on a light background, you can invert it to white for dark mode without swapping image files.
@media (prefers-color-scheme: dark) {
.logo-img {
filter: invert(1);
}
}
opacity()
Works the same as the opacity CSS property — 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);
The most common use case is applying a realistic shadow to a PNG image with a transparent background — something box-shadow cannot do correctly:
/* 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));
}
Combining Multiple Filters
You can chain multiple filter functions in a single filter declaration. They are applied in order from left to right. The same override rule from transform applies — never write two separate filter declarations, as 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 — otherwise there is nothing to "see through." 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);
}
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 — divs, text, buttons, SVGs, videos, and more. 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 — 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.