What Are CSS Filters?
The CSS filter property applies graphical effects to an element before it is composited into the page. Unlike Photoshop-style filters that modify the source image, CSS filters are non-destructive — the original element and its content remain unchanged; only the visual rendering is affected.
CSS filters work on any element — not just images. You can blur a <div>, desaturate a video, or boost the contrast of an entire section of your page. They are hardware-accelerated in modern browsers and can be animated with transition or @keyframes.
All CSS Filter Functions
blur()
Applies a Gaussian blur. The value is a length (not a percentage) — px, rem, or em. Larger values produce more blur:
/* Subtle blur — loading state */
.loading-image { filter: blur(4px); }
/* Heavy blur — background cover effect */
.hero-bg { filter: blur(20px); }
/* Zero blur — remove programmatically */
.loaded { filter: blur(0); transition: filter 0.4s ease; }
brightness()
Adjusts luminosity. 1 is the original; values below 1 darken; above 1 brighten. Accepts decimals or percentages:
/* 50% brightness — dimmed */
.dimmed { filter: brightness(0.5); }
/* 150% brightness — vivid */
.vivid { filter: brightness(1.5); }
/* Hover brighten effect */
.card img {
filter: brightness(0.9);
transition: filter 0.2s ease;
}
.card:hover img { filter: brightness(1.1); }
contrast()
Adjusts the contrast. 1 is normal; 0 is flat gray; values above 1 increase contrast:
/* High contrast black-and-white */
.print-preview {
filter: grayscale(1) contrast(1.5);
}
/* Low contrast — faded look */
.faded { filter: contrast(0.6); }
grayscale()
Converts the element to grayscale. 0 is full color; 1 (or 100%) is fully grayscale:
/* Grayscale images — re-color on hover */
.team-photo {
filter: grayscale(1);
transition: filter 0.3s ease;
}
.team-photo:hover { filter: grayscale(0); }
/* Partial desaturation */
.muted { filter: grayscale(0.5); }
hue-rotate()
Rotates the hue of all colors by a given angle. This cycles through the color wheel (0–360°). Great for color theme animations:
/* Shift hues 90 degrees */
.alt-color { filter: hue-rotate(90deg); }
/* Animated color cycling */
@keyframes colorCycle {
from { filter: hue-rotate(0deg); }
to { filter: hue-rotate(360deg); }
}
.rainbow { animation: colorCycle 4s linear infinite; }
invert()
Inverts the colors of the element. 1 is fully inverted; 0 is original. Perfect for dark mode image handling:
/* Invert dark logos for dark mode */
@media (prefers-color-scheme: dark) {
.logo { filter: invert(1); }
}
/* Partial inversion */
.tinted { filter: invert(0.3); }
opacity()
Applies opacity via the filter pipeline. Similar to the opacity property, but composited differently — the filter version doesn't create a new stacking context in the same way. The opacity property is generally preferred:
/* Mostly equivalent to opacity property */
.ghost { filter: opacity(0.5); }
saturate()
Controls color saturation. 1 is normal; 0 is grayscale; values above 1 are super-saturated:
/* Boost saturation for vivid product photos */
.product-img { filter: saturate(1.4); }
/* Desaturate hover-out state */
.portfolio-item {
filter: saturate(1);
transition: filter 0.3s;
}
.portfolio-item:not(:hover) { filter: saturate(0.4); }
sepia()
Applies a sepia (warm brown/amber) tone. 0 is original; 1 is full sepia, giving a vintage photograph look:
/* Vintage photo effect */
.vintage { filter: sepia(0.8) contrast(1.1) brightness(0.9); }
/* Animated sepia on hover */
.photo {
filter: sepia(0);
transition: filter 0.4s ease;
}
.photo:hover { filter: sepia(1); }
drop-shadow()
Unlike box-shadow, drop-shadow() follows the actual alpha channel of the element — perfect for transparent PNGs and complex shapes. The syntax mirrors box-shadow but without the spread parameter and no inset keyword:
/* drop-shadow(offset-x offset-y blur-radius color) */
.logo-png {
filter: drop-shadow(2px 4px 6px rgba(0,0,0,0.3));
}
/* The difference: box-shadow clips to bounding box */
.icon-transparent {
/* box-shadow would be a rectangle — wrong */
/* drop-shadow follows the icon shape — correct */
filter: drop-shadow(0 4px 8px #3b82f6);
}
Chaining Multiple Filters
You can chain multiple filter functions in a single filter declaration — they are applied in order from left to right:
/* Vintage photo: grayscale + sepia + contrast */
.vintage {
filter: grayscale(0.3) sepia(0.5) contrast(1.2) brightness(0.95);
}
/* Neon glow effect */
.neon-text {
filter:
brightness(1.4)
drop-shadow(0 0 4px #a855f7)
drop-shadow(0 0 12px #a855f7)
drop-shadow(0 0 24px #7c3aed);
}
/* Blurred dark overlay on image hover */
.card img {
filter: brightness(1) blur(0);
transition: filter 0.4s ease;
}
.card:hover img {
filter: brightness(0.7) blur(3px);
}
Animating CSS Filters
Filters are animatable with transitions and @keyframes. Note: animating filters does trigger repaint (they are not compositor-only like transform), so use them judiciously in high-frequency animations:
/* Blur-in on page load */
@keyframes focusIn {
from { filter: blur(12px); opacity: 0; }
to { filter: blur(0); opacity: 1; }
}
.hero-image {
animation: focusIn 0.8s ease-out forwards;
}
/* Pulsing glow */
@keyframes glow {
0%, 100% { filter: brightness(1) drop-shadow(0 0 4px #f59e0b); }
50% { filter: brightness(1.3) drop-shadow(0 0 20px #f59e0b); }
}
.alert-icon {
animation: glow 1.5s ease-in-out infinite;
}
filter vs backdrop-filter
The filter property applies effects to the element itself and its contents. The backdrop-filter property (covered in our glassmorphism guide) applies effects to whatever is behind the element — used for frosted glass effects. They are often confused but serve very different purposes.
Performance Considerations
CSS filters trigger paint operations and can be expensive at large scales or with many elements. Tips for performance:
- Avoid filtering large, complex DOM subtrees — apply filters to leaf elements like images where possible
- Add
will-change: filterfor elements with animated filters to hint the browser blur()is particularly expensive — keep blur radius reasonable- Static filters (non-animated) are generally fine; the cost is paid once at paint time
Test all filter effects interactively in the Live HTML/CSS/JS Editor for immediate visual feedback.