webdevpuneet.com

Liquid Glass Navbar — Free HTML CSS JS Glass Tab Bar

Liquid Glass Navbar · Navigation · Plain HTML, CSS & JS · Live preview

CategoryNavigation

Copy & paste

Source Code

<div class="lg-root" id="lg-root" data-wall="aurora">
  <div class="lg-wall" aria-hidden="true"><span class="lg-blob b1"></span><span class="lg-blob b2"></span><span class="lg-blob b3"></span></div>

  <div class="lg-stage">
    <div class="lg-app" id="lg-app">
      <div class="lg-art" aria-hidden="true"><span class="lg-orb o1"></span><span class="lg-orb o2"></span><span class="lg-ring r1"></span><span class="lg-ring r2"></span></div>

      <div class="lg-navbar" id="lg-navbar" role="tablist" aria-label="App sections">
        <span class="lg-sheen" aria-hidden="true"></span>
        <span class="lg-rim" aria-hidden="true"></span>
        <span class="lg-pill" id="lg-pill" aria-hidden="true"></span>
        <button class="lg-item active" id="lg-tab-0" type="button" role="tab" aria-selected="true" aria-controls="lg-screen" tabindex="0" data-idx="0">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m3 10.5 9-7.5 9 7.5"/><path d="M5 9.5V20a1 1 0 0 0 1 1h4v-6h4v6h4a1 1 0 0 0 1-1V9.5"/></svg>
          <span class="lg-label">Home</span>
        </button>
        <button class="lg-item" id="lg-tab-1" type="button" role="tab" aria-selected="false" aria-controls="lg-screen" tabindex="-1" data-idx="1">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="m15.5 8.5-2 5-5 2 2-5z"/></svg>
          <span class="lg-label">Explore</span>
        </button>
        <button class="lg-item" id="lg-tab-2" type="button" role="tab" aria-selected="false" aria-controls="lg-screen" tabindex="-1" data-idx="2">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="5"/><path d="M12 8v8M8 12h8"/></svg>
          <span class="lg-label">Create</span>
        </button>
        <button class="lg-item" id="lg-tab-3" type="button" role="tab" aria-selected="false" aria-controls="lg-screen" tabindex="-1" data-idx="3">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.9 1.9 0 0 0 3.4 0"/></svg>
          <span class="lg-label">Alerts</span>
        </button>
        <button class="lg-item" id="lg-tab-4" type="button" role="tab" aria-selected="false" aria-controls="lg-screen" tabindex="-1" data-idx="4">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></svg>
          <span class="lg-label">Profile</span>
        </button>
      </div>

      <section class="lg-copy" id="lg-screen" role="tabpanel" aria-labelledby="lg-tab-0">
        <span class="lg-tag lg-sw" id="lg-tag" style="--i:0">Home</span>
        <h1 class="lg-title lg-sw" id="lg-title" style="--i:1">Good evening, Maya</h1>
        <p class="lg-text lg-sw" id="lg-text" style="--i:2">Three new things have landed since you last looked in.</p>
        <ul class="lg-chips lg-sw" id="lg-chips" style="--i:3"><li>3 new posts</li><li>2 mentions</li><li>1 invite</li></ul>
      </section>
    </div>

    <div class="lg-foot">
      <p class="lg-hint">Click a tab: the glass pill stretches like liquid</p>
      <div class="lg-walls" role="group" aria-label="Wallpaper">
        <button type="button" class="lg-wallbtn on" data-wall="aurora" aria-pressed="true" aria-label="Aurora wallpaper" title="Aurora"><i></i></button>
        <button type="button" class="lg-wallbtn" data-wall="sunset" aria-pressed="false" aria-label="Sunset wallpaper" title="Sunset"><i></i></button>
        <button type="button" class="lg-wallbtn" data-wall="lagoon" aria-pressed="false" aria-label="Lagoon wallpaper" title="Lagoon"><i></i></button>
      </div>
    </div>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Liquid-glass body: backdrop blur, saturation, a brightness trim and inset edge highlights
