---
name: frosted-glass-effect
description: Use when you want "Clean, elegant, polished" - Blurs background content through semi-transparent surfaces.
---

# Frosted Glass Effect

> **Category:** Glass / Depth  -  **Personality:** Clean, elegant, polished
>
> **Best use:** Navigation bars, modals, floating panels
>
> **Ratings:** Professional 5/5 - Casual 3/5 - Exotic 3/5 - Visual impact 4/5 - Difficulty 2/5 - Performance cost 3/5

## What it is

Frosted Glass is the focused, _functional_ application of the glass look: a semi-transparent surface that **blurs (and lightly re-saturates) whatever sits behind it** via `backdrop-filter`, finished with a thin light border and a soft shadow so it reads as a frosted pane floating over the page. Where broad "glassmorphism" is decoration, frosted glass earns its keep as **UI chrome** — sticky navigation bars, modal scrims, and floating docks/toolbars — letting foreground content stay crisp and legible while the busy stuff behind it melts into a soft wash. It only works when there is real, varied content (a photo, a gradient, a scrolling page) behind it for the blur to act on.

## Dependencies / CDN

None for the effect itself — pure CSS (`backdrop-filter`). The demo's modal open/close and dock filtering are plain **vanilla JS** (no library). The display/body fonts are optional:

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Schibsted+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet">
```

## HTML

A scrolling "app" with the three classic frosted placements — a sticky nav, a floating dock, and a modal. They all reuse one `.fg-glass` class:

```html
<div class="fg-stage">
  <div class="fg-app" id="fgApp">                       <!-- scroll viewport -->
    <header class="fg-nav fg-glass">…brand · links…</header>   <!-- 1) sticky frosted nav -->
    <section class="fg-feed">
      <article class="fg-card">
        <img class="fg-photo" src="…/landscape-coast.jpg" alt="…">
        <div class="fg-cap fg-glass">Cala Blanca House · Formentera</div>  <!-- frost over photo -->
      </article>
      <!-- …more photo cards (gives the nav/dock something to frost) … -->
    </section>
  </div>

  <div class="fg-dock fg-glass">…All · Coast · City · Alpine…</div>   <!-- 2) floating frosted panel -->

  <div class="fg-modal" id="fgModal" hidden>                         <!-- 3) frosted modal -->
    <div class="fg-scrim" data-close></div>                          <!-- frosts the whole app -->
    <div class="fg-sheet fg-glass" role="dialog" aria-modal="true">…</div>
  </div>
</div>
```

## CSS

```css
/* THE EFFECT — reuse this one class everywhere frost is needed */
.fg-glass{
  background:linear-gradient(155deg, rgba(255,255,255,.17), rgba(255,255,255,.055));
  -webkit-backdrop-filter:blur(26px) saturate(155%);   /* Safari/iOS need the prefix */
          backdrop-filter:blur(26px) saturate(155%);
  border:1px solid rgba(255,255,255,.26);              /* lit top edge */
  box-shadow:0 22px 55px -26px rgba(0,0,0,.6),          /* float */
             inset 0 1px 0 rgba(255,255,255,.4);        /* inner highlight */
}
/* fallback where backdrop-filter is unsupported — solid-ish surface */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .fg-glass{ background:rgba(20,22,30,.86) }
}

