Tailwind v4MIT licensed

Tailwind Liquid Glass Navigation Bar

A sticky bar that turns into a frosted lens as content scrolls beneath it. The port's payoff is the active state: what was a [aria-current=page] attribute selector in a stylesheet becomes an aria-[current=page]: variant sitting on the link itself, so the pill treatment and the semantics that drive it are one line apart.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="lgt-03 relative h-svh min-h-svh w-full overflow-y-auto bg-lgt03-bg font-lgt03-sans" aria-label="Tailwind liquid glass navigation bar">
  <header class="sticky top-0 z-10 flex items-center justify-between gap-5 border-b border-white/25 bg-white/10 px-[clamp(16px,4vw,32px)] py-3.5 backdrop-blur-[20px] backdrop-saturate-180 shadow-[inset_0_1px_0_rgba(255,255,255,.4),0_10px_30px_-20px_rgba(0,0,0,.8)]">
    <span class="text-xl font-extrabold tracking-[-0.01em] text-white">Lumen</span>
    <nav class="flex gap-1.5" aria-label="Primary">
      <a href="#lgt-03-scroll" aria-current="page" class="rounded-full px-3.5 py-2 text-sm font-semibold text-white/85 no-underline transition-colors duration-200 hover:bg-white/16 hover:text-white focus-visible:bg-white/16 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white aria-[current=page]:bg-white aria-[current=page]:text-lgt03-bg">Home</a>
      <a href="#lgt-03-scroll" class="rounded-full px-3.5 py-2 text-sm font-semibold text-white/85 no-underline transition-colors duration-200 hover:bg-white/16 hover:text-white focus-visible:bg-white/16 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white aria-[current=page]:bg-white aria-[current=page]:text-lgt03-bg">Work</a>
      <a href="#lgt-03-scroll" class="rounded-full px-3.5 py-2 text-sm font-semibold text-white/85 no-underline transition-colors duration-200 hover:bg-white/16 hover:text-white focus-visible:bg-white/16 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white aria-[current=page]:bg-white aria-[current=page]:text-lgt03-bg">Studio</a>
      <a href="#lgt-03-scroll" class="rounded-full px-3.5 py-2 text-sm font-semibold text-white/85 no-underline transition-colors duration-200 hover:bg-white/16 hover:text-white focus-visible:bg-white/16 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white aria-[current=page]:bg-white aria-[current=page]:text-lgt03-bg">Contact</a>
    </nav>
  </header>
  <div id="lgt-03-scroll" class="mx-auto flex max-w-235 flex-col items-center gap-7 px-[clamp(16px,4vw,40px)] pt-7 pb-30">
    <img src="https://picsum.photos/seed/lgnav-a/900/500" width="900" height="500" alt="Coastline at dawn" class="block h-auto w-full rounded-[18px] shadow-[0_20px_44px_-24px_rgba(0,0,0,.7)]">
    <h3 class="text-center text-2xl font-extrabold text-white">Scroll &mdash; watch content frost under the bar</h3>
    <img src="https://picsum.photos/seed/lgnav-b/900/500" width="900" height="500" alt="City at night" class="block h-auto w-full rounded-[18px] shadow-[0_20px_44px_-24px_rgba(0,0,0,.7)]">
    <p class="max-w-[52ch] text-center text-base leading-relaxed text-white/75">The sticky bar samples and blurs whatever slides beneath it, in real time, on the compositor &mdash; no scroll listener, no JavaScript.</p>
    <img src="https://picsum.photos/seed/lgnav-c/900/500" width="900" height="500" alt="Mountain range" class="block h-auto w-full rounded-[18px] shadow-[0_20px_44px_-24px_rgba(0,0,0,.7)]">
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-lgt03-bg: #0e0b1a;
  --color-lgt03-bg: oklch(0.16 0.03 295);
  --font-lgt03-sans: 'Manrope', system-ui, sans-serif;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-lgt03-bg: #0e0b1a;
  --color-lgt03-bg: oklch(0.16 0.03 295);
  --font-lgt03-sans: 'Manrope', system-ui, sans-serif;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;800&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • The active-state attribute selector becomes an aria-[current=page]: variant, so the pill styling sits next to the ARIA attribute that justifies it.
  • Fluid padding is a single arbitrary clamp() utility rather than a media query — the responsive behaviour is readable in the class list.
  • No JavaScript: the frost is compositor work triggered by sticky, and adding a scroll listener to 'improve' it only costs INP.
  • The four glass utilities are identical to demo 01's, just at a different blur radius — the material is a vocabulary, not a per-component invention.
  • Every image keeps its intrinsic width/height, so the scroll body is CLS-free.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Liquid Glass Navigation Bar
Source: https://codefronts.com/design-styles/tailwind-liquid-glass-effects/tailwind-liquid-glass-navigation-bar/

