Glass is a translucent surface that blurs what sits behind it. The background is part of the effect: the same classes look like frosted glass over a photo or a mesh, and like a slightly lighter rectangle over a flat color. This guide covers the utilities, the reasons a blur can silently fail, and how to keep the text readable.

The four parts of a glass surface

  • A translucent fill. bg-white/60 compiles to a color-mix() of white and transparent in Tailwind v4. The opacity decides how much of the background shows and, more importantly, how readable the text is.
  • Backdrop blur. backdrop-blur-* sets backdrop-filter: blur(). The v4 scale is xs 4px, sm 8px, md 12px, lg 16px, xl 24px, 2xl 40px, and 3xl 64px. For an exact value use an arbitrary one such as backdrop-blur-[20px].
  • Optional saturation. backdrop-saturate-150 makes the blurred colors richer, which offsets the gray look a light fill adds.
  • An edge. A one-pixel light border (border-white/20) and a soft shadow separate the panel from the background where the blur alone does not.

You do not need to add a -webkit-backdrop-filter line by hand. Tailwind v4 writes both the prefixed and unprefixed declarations for every backdrop-* utility.

A light glass card

html
<div class="rounded-2xl border border-white/20 bg-white/60 p-6 shadow-lg
            backdrop-blur-lg backdrop-saturate-150">
  <h3 class="text-lg font-medium text-slate-900">Glass surface</h3>
  <p class="mt-2 text-slate-700">Your content goes here.</p>
</div>

Glass card

The blur picks up the violet, pink, and blue behind it. The text color was chosen against the darkest of the three.

The text color in that example was not picked by eye. With 60% white over the violet end of the gradient (#7c3aed), the effective surface is about #cbb0f8. Against that, slate-600-style gray (#475569) measures about 4.0:1, which fails the 4.5:1 minimum for body text. A darker gray (#334155) measures about 5.5:1 and passes. The same text passes easily over the pink and blue ends, which is exactly why one spot check is not enough.

A dark glass card

For a dark surface, use a translucent dark fill and light text. Dark glass is often easier to make readable over a busy, bright background, because light text against a darkened backdrop has more room before contrast drops.

html
<div class="rounded-2xl border border-white/10 bg-slate-900/60 p-6
            backdrop-blur-xl">
  <h3 class="text-lg font-medium text-white">Dark glass</h3>
  <p class="mt-2 text-slate-200">Your content goes here.</p>
</div>

Dark glass has the opposite weak spot: over a white or very light area, 60% slate-900 becomes a mid gray (about #6f747f), where white text measures around 4.7:1 and slate-200 text drops to about 3.8:1. If the panel can scroll over light content, raise the fill to 80% or more.

Why the blur sometimes does nothing

When glass classes appear to have no effect, the cause is almost always one of these:

  1. The fill is opaque. bg-white with no opacity hides the backdrop completely, so there is nothing visible to blur. Use bg-white/60 or similar.
  2. There is nothing behind it. Over a flat color, blurred flat color looks the same as flat color. Put the panel over an image, a pattern, a mesh, or scrolling content.
  3. An ancestor limits what the blur can see. The blur only reaches content up to the nearest backdrop root. Tested in Chrome, a parent with opacity below 1, a filter, a mask, mix-blend-mode, or its own backdrop-filter stopped a child's blur from reaching the page behind; a parent with only a transform did not.
  4. The panel is inside an element that fades in with opacity. During the fade the parent's opacity is below 1, so the blur is missing until the animation ends. Animate the panel's own opacity or a transform instead of a wrapper's.

The third point also explains why glass nested inside glass rarely works: the inner panel can only blur the outer panel's contents, not the page.

Provide a solid fallback

Use a readable solid fill first and apply translucency only where background blur is supported. In plain CSS that is a feature query:

css
.glass {
  background: rgb(245 243 247);
  color: rgb(30 27 35);
  border-radius: 1rem;
  padding: 1.5rem;
}

@supports (backdrop-filter: blur(12px)) {
  .glass {
    background: rgb(245 243 247 / 80%);
    backdrop-filter: blur(12px);
  }
}

In Tailwind v4 the supports-[backdrop-filter]: variant generates the same kind of @supports block, so the fallback stays in the markup:

html
<div class="rounded-2xl border border-white/20 bg-white/95 p-6
            supports-[backdrop-filter]:bg-white/60
            supports-[backdrop-filter]:backdrop-blur-lg">
  …
</div>

Respect contrast and transparency settings

Two media features let people ask for less of this effect. prefers-contrast: more is available in Tailwind as the contrast-more: variant. prefers-reduced-transparency has no built-in variant, but a one-line custom variant adds it. Browser support for prefers-reduced-transparency is still incomplete, so treat it as an extra, not as the fallback.

css
/* app.css */
@import "tailwindcss";
@custom-variant reduce-transparency (@media (prefers-reduced-transparency: reduce));
html
<div class="bg-white/60 backdrop-blur-lg
            contrast-more:bg-white/95
            reduce-transparency:bg-white reduce-transparency:backdrop-blur-none">
  …
</div>

If the backdrop moves, stop that animation when reduced motion is requested. Turning off motion on the panel alone will not stop a separate animated background.

css
@media (prefers-reduced-motion: reduce) {
  .glass-backdrop {
    animation: none;
  }
}

A sticky glass header

A header is where glass earns its place most often: content scrolls underneath, and the blur shows that the bar sits above it without hiding it completely.

html
<header class="sticky top-0 z-40 border-b border-slate-900/10
               bg-white/95 supports-[backdrop-filter]:bg-white/75
               supports-[backdrop-filter]:backdrop-blur-md">
  <nav class="mx-auto flex h-14 max-w-5xl items-center justify-between px-4">
    <a href="/" class="font-semibold text-slate-900">Field notes</a>
    <a href="/search" class="text-sm text-slate-700 hover:text-slate-900">Search</a>
  </nav>
</header>
  • Keep the fill high (75% here). Header text sits over whatever happens to scroll past, including images and dark code blocks.
  • Do not put backdrop-filter or opacity on the <body> or a page wrapper. As shown above, that would stop the header from blurring the page.
  • Sticky headers can cover the element that has keyboard focus as someone tabs down the page. Add scroll-padding-top to the html element equal to the header height so focused elements scroll clear of it.

Performance

The browser has to recompute the blur whenever what is behind the panel changes, such as when the page scrolls under a sticky header. A single header or card is normally fine. Problems show up with many glass panels on screen at once, very large panels, and animating the blur radius itself. If scrolling feels heavy on a low-end phone, reduce the number of glass surfaces before reducing the blur.

Tune it in context

Adjust blur and fill opacity before adding glow, extra panels, or more shadows. Those two values decide most of the look and all of the readability.

The liquid glass generator has 20 material presets and sliders for blur, blur falloff, saturation, tint opacity, contrast, radius, specular highlight, rim light, chromatic edge, noise, shadow, and border opacity. Preview over the built-in backdrops or upload the image you plan to use; it stays in your browser. Export as CSS, Tailwind v4, or an AI prompt. The Tailwind export leaves out chromatic edge and noise, and neither export includes a fallback, so add the supports-[backdrop-filter]: pattern above before shipping.

References

MDN: backdrop-filter

Tailwind CSS: backdrop-filter: blur()

MDN: @supports

MDN: prefers-reduced-transparency