UI/UX Craft•11 min read•2026-07-15

Glassmorphism CSS: Copy-Paste Recipes That Stay Readable

Four production glassmorphism recipes for cards, navbars and modals — plus the contrast rule that keeps frosted panels readable and the gotchas that break them.

Alex RiversHead of Product Design & AI Engineering
Glassmorphism CSS: Copy-Paste Recipes That Stay Readable

Glassmorphism is four lines of CSS and one hard problem. The four lines are on every generator on the internet. The hard problem — that text sitting on a transparent panel has a contrast ratio which changes depending on what happens to be behind it — is the reason most frosted interfaces quietly fail an accessibility audit.

Below are recipes you can paste, and then the part that actually decides whether the effect works.

The Minimum Viable Glass

Everything else in this article is a variation on these four properties:

.glass {
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(12px) saturate(160%);
  -webkit-backdrop-filter: blur(12px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.55);
}
      
  • The alpha background is the panel itself. It must be semi-transparent — a fully opaque background makes the blur invisible, which is the single most common reason the effect appears not to work.
  • backdrop-filter blurs whatever is behind the element, as opposed to filter, which blurs the element and its contents. Confusing the two is the second most common reason.
  • saturate() is the part people leave out. Blurring alone washes colour out; pushing saturation back up is what makes it read as glass rather than as fog.
  • The light border imitates the bright edge of a real bevelled pane. Without it the panel has no edge and reads as a smudge.

Keep the -webkit- prefix. Every current major browser supports backdrop-filter unprefixed, but Safari wanted the prefix for years and the duplicate line costs nothing.

Recipe 1: The Card

.glass-card {
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 20px;
  padding: 1.75rem;
  box-shadow: 0 8px 32px rgba(16, 24, 40, 0.08);
  color: #101828;
}
      

The shadow is doing quiet work: a glass panel with no shadow looks painted onto the background rather than floating above it. Keep it soft and low-opacity — a hard shadow reads as plastic.

Recipe 2: Dark Mode

Dark glass is not the light recipe inverted. A dark panel needs a lower alpha to stay legible as glass, a much fainter border, and a deeper shadow to separate it from an already-dark page.

.glass-card-dark {
  background: rgba(17, 24, 39, 0.62);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 20px;
  padding: 1.75rem;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
  color: #f2f4f7;
}
      

Note the border at 9% white rather than 55%. On dark surfaces a bright border stops looking like a light catch and starts looking like an outline.

Recipe 3: The Sticky Navbar

This is glassmorphism's best and safest use — the content scrolling underneath is exactly what gives the blur something to do.

.glass-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  border-bottom: 1px solid rgba(16, 24, 40, 0.08);
}

@supports not (backdrop-filter: blur(1px)) {
  .glass-nav { background: rgba(255, 255, 255, 0.97); }
}
      

The alpha is higher here — 0.82 rather than 0.72 — because navigation labels are small text passing over unpredictable content. That @supports block is the fallback worth writing: where the blur is unavailable or disabled, the bar becomes near-solid instead of transparent and unreadable.

Recipe 4: The Modal Scrim

.modal-scrim {
  position: fixed;
  inset: 0;
  background: rgba(16, 24, 40, 0.45);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.modal-panel {
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 24px;
}
      

Blur the scrim, not the dialog. The panel sits at 0.94 alpha because it carries the content that matters — the glass effect belongs to the layer behind it, where it signals depth without costing you any legibility.

The Readability Rule Nobody Publishes

Here is the problem in one sentence: a translucent panel has no fixed background colour, so it has no fixed contrast ratio. Text that measures a comfortable 8:1 over the pale part of your hero image can drop below 3:1 when the user scrolls and something darker slides underneath. Contrast checkers cannot catch this, because they sample one static state.

The fix is to stop treating alpha as an aesthetic dial and start treating it as an accessibility floor:

  • Body text needs alpha of 0.7 or higher in light mode. Below that you are gambling on what is behind the panel.
  • Test against the worst case, not the pretty case. Take the darkest and lightest regions of every background the panel can travel over, and check the text against both.
  • Large display text can go lower. WCAG asks 4.5:1 for body copy but only 3:1 for text at roughly 24px and above, which is why hero headlines survive on glass that would fail for a paragraph.
  • Never put form labels, error messages or small print on low-alpha glass. If someone has to read it accurately, give it an opaque surface.
  • Borders are decoration, not contrast. A 1px white edge does nothing for the person who cannot read the label inside it.

The practical version of Glassmorphism 2.0 is simply this: mostly-opaque panels with a little blur, rather than mostly-transparent panels with a lot. It looks more expensive and it passes.

Six Reasons Your Glass Is Not Working

  • The background is opaque. background: #fff with a backdrop-filter shows nothing. It needs alpha.
  • You used filter instead of backdrop-filter. That blurs your own text.
  • An ancestor has filter, transform or will-change. Those create a containing block that the backdrop cannot reach past, and the effect silently disappears.
  • There is nothing behind it. Glass over a flat colour is just a lighter flat colour. The effect needs an image, a gradient or moving content to sample.
  • A parent clips it. overflow: hidden combined with a border radius can cut the blurred region in ways that look like rendering bugs.
  • Too many panels at once. Each one is a separate expensive composite. Three sheets of glass on a page is a style; twelve is a slideshow.

Performance: What It Actually Costs

backdrop-filter forces the browser to re-sample and blur the region behind an element, and it repeats that work whenever anything behind it moves. On a sticky navbar over a scrolling page, that is every frame.

  • Never animate the blur radius. Transitioning backdrop-filter from 0 to 20px re-renders the blur on every frame and is the most reliable way to drop frames with this effect. Fade the panel's opacity instead.
  • Do not animate box-shadow either. The usual hover-lift advice to stick to transform and opacity is right, and box-shadow is not one of them — put the shadow on a pseudo-element and transition its opacity.
  • Use will-change sparingly. Leaving it on permanently keeps a compositor layer alive for the life of the page. Add it on interaction, remove it after.
  • Keep glass areas small. Cost scales with the blurred area, so a full-page glass overlay is far more expensive than a card.
/* Hover lift, done cheaply */
.glass-card {
  transition: transform 0.25s cubic-bezier(0.34, 1.2, 0.64, 1);
}
.glass-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 20px 40px -15px rgba(16, 24, 40, 0.3);
  opacity: 0;
  transition: opacity 0.25s ease;
  pointer-events: none;
}
.glass-card:hover { transform: translateY(-4px); }
.glass-card:hover::after { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .glass-card, .glass-card::after { transition: none; }
}
      

