Frosted glass is one of those effects that looks like magic and turns out to be four CSS properties in a trench coat. This is the complete recipe — the properties that matter, why each one is there, the fallback you must ship, and the mistakes that make glass look cheap. Everything here is plain CSS you can paste into any project.
The core recipe
A glass surface is a translucent fill, a backdrop blur, a light border, and a soft shadow. All four, or it doesn't read as glass:
.glass {
background: rgba(255, 255, 255, 0.06); /* 1 — translucent fill */
backdrop-filter: blur(16px) saturate(140%); /* 2 — the frost */
-webkit-backdrop-filter: blur(16px) saturate(140%); /* Safari ≤ 17 */
border: 1px solid rgba(255, 255, 255, 0.14); /* 3 — the catch-light */
border-radius: 0.875rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35); /* 4 — lift off the page */
}
What each part does:
- The fill is barely there — 4–10% white on a dark page (or ~50–70% white on a light one). Too opaque and it's just a gray card; too transparent and text becomes unreadable.
backdrop-filterblurs whatever is behind the element — that's the entire trick. Thesaturate(140%)is the underrated half: it makes the colors bleeding through richer instead of milky.- The border fakes the bright edge where light hits a pane's rim. A 1px semi-white border does more for the illusion than any shadow.
- The shadow separates the pane from the page. Keep it large, soft and low-opacity.
Glass needs something to blur
The #1 reason glass "doesn't work" in someone's project: the page behind it is a flat color. Blurring a solid gives you… the same solid. Glass only reads over a rich backdrop — a gradient, an image, or color "orbs" painted behind the page:
body {
background-color: #060912;
background-image:
radial-gradient(48rem 48rem at 82% -12%, rgba(109, 74, 245, 0.28), transparent 60%),
radial-gradient(36rem 36rem at -8% 18%, rgba(34, 211, 238, 0.20), transparent 58%);
background-attachment: fixed; /* orbs stay in the viewport on long pages */
}
background-attachment: fixed matters on long pages — without it the color lives only at the very top and your glass turns gray as the user scrolls.
The fallback you must ship
backdrop-filter is well-supported now, but "well-supported" is not "universal" — and when it's missing, a translucent panel silently becomes unreadable text floating on the raw background. Invert the logic: make the surface opaque by default and add the frost only where it works:
.glass {
background: #121a2e; /* opaque — always readable */
}
@supports (backdrop-filter: blur(1px)) {
.glass {
background: rgba(255, 255, 255, 0.06); /* the real glass */
backdrop-filter: blur(16px) saturate(140%);
}
}
Text on glass
Because the surface is translucent, the effective background of your text shifts with whatever scrolls underneath. Two rules keep it readable: keep body text near-white (or near-black on light glass) rather than a brand color, and check contrast against the composited surface, not the fill's rgba value. Brand colors that pass as button fills usually fail as text — use a lightened variant for text and links. (The full accessibility treatment is its own article.)
The mistakes that make glass look cheap
- Too much blur. 30–40px turns frost into fog. 12–20px keeps the backdrop legible as shapes, which is what sells the effect.
- No saturation boost. Plain
blur()looks washed out; addsaturate(130–150%). - Skipping the border. Without the catch-light edge, the panel reads as a smudge.
- Glass on glass on glass. Stacked blurs are expensive to render and visually murky. One glass layer per region; make nested elements solid or nearly so.
- Blurring huge areas.
backdrop-filtercosts GPU time proportional to area. A full-screen glass overlay on a low-end phone will janky-scroll. Prefer cards, bars and menus — not entire viewports.
Or take the shortcut
Everything above — plus the contrast math, the reduced-motion handling, dark/light themes and 42 ready components using the same glass system — is what Farvist packages into one ~21 KB stylesheet. The recipe is genuinely four properties; the polish is the other 95%. If you'd rather not maintain that yourself:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/farvist/dist/farvist.min.css">
<body class="bg-mesh-aurora">
<div class="card glass">…</div>
Either way — hand-rolled or framework — the difference between cheap glass and good glass is the backdrop, the border, the fallback, and restraint with the blur radius. Get those four right and everything you build looks a little bit from the future.