DEV Community

Cover image for Your `backdrop-filter` Is Working. Your Background Is Hiding It.
Parsa Jiravand
Parsa Jiravand

Posted on Originally published at bestpractic.org

Your `backdrop-filter` Is Working. Your Background Is Hiding It.

You find a frosted navbar on Dribbble, the kind that sits over a hero image with the content underneath swimming softly out of focus behind it — Apple's Control Center look, everywhere in 2026 UI. You copy the one line that's supposed to do it:

.navbar {
  background: white;
  backdrop-filter: blur(20px);
}
Enter fullscreen mode Exit fullscreen mode

Ship it. Refresh. The navbar is just... a white bar. No blur, no glass, nothing swimming behind it. You bump the blur to 40px, then 80px, convinced you fat-fingered a unit. Still nothing. You open dev tools, and the property is right there, applied, not crossed out, not overridden. It's just doing nothing you can see.

It isn't broken. It's doing exactly what you told it to do — you just told it to blur something you'd already hidden.

The wrong way, and why it feels right

background: white and backdrop-filter: blur(20px) on the same element feels like two independent style declarations: one sets a color, one adds an effect. That's how almost every other CSS property behaves — border-radius doesn't care what background says, box-shadow doesn't either. So it's reasonable to assume you can mix and match freely.

backdrop-filter isn't independent, because of what "backdrop" means. It doesn't touch the element or its content the way filter: blur(20px) would — filter blurs the element and everything inside it, into a mushy blob. backdrop-filter reaches behind the element, samples whatever's already rendered back there — the hero image, the page content scrolling underneath — blurs that, and paints it in as the element's background layer.

Which means: if the element's actual background is a fully opaque white, that opaque white paints on top of the blurred result, covering it completely. You're not seeing "no blur." You're seeing full-opacity white sitting on top of a blur that's happening perfectly correctly, one paint layer down, where you can never see it.

🎮 Try it yourself

▶️ Open the interactive playground →

Runs right in your browser — poke at it and watch the concept react live.

The one property that fixes it

The fix isn't more blur. It's less opacity:

.navbar {
  background: rgba(255, 255, 255, 0.55);
  backdrop-filter: blur(20px);
}
Enter fullscreen mode Exit fullscreen mode

That 0.55 alpha is the entire trick. Now there's a translucent white tint sitting on top of the blurred backdrop instead of a solid one hiding it — which is exactly the "frosted glass" look: not "blur," but "blur, plus a faint wash of color over it." Apple's classic blur materials work the same way: a blur, then a semi-transparent tint layered on. Drop the alpha closer to 0 and you get more of the scene showing through, softer glass; push it toward 1 and you're back to the flat panel you started with, just with a blur nobody can see.

Two API details worth knowing before you rely on this:

  • backdrop-filter takes the same filter functions as filter — blur(), brightness(), contrast(), saturate(), grayscale(), and you can stack several space-separated. The frosted-glass look usually isn't just blur() — pairing it with saturate(180%) is what gives it that slightly punchy, "more colorful than reality" look Control Center has, because a plain blur alone tends to look washed-out and grey.
  • It needs no vendor prefix on any current browser. Safari required -webkit-backdrop-filter for years and plenty of tutorials still paste both; today's Safari, Chrome, Firefox and Edge all ship the unprefixed property, so the prefix is legacy insurance, not a requirement — but "years" only ended with Safari 18 in September 2024, so backdrop-filter is Baseline 2024 (newly available), not yet widely available. If your targets include iOS/Safari 17 or older, keep -webkit-backdrop-filter next to the unprefixed line (caniuse).

The bug that shows up one sprint later: it makes its own stacking context

Say the frosted navbar works now — glass, tint, all correct. Then someone adds a dropdown menu inside it, positioned absolutely, z-index: 999 because nothing was ever going to out-rank it. And it renders underneath a modal that opens later in the page, a modal whose own z-index is a modest 10.

999 > 10. That should never lose. Except backdrop-filter — the moment its value isn't none — forces the element onto its own new stacking context, the same rule that opacity below 1, transform, and will-change already follow. A z-index only ever competes against siblings inside the stacking context it was declared in. Once the navbar becomes its own context, 999 stops meaning "above almost everything on the page" and starts meaning "above everything else inside this one blurred box" — a much smaller, much more local promise than it looks like.

This is exactly the same trap transform and opacity have quietly set for a decade; backdrop-filter just added itself to the guest list without most people noticing, because nobody reads the stacking-context spec before reaching for a glass effect. The fix is the same one you'd use for any stacking-context surprise: stop comparing z-index numbers across the boundary and instead move the dropdown out of the blurred container (render it in a portal, or as a sibling instead of a child), so it stacks against the page's actual top-level contexts instead of getting trapped inside the navbar's private one. It also becomes the containing block for position: fixed descendants, so a "full-screen" menu rendered inside the blurred navbar gets sized to the navbar, not the viewport — the same portal fix solves both. (If your navbar is already position: sticky or fixed, it was a stacking context before the blur — same trap, same fix.)

The cost nobody notices until the scroll feels wrong

Here's the part that doesn't show up in a screenshot: backdrop-filter is one of the most expensive things you can ask a browser to paint, and it pays that cost again on every frame where anything behind the blurred region changes — not once, at first paint. A blur isn't a filed-away static image; it's a real-time recomputation of "take everything currently behind this box and re-blur it," and if the page scrolls, or the content behind the glass panel is animating, that recomputation has to happen again on the next frame, and the one after that, for as long as the backdrop keeps changing.

A small blurred badge over a static hero image barely registers. A full-viewport blurred overlay sitting on top of a long, actively scrolling page is a different animal — the browser is re-blurring a screen-sized region on every scroll tick, on whatever GPU the visitor's phone happens to have, and low-to-mid-range mobile hardware is where that bill comes due first: scrolling that feels a half-step behind your finger instead of stuck to it. There's no universal number to quote here — it depends on the blurred area's size, the blur radius, and the device — which is exactly why you test it on the actual hardware your users carry, not just the laptop it was built on.

The practical rule: keep the blurred surface small and mostly static — a nav bar, a fixed card, a modal backdrop that isn't itself scrolling — and be skeptical of any design that blurs a large area the user is actively scrolling behind. If a design calls for that anyway, that's the moment to profile it on a mid-range Android before you ship, not after support tickets say "the new nav feels laggy."

🧠 Test yourself

Think it clicked? Take the 9-question quiz →

Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.

The takeaway

backdrop-filter isn't a broken filter — it's a different property with a different subject. filter blurs the element; backdrop-filter blurs whatever's behind it, which only becomes visible once the element itself stops hiding that blur behind an opaque background. Get the transparency right and you've got the effect. Forget that it also opens a new stacking context, and a z-index you were sure would win quietly loses to a scope you didn't know existed. And treat the blur radius as a performance budget, not a free design knob — the frame it costs shows up on someone's phone, not in your editor.

Have you shipped a frosted panel that turned out to be a flat one for a sprint before anyone noticed? What gave it away — the missing blur, or the z-index fight?

📚 Read next


🚀 Want more like this? Every guide, playground, and quiz lives on bestpractic.org — open it and sign up free so the next one finds you.

Thanks for reading! Let's stay connected:

Top comments (0)