One of my all-time favourite CSS tricks is using backdrop-filter: blur() to create a frosted glass effect. I use it in just about every project I work on, including this blog!
Here’s a quick demo, to show what I’m talking about:
Some Example Website
https://www.joshwcomeau.com/example-website
Some Website
This is an example website showing how I typically use backdrop-filter to create glassy headers.
Notice that as the cupcake moves behind the header, it appears blurry, as it would if it was passing behind frosted glass.
This effect helps us add depth and realism to our projects. It’s lovely.
But when I see this effect in the wild, it’s almost always missing some crucial optimizations. A couple of small changes can make our frosted glass so much more lush and realistic!
In this post, you’ll learn how to make the slickest frosted glass ever ✨. We’ll also learn quite a bit about CSS filters along the way!
To briefly explain the underlying concept: CSS gives us quick and easy access to SVG filters via the filter property.
For example, we can give elements a Gaussian blur with filter: blur():
16px
There are lots of fun filter options, the sorts of things you’d find in image-editing software. Like, rotating the hue of all the colors:
0deg
In these examples, I’m applying the filters to an <img> tag, but we can apply them to standard DOM nodes as well:
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since 1966, when designers at Letraset and James Mosley, the librarian at St Bride Printing Library in London, took a 1914 Cicero translation and scrambled it to make dummy text for Letraset's Body Type sheets.
0px
0%
Pretty neat, right?
Things get even cooler with backdrop-filter. This property lets us apply these same filters to the stuff behind a given element.
For example:
50%
130%
In this demo, the .magic-ring element sits in front of a photo (source(opens in new tab)). It uses the backdrop-filter property to apply some filtering to everything behind it, which can be used for some pretty artistic effects.
In practice, I pretty much only use backdrop-filter for one use case: blurring everything behind an element, usually a header, to create the “frosted glass” effect I mentioned earlier:
Some Example Website
https://www.joshwcomeau.com/example-website
Some Website
This is an example website showing how I typically use backdrop-filter to create glassy headers.
Notice that as the cupcake moves behind the header, it appears blurry, as it would if it was passing behind frosted glass.
Alright. Let’s talk about the thing most developers miss.
Here’s the problem: The backdrop-filter algorithm only considers the pixels that are directly behind the element.
For filters like brightness or hue-rotate, that makes perfect sense. With blur, though, we actually want to consider pixels that are near the element too.
This is one of those things where a demo is worth a thousand words. Check out the difference:
By default, the gaussian blur algorithm is applied to all of the pixels behind the element. This means that if a big colorful element is near the element, it won’t have any effect.
That’s not really how frosted glass works in real life though. Light bounces off of objects and then goes through the glass. It looks so much better when the blurring algorithm includes nearby content.
Unfortunately, this isn’t something we can configure directly. Instead, we need to be a bit crafty.
Here’s the code:
Code Playground
Result
Let’s walk through it step by step. First, we have a header with a backdrop blur:
Code Playground
Result
Because the red ball isn’t behind the header at all, it isn’t being considered by the blurring algorithm, and so we don’t get that soft red glow. We need to extend the header so that it covers at least some of the ball.
Rather than give the <header> an explicit height (which would lock us into a specific size, rather than a dynamically-calculated one), let’s move the backdrop-filter to a child element, and set that child element to be twice as large as its parent:
Code Playground
Result
Alright, now we’re getting somewhere! The .backdrop child grows to cover most of the red ball, blurring it correctly.
Now, we don’t actually want to see all of this excess backdrop. We need to trim it back to the size of the <header> parent element.
We can do that with overflow: hidden:
Code Playground
Result
When I originally published this blog post in late 2024, this approach worked in Firefox and Safari but didn’t work in Chromium browsers. I believe this was an order-of-operations issue; the overflow trimming occurred before the filters were applied, so when the blurring algorithm is executed, the content has already been hidden.
Fortunately, this behaviour was fixed in Chrome 142, released in October 2025. This means that overflow: hiddenis now a viable solution to this problem. 😄
Alternatively, if you need to support older versions of Chrome, you can also use the mask-image property on the child, which effectively does the same thing by only painting the top half of the nested .backdrop element:
Code Playground
Result
This is the basic idea behind this solution, but there’s a couple more optimizations we should consider.
By extending the glassy backdrop below the header, we can ensure that the blurring algorithm takes it into consideration even before that element reaches the header.
But what about when things leave the top of the viewport?
Things aren’t quite so nice. Scroll down slowly in this demo:
Some Example Website
https://www.joshwcomeau.com/example-website
Notice that weird goopy flickering, at the very top of the viewport?
It’s the same issue as before. The gaussian blur algorithm is only considering the pixels directly underneath it. When a yellow longboard is scrolled out of view, for example, that data is no longer factoring into the blur algorithm, causing those unnatural color flickers.
Unfortunately, we can’t re-use our solution here. As far as I can tell, elements outside the viewport are never considered by backdrop-filter(), even if the elements extend past the top.
The best solution I’ve found for this solution is to add a gradient that covers the flickering:
Some Example Website
https://www.joshwcomeau.com/example-website
Here’s the code:
.backdrop { position: absolute; inset: 0; height: 200%; background: linear-gradient( to bottom, /* Replace this with your site’s actual background color: */ hsl(0deg 0% 0%) 0%, transparent 50% ); backdrop-filter: blur(16px);}
Until now, the .backdrop element has been fully transparent; we haven’t applied a background at all. This gradient makes it opaque at the very top, blocking the flickering colors from view, but fading to transparent, to show the frosted glass effect.
In some circumstances, the frosted glass effect can be a bit distracting:
Some Example Website
https://www.joshwcomeau.com/example-website
Some Website
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since 1966, when designers at Letraset and James Mosley, the librarian at St Bride Printing Library in London, took a 1914 Cicero translation and scrambled it to make dummy text for Letraset's Body Type sheets.
It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.
This feels too “busy” to me; the blurry text sitting behind the header makes the site name and navigation too hard to read. It all feels a bit messy, and not as subtle as I want.
There are two main ways to fix this. We could increase the blur radius:
Some Example Website
https://www.joshwcomeau.com/example-website
Some Website
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since 1966, when designers at Letraset and James Mosley, the librarian at St Bride Printing Library in London, took a 1914 Cicero translation and scrambled it to make dummy text for Letraset's Body Type sheets.
It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.
12px
Or, we could add a background-color to the parent <header>, making it semi-opaque:
Some Example Website
https://www.joshwcomeau.com/example-website
Some Website
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since 1966, when designers at Letraset and James Mosley, the librarian at St Bride Printing Library in London, took a 1914 Cicero translation and scrambled it to make dummy text for Letraset's Body Type sheets.
It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.
0.6
(We could also tweak the gradient we added in the previous section, making it fade from fully-opaque to semi-opaque, but I prefer to keep the two things separate, so that I can tweak them independently.)
backdrop-filter has been around in all major browsers for a number of years now; according to caniuse(opens in new tab), it’s above 97% support as I write this in October 2026.
At the bottom of this blog post, I’ll include the full copy-ready code, which uses feature queries to make sure that older browsers still have a usable experience. They won’t get the frosted glass effect, but everything will still be readable and usable.
As if this stuff wasn’t complicated enough already, Artur Bien came up with an extra twist; we can create the illusion of a 3D piece of glass by adding a second blurred element with different filter settings:
3px
8px
Isn’t that lovely?!
Here’s how this works: The bottom edge is a separate DOM node with its ownbackdrop-filter. I find it looks better with a smaller blur radius (eg. 8px in the bottom edge, 16px in the main backdrop), and with an extra brightness filter to really make it pop. ✨
The code for this is a bit gnarly 😅. I’ve done my best to explain it in the comments below:
<style> .backdrop { position: absolute; inset: 0; height: 200%; border-radius: 4px; background: hsl(0deg 0% 100% / 0.1); pointer-events: none; backdrop-filter: blur(16px); /* Trim away the overflow. I'm using “mask-image” here rather than “overflow: hidden” because it seems to work better when dealing with nested elements like this (though I haven’t done extensive testing). */ mask-image: linear-gradient( to bottom, black 0, black 50%, transparent 50% ); } .backdrop-edge { /* Set this to whatever you want for the edge thickness: */ --thickness: 6px; position: absolute; inset: 0; /* Only a few pixels will be visible, but we’ll set the height by 100% to include nearby elements. */ height: 100%; /* Shift down by 100% of its own height, so that the edge stacks underneath the main <header>: */ transform: translateY(100%); background: hsl(0deg 0% 100% / 0.1); backdrop-filter: blur(8px) brightness(120%); pointer-events: none; /* We mask out everything aside from the first few pixels, specified by the --thickness variable: */ mask-image: linear-gradient( to bottom, black 0, black var(--thickness), transparent var(--thickness) ); }</style><header> <div class="backdrop"></div> <div class="backdrop-edge"></div></header>
Here’s the final code, with all of the optimizations we’ve discussed. I’ve also included feature queries, to make sure that our website remains legible on older browsers.
Feel free to copy this code, and make it your own! This is intended to be a starting point, not a complete solution. For example, you may wish to tweak the size of the backdrop’s overlap for your particular circumstances.
If you’ve enjoyed this blog post, you might like to know that I have an entire course about CSS!
It’s called CSS for JavaScript Developers(opens in new tab), and it’s built using the same tech stack as this blog: it’s chock full of interactive articles, demos, and opportunities to experiment. There are also bite-sized videos, exercises, workshops, and even a few mini-games!