Which CSS property creates which image effect?
Filter handles color and blur, plus shadows when you need them. Blend modes tint and layer, clip-path and mask-image reshape, and object-fit frames the picture inside its box. Motion comes from transition with transform, and backdrop-filter frosts whatever sits behind an element.
| Goal | Property | Example value | Baseline status |
|---|
| Recolor, blur, add a shadow | filter | grayscale(100%) | Widely available |
| Tint or layer | mix-blend-mode, background-blend-mode | multiply | Widely available |
| Crop or reshape | clip-path, object-fit | circle(40%), cover | Widely available |
| Fade edges, cut out shapes | mask-image | linear-gradient(black, transparent) | Widely available (since June 2026) |
| Frost what sits behind | backdrop-filter | blur(8px) | Newly available in 2024 |
Status labels come from the web-features dataset. The newest row, backdrop-filter, deserves a fresh look at its Baseline label before it goes on a production page.
Motion sits on top of every row. The transition property has worked across browsers since September 2015 (MDN Web Docs), and it animates any property with an interpolable value, filter and transform included.
Reach for filter when the pixels themselves need a new color or sharpness. A blend mode fits when the image has to relate to another layer underneath it. If the real question is which part of the image shows, you’re looking at clip-path, mask-image or object-fit, and anything that moves belongs to transition and transform.
None of these touches the file on disk. The browser applies the effect at render time, so deleting the rule brings the original back.
One opinion, though. If an effect is permanent and the image set is fixed (a grayscale client-logo strip, say), bake it into the file, since a pre-edited image adds no render-time work.
How do CSS filter functions change an image?
The filter property takes a list of functions and applies them to an element. The available ones are blur(), brightness(), contrast(), drop-shadow(), grayscale(), hue-rotate(), invert(), opacity(), saturate() and sepia().
The property has held Baseline Widely Available status since March 2019 (web-features dataset, Web Platform DX), so all 10 functions run across the core browsers.
Most take a single value. blur() wants a length (blur(5px)), hue-rotate() wants an angle (hue-rotate(90deg)), and the tone functions accept percentages or plain numbers (contrast(200%), brightness(0.4)), per MDN Web Docs. drop-shadow() is the odd one out, with offsets, a blur radius and a color.
Color and tone functions
grayscale() runs from 0%, which leaves color alone, to 100%, which removes all of it. sepia() adds a warm brown cast (MDN’s own example uses sepia(60%)), and hue-rotate() shifts every hue around the color wheel by whatever angle you give it.
For brightness(), contrast() and saturate(), 100% is neutral, so values above or below push the picture in either direction. invert() flips every channel, so invert(100%) gives a negative.
Bennett Feely’s image effects library leans on grayscale(), brightness() and contrast() to refine its blend-mode effects, and its notes say the values need tweaking per photo. That matches how this works in practice. A setting that flatters a landscape can crush a portrait.
Blur and soft focus
blur() takes a length, never a percentage.
Low values soften an image, and high values turn it into a color wash behind text. A fuller walkthrough of blur-on-hover and blurred backgrounds lives in this guide to CSS blur effects.
One catch. filter applies to the whole element, border included. MDN’s own example greys and blurs the border along with the image, so put borders on a wrapper if they should stay sharp.
Drop-shadow versus box-shadow on images
box-shadow traces the rectangular box and accepts spread and inset. drop-shadow() follows the alpha channel of the image itself, and it rejects inset and spread (MDN Web Docs).
A transparent PNG or SVG cutout gets a shadow in its real silhouette from drop-shadow(). A plain rectangular photo gets a rectangle from either one, so box-shadow is the simpler pick there, and a box-shadow generator speeds up tuning the offsets.
Chaining several functions
Functions run in the order written, and each one receives the output of the one before it, according to MDN.
That makes contrast(175%) brightness(103%) a different recipe from brightness(103%) contrast(175%). When a chain needs tuning by eye, a point-and-click filter generator shows the result as the sliders move.
How to build image hover effects with transition and transform
Start with a container that clips overflow. The transition goes on the image’s normal state, and the changed values go under :hover. Declaring the transition on the normal state is what makes the effect animate back out when the pointer leaves.
- Wrap the img in a container and set overflow: hidden, so a zoomed image stays inside its frame.
- Add transition: transform 0.4s ease to the img itself, not to the :hover rule.
- Under the container’s :hover state, set the img to transform: scale(1.1).
- For a color change, list filter in the same transition and give the two states matching values, such as grayscale(100%) at rest and grayscale(0%) on hover.
- Check the resting state on a touch screen, where hover never fires, and make sure the image still reads well without it.
A few details from MDN trip people up. In the transition shorthand, the first time value is the duration and the second is the delay. And transition-property defaults to all, so a bare duration animates everything animatable. Name transform and filter instead.
Zoom and rotate on hover
transform changes how the image is painted, not where it sits in the layout, so a scaled image never pushes its neighbors around.
scale(1.1) is a safe zoom. Past 1.2 the crop starts hiding the subject. Add rotate(2deg) for a tilt, and keep the duration between 0.3s and 0.6s so the motion reads as deliberate. I’d skip the zoom entirely on any image with text baked into it, since the edges get cut off fast.
Beaver Builder’s documentation has offered ready-made zoom, zoom-and-rotate and unblur hover classes for its Photo module, with transitions set to 0.3s.
Cards, buttons and links follow the same pattern, and this roundup of CSS hover effects collects variations beyond images.
Color change on hover
MDN Web Docs lists the animation type of filter as a filter function list, so each function interpolates on its own value.
Grayscale-to-color is grayscale(100%) at rest and grayscale(0%) on hover. Blur-to-sharp works the same way, from blur(4px) to blur(0).
Keep the same functions in the same order in both states and change only the values. Every pair then has a partner to animate toward.
Ken Burns effect with keyframes
The Ken Burns effect is a slow zoom and drift that runs without any pointer input.
It needs @keyframes rather than a transition, because the motion starts on load and loops. If the syntax is new, this primer on CSS keyframes covers it.
The effect itself is one rule, animation: drift 20s ease-in-out infinite alternate, paired with @keyframes drift that moves transform from scale(1) to scale(1.15) and a small translate. Put the image inside an overflow: hidden frame so the zoom crops instead of spilling.
Slow motion is still motion for visitors who set prefers-reduced-motion, so the animation needs an off switch.
How do overlays, blend modes and duotone effects work?
Stack a colored or gradient layer above an image and you have an overlay. A blend mode then decides how that layer’s pixels combine with the image underneath.
Duotone is the best-known result, built by graying the image and blending two color layers over it.
Color and gradient overlays
Build the overlay on a wrapper’s ::after pseudo-element, so the markup stays a single img inside a container. Give it position: absolute with inset: 0 so the layer matches the image box, and add pointer-events: none so clicks pass through to any link beneath. Keep its z-index below the caption text.
A bottom-weighted gradient, transparent at the top and dark at the bottom, keeps caption text readable without dimming the whole photo. A visual gradient generator speeds up tuning the stops.
For a caption reveal, start the caption at opacity: 0 and raise it to 1 on :hover and :focus-within. The :focus-within half gives keyboard users the same reveal.
mix-blend-mode versus background-blend-mode
mix-blend-mode blends an element with the content rendered behind it in the same stacking context (MDN Web Docs), which makes it the one to use on an img placed over a colored container.
background-blend-mode is narrower. It mixes an element’s own background layers with each other and with its background-color, so it can tint a CSS background image with no extra markup.
The values for background-blend-mode follow the order of background-image, and a shorter list repeats or truncates until the lengths match, per MDN. The keywords people reach for are multiply (darkens), screen (lightens), overlay (boosts contrast) and difference (inverts where layers disagree).
Blend mode changes are not interpolated (MDN), so a hover that swaps multiply for screen snaps instead of fading. Fade the overlay’s opacity and keep the blend mode fixed.
You’ll see mix-blend-mode quoted with two different support dates. MDN says it has worked across browsers since January 2020, while web-features marks it Baseline Widely Available since 2022-07-15. Both are right: Edge 79, released 2020-01-15, was the last engine to ship it, and the later date falls exactly 30 months afterward.
Duotone with blend modes
Duotone maps a photo’s shadows to one color and its highlights to another.
The CSS version grays the image, then layers two solid colors over it with the multiply and lighten blend modes (darken can replace multiply when the result runs too dark), in pure CSS with no script. Bright colors on images with large areas of white or black give the best results, according to Web Designer Wall.
Spotify built its brand identity around duotone photography, and its Colorizer tool kept thousands of uploaded images consistent with the brand (Web Designer Wall, 2017). Text laid over a duotone image needs its own contrast check, because the effect shifts brightness relationships across the whole photo.
How to crop and shape images with clip-path, mask-image and object-fit
object-fit controls how an image fills its box. clip-path cuts hard-edged shapes, and mask-image handles soft or gradient-based edges.
All three change which part of the picture shows without touching the file, and they combine freely. An object-fit: cover image inside a clip-path circle is the standard avatar.
Fitting images to a box with object-fit
object-fit takes a single keyword: fill, contain, cover, none or scale-down (MDN Web Docs). fill is the initial value.
fill stretches the image to the box, which distorts it when the ratios differ. contain scales the whole image to fit inside and leaves empty space on one axis. cover fills the box and clips whatever overflows, and it’s the usual pick for thumbnails and cards.
none keeps the natural size, and scale-down picks whichever of none or contain is smaller. The property targets replaced elements such as img and video, and MDN notes it does nothing on iframe, embed or fencedframe.
With cover, object-position decides which part of the image survives the crop. Setting object-position: top, or a percentage pair, keeps a face or a product in frame when the default center crop would cut it off. Unlike CSS cropping, which only changes how an image appears in the browser, a cropping pictures app lets you permanently adjust the image’s composition before uploading it to your website.
Clip-path shapes
clip-path hides everything outside the shape you give it.
The shape functions are circle(), ellipse(), inset() and polygon(), plus path(), rect(), xywh() and shape() (MDN Web Docs). circle(40%) gives a round crop, and polygon(50% 0, 100% 100%, 0 100%) gives a triangle.
Placing polygon points by hand is slow, so a visual clip-path generator pays off for anything beyond a circle.
clip-path animates with transitions and animations, which makes reveals cheap to build. Start at inset(0 100% 0 0), end at inset(0), and keep the same shape function (and the same number of polygon points) in both states.
Watch out for the shadow, though. clip-path also clips the element’s own shadow, so put box-shadow or drop-shadow() on a wrapper if the cropped shape needs one.
Gradient masks
mask-image hides parts of an element based on the alpha channel of the mask image and, depending on mask-mode, its luminance (MDN Web Docs). Opaque areas of the mask show the image, and transparent areas hide it.
A gradient is the simplest mask. mask-image: linear-gradient(black, transparent) fades an image out toward the bottom, and radial-gradient(black 50%, transparent 60%) gives a round image with a soft edge, both patterns taken from MDN’s examples.
mask-image works like background-image, so it accepts a gradient, an image file or an SVG, and a file-based mask needs a transparent or semi-transparent area to do anything (web.dev). An SVG file or an inline SVG data URI helps when the mask is an irregular shape. File-based masks only load over HTTP or HTTPS, so a mask tested from a file:// URL renders as transparent black (MDN Web Docs).
MDN lists the animation type of mask-image as discrete, so a transition between two masks snaps instead of fading. Fade the wrapper’s opacity on hover and leave the mask static.
When do backdrop-filter and SVG filters beat the filter property?
Reach for backdrop-filter when the effect belongs to what sits behind an element, and for an SVG filter when no CSS function can produce the result, such as an exact two-color duotone or warped pixels.
| Need | Tool | Why it fits |
|---|
| Frosted caption bar over a photo | backdrop-filter | Affects the area behind the element |
| Exact two-color duotone | feColorMatrix | Maps colors with matrix values |
| Warped or glitched pixels | feDisplacementMap | Shifts pixels using a second input |
| Plain blur, grayscale, shadow | filter | One declaration, no extra file |
The last row is the honest default. Most pages never need the other three.
backdrop-filter for frosted glass
The web-features dataset defines backdrop-filter as applying blur or color shifts to the area behind an element, where filter changes the element itself.
It takes the same function list as filter, so backdrop-filter: blur(8px) on a semi-transparent panel produces the frosted bar seen on image cards. The panel needs a translucent background, or the blur has nothing to show through.
Frosted panels are the core of glassmorphism, and this walkthrough of CSS glassmorphism builds full card layouts on the same property.
SVG filter primitives
An SVG filter is a chain of primitives (elements prefixed with fe) defined once and applied with filter: url(file.svg#id). The primitives hand results to each other through result and in attributes, as MDN’s feBlend example does with feFlood.
feColorMatrix remaps color channels, which is how an exact two-color duotone gets built. feDisplacementMap moves pixels in one input by the values of a second input, and that’s the basis of distortion and glitch looks. feTurbulence generates noise, usually to feed the displacement map (MDN’s own example pairs the two), and feBlend applies blend modes inside the filter.
José M. Pérez’s 2016 write-up applies a duotone to an image with feColorMatrix, computing the matrix in JavaScript from the target colors, and points to CSS blend modes as the alternative that needs no JavaScript.
The price of an SVG filter is upkeep, since it lives in markup that few teams read fluently. Defining it in an inline block, as this guide to SVG in HTML describes, keeps it next to the image that uses it.
Which browsers support CSS image effects, and what fallbacks work?
Every property covered here runs in current Chrome, Edge, Firefox and Safari. Fallbacks matter for older engines and for the newest entry, backdrop-filter.
Current support figures
As of October 2026, these are the figures worth knowing:
- clip-path has been Baseline Widely Available since 2023-07-21 (web-features).
- mask has been Baseline Widely Available since 2026-06-07 (web-features), and it appears among features that reached Widely Available in June 2026 (web.dev Baseline digest).
- Safari 18 completed the set of engines shipping backdrop-filter, with Widely Available projected for 2027-03-16 (web-features).
- backdrop-filter appears in about 37% of page loads (Chrome Platform Status, October 2026).
The basic shapes behind most image crops (circle(), ellipse(), inset(), polygon()) cleared the Widely Available bar earlier than the full clip-path property. Cropping with them sits at the safe end.
Sources now agree on mask. The web-features explorer page carried the Newly Available label from 2023 with a projected Widely Available date of 2026-06-07, and web.dev’s June 2026 digest, published on July 17, 2026, lists the mask property among features that reached Widely Available. The comparison table at the top of this article uses the updated label.
The projected date has passed, so older guides that still call mask newly available are out of date. MDN’s wording for the Newly Available label is that a feature works in the latest browser versions and can fail in older ones, and that is the label backdrop-filter still carries.
Fallback patterns
Write the unaffected image as the base style and put each effect inside @supports. Bennett Feely’s image effects library works this way, showing the unedited source image by default and turning an effect on only when the browser passes the check.
Masks need a second declaration. web.dev notes that most mask-image features reach older browsers through -webkit-mask-image, so write both properties. For frosted panels, use @supports not (backdrop-filter: blur(1px)) to swap in a more opaque background, so text stays readable where the blur is missing.
Tables only go so far, and cross-browser compatibility checks in real engines catch what they miss. Start with Safari, the last engine to ship backdrop-filter.
When do image effects hurt performance or accessibility?
Effects get expensive when they repaint large areas or run on many elements at once. They turn into an accessibility problem when they move and can’t be stopped.
Rendering cost
web.dev advises restricting animation to opacity and transform wherever possible, because those properties stay on the compositing stage of the rendering path.
For any other property, check which stage it hits before shipping. Chrome’s Lighthouse flags non-composited animations, meaning any animation that triggers Style, Layout or Paint, and notes that they can also raise Cumulative Layout Shift.
F22 Labs’ case study of a blur that slowed a video interface found the cost followed how much area the blur covered and how large the radius was. SitePoint warns that backdrop-filter on many elements, or across a large area, drags performance down. And MDN calls will-change a last resort for existing performance problems, since heavy use costs memory and forces more complex rendering.
Chrome answers certain will-change values by creating a compositing layer or a stacking context. MDN suggests setting the property from script just before a change instead of leaving it in the stylesheet.
How to find the expensive effect
- Open the Rendering panel in Chrome DevTools (More tools, then Rendering).
- Switch on Paint flashing, which flashes repainted regions in green, and trigger the effect.
- Turn on Layer borders to see compositor layers outlined in orange and olive, with tiles in cyan, and look for layers nobody asked for.
- Record in the Performance panel and open the paint profiler if a frame runs long.
- Run Lighthouse and read its list of non-composited animations.
Chrome’s DevTools documentation describes the green paint-flashing overlay and the layer-border colors, and web.dev points to the paint profiler for spotting costly paint work. A layer that flashes green with no user input is repainting for nothing.
Contrast and motion
WCAG 1.4.3 sets a minimum contrast ratio of 4.5:1 for text, or 3:1 for large text (W3C). The criterion covers text revealed on hover or keyboard focus, so hover captions count.
Overlays and blend modes change the backdrop under that text, so measure color contrast at the lightest patch behind the words, not at the average of the image.
On reduced motion, MDN describes the prefers-reduced-motion media feature as detecting a device setting that minimizes non-essential motion. Its example uses @media (prefers-reduced-motion: reduce) to swap a scaling animation for a gentler dissolve.
MDN also describes the setting as a request to remove, reduce or replace motion, so reduce does not mean stripping every animation. WCAG 2.3.3 (Level AAA) asks that motion triggered by interaction can be disabled unless it is essential to the function or the information conveyed. For image effects, scale, rotate and drift are the motion to remove, while a color fade can stay.
Auto-playing loops run into WCAG 2.2.2, which requires a way to pause, stop or hide moving content that starts automatically, lasts longer than five seconds and sits beside other content. A looping Ken Burns drift qualifies, and W3C adds that hover- or focus-triggered motion with no way to stop it can fail 2.2.2 and possibly 2.3.3.
Run the finished page through a web accessibility checklist before release, since contrast and motion are only two of its lines.
When CSS is the wrong tool
Skip CSS for a full-width, always-on blur and export a pre-blurred copy at build time instead. Charts, maps and screenshots where color carries meaning should stay unfiltered. In a long gallery, run effects on hover or focus only, never as always-on filters.
If an effect needs will-change to feel smooth, cut the effect first.
CSS Image Effects FAQ
Does a CSS filter reduce image file size?
No. The browser downloads the full original file and applies the effect afterward, so a blurred or grayscale image costs the same bytes as the untouched one. To shrink the download, resize or compress the file itself.
What is the difference between filter: opacity() and the opacity property?
Both control transparency. filter: opacity() sits inside a filter chain, so its position among other functions changes the result, while the opacity property applies to the element on its own.
For a plain fade, the property is the simpler choice.
How do I apply a filter to a background image only?
Move the background onto a pseudo-element such as ::before, then put the filter on that pseudo-element. A filter on the parent would also blur or recolor the text and child elements sitting on top.
Position the pseudo-element behind the content with a lower z-index.
How do I make a circular image or round the corners?
Set border-radius on the img. A value of 50% turns a square image into a circle, and a fixed value such as 12px rounds the corners.
Add object-fit: cover so a non-square photo fills the shape without stretching. A border radius generator previews the values live.
How do I use filters in Tailwind CSS?
Tailwind ships a utility class per function: blur, brightness, contrast, drop-shadow, grayscale, hue-rotate, invert, saturate and sepia (Tailwind CSS documentation). Combine them on one element, add variants such as hover:, and use filter-none to reset.
Version 4 renamed blur to blur-sm and blur-sm to blur-xs.
Shipping CSS Image Effects in the Right Order
Release effects in order of support risk and rendering cost. That puts Widely Available properties ahead of newer ones and keeps expensive repaints out of the first release.
- object-fit, clip-path and static filters
- transform and opacity for hover motion
- backdrop-filter and SVG filters behind @supports
The trade-off is that the showiest effects arrive last, in exchange for a page that falls back to the plain image in any engine.
The advice changes once backdrop-filter reaches Widely Available status, which moves it up the list (position checked October 2026).
Image-heavy layouts are the natural test bed, and building a CSS gallery gives each step in the sequence a real page to run on.