Bevelled 1px gradient rim cut out with a mask and mask-composite
Pointer-tracked sheen and rim light driven by two CSS variables
Pill that stretches and squashes between tabs, more the further it travels
FLIP written by hand and played with the Web Animations API, transform only
Interrupt-safe: clicking mid-move continues from the pill's current position
Scene colours blend smoothly through registered @property colour variables
Floating over a live app screen, so the glass has something to refract
Three wallpapers that change the colours behind the glass
Tab-list semantics with arrow keys, Home, End and a roving tabindex
Icons and labels, with labels hidden on small screens and the pill re-measured automatically
Reduced-motion support and a graceful fallback without backdrop-filter or @property

About this UI Snippet

Liquid Glass Navbar — A Glass Tab Bar With a Stretching Liquid Pill and a Scene That Follows It

Screenshot of the Liquid Glass Navbar snippet rendered live

Apple's "Liquid Glass" look comes from a few ingredients layered together: a heavily blurred, saturated translucent body, a thin bevelled edge that catches light, a soft highlight that moves with your attention, and controls that feel like drops of liquid rather than flat shapes. This snippet recreates those ingredients with plain CSS and a little JavaScript, and shows them where they matter: a floating tab bar laid over a live app screen, so the blur has colour and shapes to refract. Click a tab and the pill stretches across the bar like a drop of liquid while the whole scene behind the glass changes colour.

The glass body

The bar combines backdrop-filter: blur(22px) saturate(180%) brightness(0.95) with a faint white gradient fill. The blur softens what is behind it, the saturation keeps those colours vivid instead of washed out, and the slight brightness drop stops light areas from bleaching the white labels. Two inset box-shadows add a bright hairline along the top and a faint one along the bottom, which reads as the curved thickness of a glass edge.

A rim made with a mask

The bevelled edge is a ::before pseudo-element with a diagonal gradient (bright at the top left, bright again at the bottom right) that is cut down to a 1px ring with a mask. Two mask layers, one over the content box and one over the whole box, are combined with mask-composite: exclude (xor with the -webkit- prefix), which leaves only the border. A second ring, .lg-rim, uses a radial gradient centred on the pointer so the edge glows where you hover, and a sheen layer inside the glass follows the same two CSS variables, --mx and --my.

FLIP, played with the Web Animations API

The pill is one element reused by every tab. FLIP stands for First, Last, Invert, Play. First: measure where the pill looks right now with getBoundingClientRect(), even in the middle of an earlier move. Last: cancel any running animation, then set the pill's real left and width to the clicked tab's box and measure again. Invert: the difference between the two boxes becomes a translateX and a scaleX that make the pill look as if it never moved. Play: pill.animate() runs the transform back to identity. Because animate() starts from a keyframe list, there is no forced reflow and no inline transition to clean up, and only transform is animated, so the motion stays on the compositor thread.

Making it liquid: stretch and squash

A plain slide looks mechanical. The animation has a middle keyframe at 40% where the pill is stretched horizontally and squashed vertically (scale(stretch, 0.88)), with the stretch growing with the distance travelled. Each keyframe carries its own easing: the first leg eases out into the stretch and the second, cubic-bezier(0.3, 1.45, 0.5, 1), overshoots so the pill settles with a small wobble. Easing set on the whole animation instead would hurry past the middle frame and the stretch would barely show. Tapping a far tab stretches the pill more than tapping its neighbour. Because first is measured from the pill's current on-screen box, clicking quickly between tabs continues from wherever the pill is instead of jumping.

The scene follows the tab

Each tab has three colours. They are written to --c1, --c2 and --c3 on the app, and the screen's background gradients read them. The variables are registered with @property as <color>, so a normal transition blends the old colour into the new one over 0.8 seconds, and the same trick tints the pill and the wallpaper blobs. In a browser without @property the colours switch instantly and everything else still works. The copy fades out, changes and fades back in with a 50ms stagger.

Keyboard, accessibility and motion

The bar is a role="tablist" of role="tab" buttons with aria-selected, a roving tabindex and aria-controls pointing at the screen panel, whose aria-labelledby follows the active tab. Arrow keys, Home and End move between tabs. Visitors who prefer reduced motion get an instant pill move, no drifting blobs and near-zero transitions. A ResizeObserver re-places the pill whenever the bar changes size, for example when the labels hide on small screens.