/* the stage must isolate so the blur samples only inside it */
.fg-stage{ position:relative; overflow:hidden; border-radius:26px; isolation:isolate; background:#0a0b0f }
.fg-app{ position:relative; z-index:1; height:600px; overflow-y:auto }

/* 1) NAV — sticks at the top and frosts the photos scrolling under it */
.fg-nav{ position:sticky; top:0; z-index:4; margin:10px 12px 0; padding:13px 18px; border-radius:18px }

/* 2) DOCK — absolutely pinned, floats over the scroll area */
.fg-dock{ position:absolute; z-index:3; left:50%; bottom:18px; transform:translateX(-50%);
  border-radius:999px; padding:6px }

/* 3) MODAL — the scrim itself frosts + dims the whole app behind the sheet */
.fg-modal{ position:absolute; inset:0; z-index:5; display:grid; place-items:center }
.fg-modal[hidden]{ display:none }
.fg-scrim{ position:absolute; inset:0; background:rgba(8,9,12,.5); opacity:0; transition:opacity .3s;
  -webkit-backdrop-filter:blur(14px) saturate(120%); backdrop-filter:blur(14px) saturate(120%) }
.fg-modal.is-open .fg-scrim{ opacity:1 }
.fg-sheet{ position:relative; z-index:1; width:min(440px,100%); border-radius:24px; overflow:hidden }
```

Optional **frost grain** (used in the demo) — a faint SVG-noise overlay so the pane reads as *frosted*, not clear glass. Add the class `fg-frost` alongside `fg-glass`:

```css
.fg-frost{ position:relative }
.fg-frost::after{ content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  opacity:.05; mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E") }
```

## JavaScript

Pure CSS does the frosting — JS only drives the demo's chrome (nav firms on scroll, dock filters, modal opens). Trimmed essentials:

```js
var app=document.getElementById('fgApp'),
    nav=document.getElementById('fgNav'),
    modal=document.getElementById('fgModal');

// nav frost firms up once real content scrolls underneath it
app.addEventListener('scroll',function(){
  nav.classList.toggle('is-scrolled', app.scrollTop>36);
},{passive:true});

// modal: the scrim blurs the whole app behind the sheet
function open(){
  modal.hidden=false;
  requestAnimationFrame(function(){ modal.classList.add('is-open'); }); // next frame so it transitions
  modal.querySelector('.fg-close').focus();
}
function close(){
  modal.classList.remove('is-open');
  setTimeout(function(){ modal.hidden=true; }, 340);                    // hide after the fade-out
}
document.querySelectorAll('[data-open]').forEach(function(b){ b.addEventListener('click',open); });
modal.querySelectorAll('[data-close]').forEach(function(x){ x.addEventListener('click',close); });
document.addEventListener('keydown',function(e){ if(e.key==='Escape' && !modal.hidden) close(); });
```

## How it works

- **`backdrop-filter: blur() saturate()`** samples the pixels *behind* the element and blurs them in place — that is the entire effect. `saturate(155%)` keeps the bleed-through colour alive instead of going muddy grey.
- **Translucent fill (rgba ~0.05–0.17)** lets the blurred backdrop show through. The whiter / more opaque you push it, the more it reads as *frosted* rather than *clear* glass.
- **1px light border + `inset 0 1px 0` highlight** fake the lit top edge of a real glass slab; the large soft outer shadow lifts it off the page.
- **Placement is what makes it "frosted glass" specifically:** a `position:sticky` nav frosts the page scrolling under it; an `absolute` dock frosts the area it floats over; a full-cover scrim frosts the whole view behind a modal. In every case the glass is an overlay sitting *above* moving content.

## Customization

- **Frost weight:** `blur(10px)` (light) → `blur(30px)` (heavy milk glass). Pair heavier blur with a higher white-fill alpha.
- **Tone:** neutral white fill for clean UI, or a brand tint like `rgba(120,160,205,.12)`.
- **Edge & lift:** raise the border alpha for a crisper rim; grow the shadow blur/spread to float higher.
- **Texture:** the optional SVG-noise `::after` sells true "frost"; drop it for slicker modern glass.
- **Modal depth:** blur the scrim *less* than the panels (demo uses 14px scrim vs 26px chrome) so the dialog still reads as the front-most layer.

## Accessibility & performance

- `backdrop-filter` is GPU-bound: keep the count of simultaneously-blurred panels modest and **never animate the blur radius** — animate `opacity`/`transform` instead (the demo fades the scrim, not the blur).
- Maintain text contrast over the *blurred* result; if text can land on light areas, darken the fill or add a gradient scrim beneath the text.
- Honour `prefers-reduced-motion` for the modal/filter transitions (the demo's only motion); the frost itself is static and needs no gating.
- Modal a11y: set `role="dialog"` + `aria-modal="true"`, move focus into the sheet on open, restore it on close, and close on `Esc` + scrim click (all wired in the demo).

## Gotchas

- **Add the `-webkit-` prefix** or Safari/iOS silently render nothing.
- **Nothing behind it = no effect.** Frosted glass over a flat solid colour is just a translucent box — it needs a photo / gradient / scrolling content beneath to blur.
- A rounded `overflow:hidden` stage should set **`isolation:isolate`** so the blur samples only inside the stage, not the page behind it.
- An ancestor with `filter` / `transform` / `will-change` creates a containing block that can **break or clip** `backdrop-filter`. Keep the glass and the content it samples as siblings inside the isolated stage.
- `position:sticky` panels make their own stacking context — fine for `backdrop-filter`, but ensure they sit *above* the scrolling content (`z-index`) or they sample nothing.