A sticky bar that turns into a frosted lens as content scrolls beneath it. The port's payoff is the active state: what was a [aria-current=page] attribute selector in a stylesheet becomes an aria-[current=page]: variant sitting on the link itself, so the pill treatment and the semantics that drive it are one line apart.
## HTML
```html
<section class="lgt-03 relative h-svh min-h-svh w-full overflow-y-auto bg-lgt03-bg font-lgt03-sans" aria-label="Tailwind liquid glass navigation bar">
  <header class="sticky top-0 z-10 flex items-center justify-between gap-5 border-b border-white/25 bg-white/10 px-[clamp(16px,4vw,32px)] py-3.5 backdrop-blur-[20px] backdrop-saturate-180 shadow-[inset_0_1px_0_rgba(255,255,255,.4),0_10px_30px_-20px_rgba(0,0,0,.8)]">
    <span class="text-xl font-extrabold tracking-[-0.01em] text-white">Lumen</span>
    <nav class="flex gap-1.5" aria-label="Primary">
      <a href="#lgt-03-scroll" aria-current="page" class="rounded-full px-3.5 py-2 text-sm font-semibold text-white/85 no-underline transition-colors duration-200 hover:bg-white/16 hover:text-white focus-visible:bg-white/16 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white aria-[current=page]:bg-white aria-[current=page]:text-lgt03-bg">Home</a>
      <a href="#lgt-03-scroll" class="rounded-full px-3.5 py-2 text-sm font-semibold text-white/85 no-underline transition-colors duration-200 hover:bg-white/16 hover:text-white focus-visible:bg-white/16 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white aria-[current=page]:bg-white aria-[current=page]:text-lgt03-bg">Work</a>
      <a href="#lgt-03-scroll" class="rounded-full px-3.5 py-2 text-sm font-semibold text-white/85 no-underline transition-colors duration-200 hover:bg-white/16 hover:text-white focus-visible:bg-white/16 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white aria-[current=page]:bg-white aria-[current=page]:text-lgt03-bg">Studio</a>
      <a href="#lgt-03-scroll" class="rounded-full px-3.5 py-2 text-sm font-semibold text-white/85 no-underline transition-colors duration-200 hover:bg-white/16 hover:text-white focus-visible:bg-white/16 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white aria-[current=page]:bg-white aria-[current=page]:text-lgt03-bg">Contact</a>
    </nav>
  </header>
  <div id="lgt-03-scroll" class="mx-auto flex max-w-235 flex-col items-center gap-7 px-[clamp(16px,4vw,40px)] pt-7 pb-30">
    <img src="https://picsum.photos/seed/lgnav-a/900/500" width="900" height="500" alt="Coastline at dawn" class="block h-auto w-full rounded-[18px] shadow-[0_20px_44px_-24px_rgba(0,0,0,.7)]">
    <h3 class="text-center text-2xl font-extrabold text-white">Scroll &mdash; watch content frost under the bar</h3>
    <img src="https://picsum.photos/seed/lgnav-b/900/500" width="900" height="500" alt="City at night" class="block h-auto w-full rounded-[18px] shadow-[0_20px_44px_-24px_rgba(0,0,0,.7)]">
    <p class="max-w-[52ch] text-center text-base leading-relaxed text-white/75">The sticky bar samples and blurs whatever slides beneath it, in real time, on the compositor &mdash; no scroll listener, no JavaScript.</p>
    <img src="https://picsum.photos/seed/lgnav-c/900/500" width="900" height="500" alt="Mountain range" class="block h-auto w-full rounded-[18px] shadow-[0_20px_44px_-24px_rgba(0,0,0,.7)]">
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-lgt03-bg: #0e0b1a;
  --color-lgt03-bg: oklch(0.16 0.03 295);
  --font-lgt03-sans: 'Manrope', system-ui, sans-serif;
}
```

How this works

Sticky, not fixed. sticky top-0 keeps the bar in flow, so it reserves its own space and adds nothing to Cumulative Layout Shift. Everything that scrolls under it is sampled, blurred and colour-boosted by the compositor — no scroll handler, no class toggling, no JavaScript at all.

class="sticky top-0 z-10 flex items-center justify-between
       border-b border-white/25 bg-white/10
       backdrop-blur-[20px] backdrop-saturate-180
       shadow-[inset_0_1px_0_rgba(255,255,255,.4),0_10px_30px_-20px_rgba(0,0,0,.8)]"

The active link is driven by ARIA, not by a class. aria-[current=page]: reads the attribute the screen reader already needs, which means the visual state and the announced state can never drift apart. This is the port's best structural improvement over the CSS original.

Saturation is what makes it look like glass rather than fog. Blur alone desaturates whatever is behind it; backdrop-saturate-180 pushes the colour back up so photographs stay vivid through the bar. Dropping it is the fastest way to make a frosted navbar look cheap.

Make it yours

  • Dial the material with the blur radius and saturation pair; backdrop-blur-md backdrop-saturate-150 is a subtler, more corporate setting.
  • Switch to smoked glass for light pages: bg-black/20 with border-white/10 and light text.
  • Restyle the active pill entirely inside the aria-[current=page]: chain — no new selector, no new rule.
  • Make the bar taller and airier by editing the single py-3.5, or fully fluid with py-[clamp(10px,2vh,20px)].
  • Add a scrolled-past state with a tiny IntersectionObserver that flips a data-scrolled attribute, then style it with data-scrolled:shadow-lg.

Gotchas — read before shipping

  • A sticky bar only frosts what actually scrolls under it — if your page is short the effect never appears. The demo ships a tall scroll body for exactly this reason.
  • z-10 matters: without a stacking order the blurred bar can be painted under later siblings that create their own contexts.
  • Do not make the fill opaque to 'fix' contrast. Raise the tint to bg-white/16 and keep the text at full opacity instead.
  • aria-[current=page] needs the literal attribute on the link. Setting a class instead loses the accessibility half of the win.
  • Images without width/height reintroduce CLS the sticky bar was avoiding in the first place.
  • backdrop-filter on a sticky element can flicker in older iOS Safari; a transform: translateZ(0) nudge (as [transform:translateZ(0)]) settles it.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Utilities only, no JavaScript. position:sticky is universal; where backdrop-filter is unavailable the bar degrades to a lightly tinted solid header that is still perfectly functional.

Techniques used in this demo

Theme tokensset in @theme, used by a utility

HTML & JSno Tailwind involved

Search CodeFronts

Loading…