Your backdrop-filter Is Working. Your Background Is Hiding It.
backdrop-filter looks like a one-line copy from a Codepen: blur(20px), ship the frosted navbar. Half the time it renders as a plain flat panel instead — because backdrop-filter blurs whatever is BEHIND an element, and that only shows up if the element itself is see-through. Here's the transparency rule, the stacking-context trap it quietly sets, and why it's one of the priciest effects you can scroll past.

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:
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.
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.
Runs right in your browser — poke at it and watch the concept react live.
The fix isn't more blur. It's less opacity:
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-filtertakes the same filter functions asfilter—blur(),brightness(),contrast(),saturate(),grayscale(), and you can stack several space-separated. The frosted-glass look usually isn't justblur()— pairing it withsaturate(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-filterfor 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, sobackdrop-filteris Baseline 2024 (newly available), not yet widely available. If your targets include iOS/Safari 17 or older, keep-webkit-backdrop-filternext to the unprefixed line (caniuse).
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.)
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."
Think it clicked? Take the 9-question quiz →
Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.
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?
- The CSS Property That Fixes Dark Mode's Ugly White Boxes
- One CSS Property Replaces Your Checkbox Hack
- You reach for Sass to mix colors.
color-mix()does it natively.
🚀 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:
- ⭐ GitHub — follow me and star the projects: github.com/parsajiravand
- 💬 Discord — join the frontend best-practices community: discord.gg/d9KRhuAwQ
- 📸 Instagram — frontend best practices, daily: @bestpractice___
Keep reading
One post a day, in your inbox
Each one with a runnable playground and a quiz. No pitch, no digest, unsubscribe in one click.
0 comments
Sign in to join the discussion, like comments, and save articles for later.