How it works
Ordinary "glass" on the web is a blur over a translucent background. Real glass also bends light, so straight lines behind a curved edge stop being straight. CSS has no property for that, but an SVG filter can move pixels around, and backdrop-filter can apply that filter to whatever is behind an element:
.liquid-glass {
backdrop-filter: url(#liquid-glass);
}The filter's main step is feDisplacementMap. It takes an image, the map, and shifts each pixel according to the colour at the same spot in the map: red for the horizontal shift, green for the vertical one. A value of 128 means no shift.
Where the map comes from
The generator treats your element as a piece of glass and works out the path of a ray at each distance from the edge:
- Height. The edge profile gives the height of the glass across the bezel. Past the bezel the top is flat, and flat glass doesn't move anything.
- Slope. The steeper the surface, the more it tilts away from the incoming light.
- Snell's law.
sin θ₂ = sin θ₁ / ngives the angle inside the glass, wherenis the refractive index: about 1.33 for water and 1.5 for window glass. - Sideways shift. The ray keeps travelling at that angle until it reaches the page, so thicker glass moves it further.
The result is stored as colours in a PNG, sized to match the element exactly.
What the controls change
- Bezel is how far the curve reaches in from the edge. Set it to half the short side and the whole object becomes a lens.
- Thickness and refractive index both make the bending stronger.
- Edge profile:
domeis a section of a circle,squirclestays flat for longer and curves sharply near the edge, andsofteases in and out of the curve, so the strongest bending sits in the middle of the bezel. - Fringing displaces red, green and blue by slightly different amounts, which is the rainbow edge you see on real lenses.
Browser support
Using an SVG filter in backdrop-filter works only in Chromium browsers such as Chrome and Edge. Safari and Firefox don't render it, and they can't be detected with a plain @supports (backdrop-filter: url(#x)), which may pass without the filter ever being drawn. So the CSS output starts with a frosted fallback that works everywhere and switches to refraction only after ruling those two engines out:
@supports not ((-moz-appearance: none) or (font: -apple-system-body)) {
.liquid-glass {
backdrop-filter: url(#liquid-glass);
}
}Always check the design with the fallback too. On iPhones every browser uses WebKit, so all of them get the frosted version.
Limits worth knowing
- The size is fixed. The map is drawn for one exact width and height. An element that resizes needs a new map, drawn with JavaScript or generated for each size.
- Readability comes first. Refraction looks best over busy backgrounds, which is exactly where text is hardest to read. Add more tint or blur under any text.
- Keep it small. Filtering the backdrop is expensive, and the browser has to redo it whenever something behind the element changes. One or two pieces of glass are fine, but a page full of them over video is not.