Glassmorphism vs Neumorphism

They get mentioned together and they are opposites. Neumorphism extrudes shapes from a single flat colour using paired light and dark shadows — everything is the same colour as the page, which is precisely why it has an unfixable contrast problem and why it faded. Glassmorphism layers a translucent surface over a different background, so it has somewhere to get contrast from. That is the whole reason one survived and the other did not, and it is worth remembering when the next soft-UI revival arrives.

Decide the Look Before You Write the CSS

The values above are cheap to change and the background behind them is not — glass only looks good over something worth blurring, and that is a design decision rather than a CSS one. It is usually faster to generate a few full-page directions and see which one has a background with enough life in it, then implement the winner. The landing page generator and app UI generator both take a prompt like frosted glass panels over a dark photographic background, 16px blur, high text contrast. You get a picture to judge, not code — the CSS on this page is the build step.

Frequently Asked Questions

How do I create a glassmorphism effect in CSS?

Give the element a semi-transparent background, add backdrop-filter: blur(12px) saturate(160%) with the -webkit- duplicate, and finish with a subtle light border. Those four properties are the entire effect; everything else is tuning the alpha, the radius and the shadow for your particular background.

Why is my backdrop-filter not working?

Almost always one of three things: the element's own background is fully opaque so there is nothing to see through, you wrote filter instead of backdrop-filter, or an ancestor has a filter, transform or will-change that creates a containing block the backdrop cannot sample past. Check those in that order.

Is glassmorphism bad for accessibility?

It is risky rather than forbidden. The danger is that contrast varies with whatever scrolls behind the panel, so a design can pass a checker in one state and fail in another. Keep body text on panels of 0.7 alpha or higher, test against the darkest and lightest backgrounds the panel can cross, and never place small or critical text on heavily transparent glass.

What blur value should I use?

Between 10px and 20px for most panels. Under about 8px the background stays legible enough to fight your text; over about 24px you lose the sense that anything specific is behind it and it becomes an expensive way to draw a grey rectangle. Pair whatever you choose with saturate() between 150% and 180%.

Does backdrop-filter hurt performance?

It costs real work, and the cost scales with the blurred area and with how often the content behind it changes. A few cards are fine. What genuinely hurts is animating the blur radius, using full-page glass overlays, or stacking a dozen panels — all of which show up as dropped frames while scrolling.

Is glassmorphism still in style?

Yes, in a more restrained form. The 2020 version — heavy blur, very low alpha, rainbow gradients behind everything — now looks dated. What has lasted is a single translucent surface over a genuinely interesting background with contrast high enough to read comfortably. See our roundup of 2026 web design trends for where it sits among the rest.

How do I make a glassmorphism navbar?

Use position: sticky with a higher alpha than you would use on a card — around 0.82 — because navigation labels are small text crossing unpredictable content. Add an @supports not (backdrop-filter: blur(1px)) block that raises the background to near-opaque, so the bar degrades to solid rather than to unreadable.

What background works best behind glass?

Something with variation but not detail: a photograph with soft large shapes, a multi-stop gradient, or content scrolling past. Flat colours give the blur nothing to work with, and busy detailed images fight the text even after blurring. Large, slow colour transitions are the reliable choice.

Should I use a glassmorphism CSS generator?

They are handy for finding values quickly, but they all produce the same four properties and none of them know what is behind your panel — which is the only part that determines whether the result is readable. Use one to explore, then set the alpha yourself against your real worst-case background.

Glass is one of the few effects that is trivial to implement and easy to get wrong, because the failure is invisible to the person who designed it on a light background at full brightness. Set the alpha for the worst case first and tune the beauty afterwards. Browse the design tools to try a few directions before you build one.

Try Design Maker AI

Ready to generate your next web design in seconds?

Turn your prompts into high-fidelity UI mockups, landing pages, and marketing graphics with AI.