// blog/design/
Back to Blog
Design · Published August 4, 2026 · 6 min read · By Toine ·

Update note: Rewritten from experience; Safari prefix advice updated for Safari 18, contrast checker and generator links added

Glassmorphism in CSS: The Frosted Glass Effect Done Right

Glassmorphism in CSS: The Frosted Glass Effect Done Right

Frosted glass in CSS is four declarations: a translucent background, backdrop-filter: blur(), a thin light border and a soft shadow. Apple made it the default look of macOS and iOS and it has been requested on every dashboard mockup since. This post gives the recipe, the dark mode version, the limit on how many glass layers a phone will render smoothly, and the contrast check that most glass interfaces fail.

One condition before any of it: glass needs something behind it. On a flat white page there is nothing to blur and the effect is invisible. If the design has no gradient, image or colour behind the card, this is the wrong technique and the rest of the post will not change that.

* * *

The recipe, and what each line is for

`css .glass-card { background: rgba(255, 255, 255, 0.15); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); } `

The background tints the glass white at 15%. Higher is more tint and better contrast, lower is more see-through.

backdrop-filter: blur(12px) blurs whatever sits behind the element. 8 to 16px covers almost every design; above 30px the cost on a phone starts to show.

The -webkit- line is for older Safari. Safari 18 and later take the unprefixed property, but iPhones still on iOS 17 do not, so I keep it until the analytics say those are gone.

The 1px white border at 20% is the line that makes it read as glass rather than as a blurred rectangle. It imitates light catching an edge. Leave it out and the element looks like a rendering bug.

The shadow lifts the card off the background. Keep it soft and low-contrast; a hard shadow under glass looks wrong.

For the background, I put a gradient from the CSS Gradient Generator behind it while tuning, because the values only make sense against something colourful. If you would rather start from a working set of numbers, the Glassmorphism Generator produces this block with sliders for blur, tint and border.

* * *

Dark glass needs more tint

Dark glass on a light page inverts the tint and drops the border further:

`css .glass-dark { background: rgba(0, 0, 0, 0.25); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); color: #ffffff; } `

A version that follows the system theme:

`css .glass { backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border-radius: 16px; }

@media (prefers-color-scheme: light) { .glass { background: rgba(255, 255, 255, 0.2); border: 1px solid rgba(255, 255, 255, 0.3); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); } }

@media (prefers-color-scheme: dark) { .glass { background: rgba(0, 0, 0, 0.3); border: 1px solid rgba(255, 255, 255, 0.08); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); } } `

Dark glass is where readability goes first. White text on 25% black over a dark photo can end up at a contrast ratio of two to one. Test with the real background image, not the placeholder gradient, and with the darkest part of that image behind the text.

For depth, two shadows from the CSS Box Shadow Generator work better than one: a tight one for the edge and a large soft one for distance.

Frosted glass UI cards over colorful background
Frosted glass UI cards over colorful background
* * *

Layers: five is the budget on a phone

A real glass interface stacks several translucent surfaces: a sidebar, a top bar, cards on top of both.

`css .glass-bg { background: linear-gradient(135deg, #667eea, #764ba2, #f093fb); min-height: 100vh; }

.glass-sidebar { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(20px); border-right: 1px solid rgba(255, 255, 255, 0.15); }

.glass-card { background: rgba(255, 255, 255, 0.15); backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 16px; }

.glass-nav { background: rgba(255, 255, 255, 0.08); backdrop-filter: blur(16px); border-bottom: 1px solid rgba(255, 255, 255, 0.12); } `

Four rules that keep a stack readable and rendering:

  • The layer furthest back blurs the most. Blur decreasing towards the front is what creates depth.
  • Deeper layers are more transparent; the front layer that carries text is the least transparent.
  • One border colour for every glass surface, white at varying opacity, so the elements read as one material.
  • Every backdrop-filter is a compositing layer and each one re-blurs on scroll. More than five to eight overlapping glass elements drops frames on a mid-range Android phone. Test on one, not on a desktop.

Give each layer an explicit z-index. Without it, overlapping glass elements blur in whatever order the DOM happens to put them, and the sidebar ends up blurring the card instead of the background.

* * *

Where it works, where it fails, and the contrast check

Works: navigation bars over a hero image, modals and overlays, dashboard cards on a gradient, media player controls, a sign-in form over a photograph.

Fails: solid-colour pages, anything where reading is the job (documentation, long articles, data tables), product pages where the photo has to stay sharp, and interfaces where contrast is a hard requirement, which includes anything a public body or a bank ships.

The check I run before sign-off: put the text over the busiest, darkest part of the real background and measure it in the Color Contrast Checker. WCAG wants 4.5:1 for body text. A 15% white tint over a colourful image usually gives you two to three to one. Raising the tint to 40% or more usually passes, at the cost of some transparency, and a faint text shadow buys a little more.

Two other things: heavy blur behind moving content bothers people with motion sensitivity, so keep blur values moderate on anything that scrolls under the glass. And give browsers without backdrop-filter an opaque fallback, or the text sits on an unblurred photo:

`css @supports not (backdrop-filter: blur(12px)) { .glass-card { background: rgba(255, 255, 255, 0.9); } } `

Glass rules multiply quickly across a design system. When the stylesheet is final it goes through the CSS Minifier.

Modern dashboard with glassmorphism design elements
Modern dashboard with glassmorphism design elements
* * *

FAQ

Does backdrop-filter work in all browsers?

Chrome, Edge, Safari and, since version 103 in 2022, Firefox. Keep the -webkit- prefix for iOS 17 and older. Anything without support gets the @supports fallback above.

Does glassmorphism hurt performance?

A few glass elements with 8 to 16px blur are fine; the compositor handles them. Performance drops with many overlapping layers or blur above 30px, and it drops on phones first. Test on a real mid-range phone and remove layers until it scrolls smoothly.

How do I keep text readable on glass?

Raise the tint until the contrast checker passes. rgba(255, 255, 255, 0.15) almost never does; 0.4 and up usually does. A subtle text shadow helps at the margin. Do not rely on the glass looking readable on your monitor over your test gradient.

Can I mix glassmorphism with neumorphism?

You can write the CSS. Neumorphism is opaque surfaces with inner and outer shadows; glass is transparency and blur. On one screen they look like two designers who did not talk. Pick one.