Animated blur overlay

September 7, 2023
Overlay with backdrop blur and icons on top

Chrome browser is usually better at rendering CSS backdrop filters than any other browser. That's not the case with one of the filters - blur().

Chrome fails under multiple scenarios:

  • unpleasant flickering, when content under the backdrop is moving, or when the backdrop moves beyond the viewport,
  • the backdrop-filter: blur() completely breaks when you try to animate it with transition or animation.

I've found a remedy for the second scenario. And the use case for transitioning the backdrop-filter: blur() is quite common - the blur overlay is often displayed behind a modal window and now we'll be able to make a smooth transition between the two states.

To see this effect in action, hover or tap the box below.

The solution

I've discovered that while transitioning the backdrop-filter: blur() property breaks the transition in Chrome, we can transition the blur in a different way. Here's where @property comes to rescue - we can use it to define a custom CSS property and transition it instead.

Now the problem is Firefox does NOT support @property, but we'll make a fallback for it!

.blur-overlay { --end-blur: 20px; --duration: 0.3s; --easing: ease-in-out; position: fixed; inset: 0; } @supports (-moz-appearance: none) { .blur-overlay { transition: var(--duration) backdrop-filter var(--easing); } .blur-overlay[data-state="open"] { backdrop-filter: blur(var(--end-blur)); } .blur-overlay[data-state="closed"] { backdrop-filter: blur(0px); } } @property --overlay-blur { syntax: "<length>"; inherits: false; initial-value: 0px; } @supports not (-moz-appearance: none) { .blur-overlay { transition: var(--duration) --overlay-blur var(--easing); -webkit-backdrop-filter: blur(var(--overlay-blur)); backdrop-filter: blur(var(--overlay-blur)); } .blur-overlay[data-state="open"] { --overlay-blur: var(--end-blur); } .blur-overlay[data-state="closed"] { --overlay-blur: 0px; } }