Step by step

How to Use

  1. 1
    Look at the glassThe bar floats over a coloured screen with heavy blur, a bright top edge and a pill under the active tab. The blur only looks like glass because there are colours and shapes behind it.
  2. 2
    Move the pointer over the barA soft highlight follows the cursor inside the glass and the rim glows at the same spot, as if light were catching the edge.
  3. 3
    Click a distant tabTry Profile from Home. The pill stretches and squashes as it travels, overshoots slightly, and settles. A nearby tab stretches it less.
  4. 4
    Click quickly between tabsBecause every move starts from the pill's current on-screen box, rapid clicks stay smooth and never cut.
  5. 5
    Watch the scene changeThe screen behind the bar fades to the tab's palette, the pill takes on a matching tint and the copy changes.
  6. 6
    Try the keyboardTab into the bar, then use the arrow keys, Home and End to move between tabs. The pill moves exactly as it does for a click.
  7. 7
    Change the wallpaperUse the three dots under the screen to swap the background colours. The glass picks up whatever is behind it.
  8. 8
    Make it yoursEdit the TABS array for your own labels, copy and colours. To add a tab, add a button in the HTML and an entry in TABS; the pill measures the real button, so nothing else changes.

Real-world uses

Common Use Cases

iOS and macOS style app shells
Use it as the main navigation of a web app that echoes Apple's Liquid Glass look, alongside a glass card or glassmorphism login for a consistent frosted style.
Product and landing page headers
A translucent pill over a colourful hero reads as premium, and the stretching pill rewards the first click.
Mobile web app tab bars
The icon-only layout on small screens works as a floating bottom or top tab bar for a PWA, next to a bottom nav.
Learning FLIP and the Web Animations API
Every step of First, Last, Invert and Play is labelled in movePill(), and animate() replaces the old forced-reflow trick.
Design system tab and segmented controls
The same pill can follow a selection across variable-width items. Compare it with the simpler segmented control.
Portfolio showcase pieces
It demonstrates backdrop-filter, mask-composite, registered custom properties and FLIP in one compact, explainable component.
Related: Off-Canvas Push Menu
See the Off-Canvas Push Menu for a related navigation pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Transform can run on the compositor thread without recalculating layout on every frame, while left and width force a layout pass each frame. FLIP gives the same visual result, a pill that slides and resizes between two real boxes, while only animating a transform.

The animation has three keyframes: the inverted start, a middle frame at 40% with scale(stretch, 0.88), and the identity end. The stretch value grows with the distance travelled, and each keyframe has its own easing, the last one overshooting, so the pill settles with a small wobble.

movePill() measures the pill's current on-screen box first, then cancels the running animation and sets the new left and width. The new animation therefore starts from where the pill visually is, so it continues smoothly instead of jumping back.

The colours are custom properties registered with @property as colour values, which lets a CSS transition interpolate them. Writing a new --c1 to --c3 on the app element makes every gradient that uses them blend over 0.8 seconds. Without @property support the colours simply switch instantly.

It works in current Chrome, Edge, Safari and Firefox, and the snippet includes the -webkit- prefix for older WebKit. Where it is unsupported the bar keeps its translucent gradient fill without the blur, which still looks acceptable.

Yes. Keep the active index in state and run movePill in a layout effect or watcher after the active tab changes, with refs for the bar and the pill. The animation is a one-shot Web Animations call, so there is no loop to clean up; only the ResizeObserver needs disconnecting on unmount.

Add a button with role="tab" in the HTML and an entry in the TABS array. movePill() reads the clicked button's real box, so a short label and a long one both fit with no pill-width setting to change.

Yes. While the Liquid Glass Navbar snippet gives you one pattern to copy, the free Responsive Navbar Builder at webdevpuneet.com/navbar-builder/ designs a whole navigation bar: logo, links and dropdown menus, a mobile hamburger menu (dropdown panel, slide-in drawer or full screen) and a scroll effect (sticky, shrink, hide on scroll or transparent). It has six presets, a live preview that runs the real code, and exports HTML + CSS + vanilla JS, React, Tailwind, Vue 3 or Angular — or you can click Fork & Edit to keep editing it in My Code.