A little help goes a long way. Find a tool/

Design FIELD NOTES · 4 MIN READ

CSS Filters Explained: The Post-Processing Pipeline

How filter works: nine stacking functions, their neutral values, why order matters, and when drop-shadow beats box-shadow.

The CSS filter property is a small image-editor living in a stylesheet: blur, recolor, brighten, shadow — applied to any element after it renders. Each effect is a function, and a single declaration can stack all of them into a pipeline. The catch is that the pipeline has real semantics: order matters, every function has a neutral value, and the whole thing is rasterized, which has performance consequences.

A pipeline, not a list

filter: blur(4px) saturate(150%) means "blur first, then boost saturation on the blurred result". Swap them and the saturation boost applies to the sharp image before blurring smears it — subtly different picture. Each function consumes the output of the previous one, which is why the same functions in a different order produce a different image. Ten functions are available: blur, brightness, contrast, grayscale, hue-rotate, invert, opacity, saturate, sepia, and drop-shadow.

Neutral values: the shortest correct CSS

Every filter function has a value at which it does nothing: blur(0), brightness(100%), contrast(100%), grayscale(0%), hue-rotate(0deg), invert(0%), opacity(100%), saturate(100%), sepia(0%). Writing them is valid but pointless — the browser runs a pipeline that changes nothing. Well-built generators omit neutral functions, so filter: blur(4px) is all you get when blur is the only dial you moved. This is worth internalizing when writing filter UIs of your own: diff against neutral, emit only what changed.

The ranges differ per function in a meaningful way. Grayscale, sepia and invert interpolate toward a fixed look, so they max out at 100% — there is nothing beyond "fully gray". Brightness, contrast and saturate are multipliers with no natural ceiling: brightness(180%) over-exposes, contrast(200%) deepens both blacks and whites, and extreme values are legitimate stylistic choices.

The color functions and how they differ

Four functions recolor, and they are easy to confuse. grayscale(100%) discards saturation entirely — every color collapses to its luminance on the gray axis. sepia(100%) pushes everything onto a warm brown axis, the vintage-photo look. saturate(0%) is mathematically equivalent to grayscale(100%), but saturate(200%) amplifies existing color instead of removing it. hue-rotate(45deg) spins every hue around the color wheel by 45 degrees — reds drift toward orange, greens toward teal — while leaving saturation and lightness alone. And brightness scales the light output while contrast stretches the distance between dark and light values, which is why a washed-out screenshot is usually fixed with contrast, not brightness.

drop-shadow versus box-shadow

Both cast shadows, but they trace different outlines. box-shadow draws a rectangle around the border box — it cannot know what the element looks like. drop-shadow shadows the actual rendered alpha shape: a transparent PNG of a leaf gets a leaf-shaped shadow; an SVG icon gets an icon-shaped one. For anything that is not a rectangle, drop-shadow is the honest tool. It is also the filter family's answer to "blur the background but keep the subject crisp". The trade: drop-shadow has no spread parameter and no inset mode, so rectangles with tight contact shadows remain box-shadow territory.

filter: drop-shadow(0 8px 12px rgba(0, 0, 0, 0.35));  /* follows the shape */
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.35);           /* follows the box  */

Performance and the edge-fade trap

Filters are raster effects: the browser renders the element to a texture and post-processes it. Small elements pay almost nothing; a full-viewport blurred hero can cost real paint time, and animating filter values frame-by-frame multiplies that cost. Two practical rules: prefer transitioning transform and opacity for motion, and keep static filters on elements that do not repaint constantly. Also note that blur() fades the element's own edges, because it samples pixels beyond the box — if you want a soft shadow with a crisp subject, that is drop-shadow's job. And backdrop-filter is the sibling that applies these functions to whatever is behind the element, the frosted-glass effect.

Try it yourself

The CSS Filter Generator gives you every function as a dial, omits the neutral ones automatically, previews the stack live and emits the shortest correct declaration. The CSS Box Shadow Generator covers the rectangle-shadow side of the comparison, and the CSS Transition Generator shows how a filter stack behaves when it changes state instead of staying static.

Frequently asked questions

Why does the order of filter functions matter?
filter is a pipeline: each function processes the output of the previous one. blur(4px) saturate(150%) blurs first then recolors, while saturate(150%) blur(4px) recolors first — the results differ wherever color and blur interact, so sequence is part of the look.
What is a neutral filter value?
The value at which a function does nothing: blur(0), brightness(100%), contrast(100%), grayscale(0%), sepia(0%), invert(0%), hue-rotate(0deg), opacity(100%), saturate(100%). Writing neutral functions is valid CSS but pure overhead, so generators omit them.
When is drop-shadow better than box-shadow?
When the element is not a rectangle. box-shadow traces the border box; drop-shadow traces the rendered alpha shape, so transparent PNG cut-outs and SVG icons get a shaped shadow. drop-shadow has no spread and no inset mode, so rectangles are still box-shadow territory.
Do CSS filters hurt performance?
They can. Filters rasterize the element and post-process the bitmap, so large areas and animated values cost paint time. Static filters on small elements are cheap; for continuous animation, transition transform and opacity instead, which the compositor handles.
Why does blur() fade my element's own edges?
blur() samples pixels beyond the element's box and averages them, so the element itself softens at its border, not just its surroundings. Use drop-shadow for soft shadows that keep a crisp subject, and remember backdrop-filter blurs what is behind the element instead.