Home / Blog / CSS Filter Effects: blur, brightness, contrast, grayscale & More
CSS Animations 2026-09-23 · 9 min read

CSS Filter Effects: blur, brightness, contrast, grayscale & More

Use CSS filter effects to apply visual transformations to elements and images. Learn blur, brightness, contrast, grayscale, and drop-shadow.

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: filter for 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.

Frequently Asked Questions

What is the difference between CSS filter drop-shadow and box-shadow?
box-shadow is applied to the element's bounding box (rectangle), ignoring transparency. filter: drop-shadow() follows the actual rendered pixels and alpha channel, so it perfectly hugs the shape of transparent PNGs, SVGs, and irregular elements. Use drop-shadow for transparent images and icons; use box-shadow for rectangular elements.
Can I apply CSS filters to text and other HTML elements, not just images?
Yes — the filter property applies to any HTML element, including divs, paragraphs, headings, buttons, and SVGs. You can blur, desaturate, or adjust the brightness of entire page sections, not just images.
Does animating CSS filters affect performance?
Animating filters triggers repaint on every frame, so it's more expensive than animating transform or opacity. For smooth animated filters, add will-change: filter to hint the browser, keep blur values moderate, and avoid applying animated filters to large or complex DOM trees.
How do I make a grayscale image return to color on hover?
Apply filter: grayscale(1) at rest and filter: grayscale(0) on hover, with a transition: filter 0.3s ease for a smooth effect. This pattern is common for portfolio grids and team photo sections.
What is the difference between filter: opacity() and the opacity property?
Both change transparency, but they differ in compositing behavior. The opacity property creates a new stacking context and is compositor-accelerated. filter: opacity() applies through the filter pipeline and is slightly less efficient. Prefer the opacity property for transparency animations.

Try Our Free HTML CSS Tools

Practice what you've learned with our free online tools — no installation required.

Live HTML/CSS Editor CSS Formatter Gradient Generator