Tailwind v4MIT licensed

Tailwind Responsive Glass Navbar with Burger

A glass navbar that collapses behind an animated burger — ported so the container query lives on the elements that respond to it (@max-[560px]:hidden) and the open state lives on one data-open attribute. That pairing is exactly what a React component wants: useState writes the attribute, and every visual consequence is already in the JSX class strings.

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="nbtw-08 group relative block w-full min-h-svh overflow-hidden font-nb08-sans bg-[linear-gradient(130deg,#1fa2ff,#12d8fa_50%,#a6ffcb)]" id="nbtw-08-root" data-open="false" aria-label="Responsive glass navbar with burger demo">
  <header class="@container relative z-[6] flex items-center gap-4 mx-auto mt-4 max-w-130 px-4 py-3 rounded-[18px] text-[#0a2540] bg-white/[0.24] border border-white/50 shadow-[inset_0_1px_0_rgba(255,255,255,.7),0_16px_36px_-20px_rgba(0,0,0,.4)] backdrop-blur-[20px] backdrop-saturate-[1.8]">
    <a class="text-[17px] font-extrabold no-underline text-[#0a2540] focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">⬡ Stateful</a>
    <nav class="flex gap-1 ms-auto @max-[560px]:hidden" aria-label="Primary">
      <a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-[#0a2540] text-[14.5px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2 aria-[current=page]:bg-white/60" href="#nbtw-08" aria-current="page">Home</a>
      <a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-[#0a2540] text-[14.5px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">Features</a>
      <a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-[#0a2540] text-[14.5px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">Team</a>
      <a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-[#0a2540] text-[14.5px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">Contact</a>
    </nav>
    <button class="hidden ms-auto size-11 flex-col items-center justify-center gap-[5px] border-0 bg-transparent cursor-pointer @max-[560px]:flex focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" id="nbtw-08-burger" type="button" aria-expanded="false" aria-controls="nbtw-08-sheet" aria-label="Menu">
      <span class="w-5.5 h-0.5 rounded-sm bg-[#0a2540] transition-[transform,opacity] duration-300 group-data-[open=true]:translate-y-[7px] group-data-[open=true]:rotate-45 motion-reduce:transition-none"></span>
      <span class="w-5.5 h-0.5 rounded-sm bg-[#0a2540] transition-[transform,opacity] duration-300 group-data-[open=true]:opacity-0 motion-reduce:transition-none"></span>
      <span class="w-5.5 h-0.5 rounded-sm bg-[#0a2540] transition-[transform,opacity] duration-300 group-data-[open=true]:-translate-y-[7px] group-data-[open=true]:-rotate-45 motion-reduce:transition-none"></span>
    </button>
  </header>
  <div class="absolute z-[5] left-1/2 top-[86px] -translate-x-1/2 -translate-y-2.5 w-[calc(100%-32px)] max-w-130 flex flex-col gap-1 p-2.5 rounded-[18px] bg-white/[0.28] border border-white/50 shadow-[0_24px_50px_-24px_rgba(0,0,0,.5)] backdrop-blur-[22px] backdrop-saturate-[1.8] opacity-0 invisible pointer-events-none transition-[opacity,transform,visibility] duration-300 group-data-[open=true]:opacity-100 group-data-[open=true]:visible group-data-[open=true]:pointer-events-auto group-data-[open=true]:translate-y-0 motion-reduce:transition-none" id="nbtw-08-sheet" role="group" aria-label="Mobile menu">
    <a class="flex items-center min-h-11 px-3.5 rounded-xl no-underline text-[#0a2540] text-[15px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2 aria-[current=page]:bg-white/60" href="#nbtw-08" aria-current="page">Home</a>
    <a class="flex items-center min-h-11 px-3.5 rounded-xl no-underline text-[#0a2540] text-[15px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">Features</a>
    <a class="flex items-center min-h-11 px-3.5 rounded-xl no-underline text-[#0a2540] text-[15px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">Team</a>
    <a class="flex items-center min-h-11 px-3.5 rounded-xl no-underline text-[#0a2540] text-[15px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">Contact</a>
  </div>
  <div class="grid place-items-center min-h-[60svh] px-6">
    <p class="max-w-[38ch] text-center text-[#0a2540] text-[15px] font-semibold leading-[1.6]">Narrow the preview below 560px: the links hide and the burger appears, both stated as <code>@max-[560px]:</code> utilities on the elements themselves.</p>
  </div>
</section>
@import "tailwindcss";

@theme {
  --font-nb08-sans: system-ui, 'Segoe UI', sans-serif;
}
(() => {
  const root = document.querySelector('#nbtw-08-root');
  const burger = document.querySelector('#nbtw-08-burger');
  const sheet = document.querySelector('#nbtw-08-sheet');
  if (!root || !burger || !sheet) return;
  let open = false;                       // React: const [open, setOpen] = useState(false)
  const render = () => {
    root.dataset.open = String(open);     // React: data-open={String(open)}
    burger.setAttribute('aria-expanded', String(open));
    if (open) sheet.querySelector('a').focus();
  };
  burger.addEventListener('click', () => { open = !open; render(); });
  document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape' && open) { open = false; render(); burger.focus(); }
  });
  root.addEventListener('click', (e) => {
    if (open && !e.target.closest('header') && !e.target.closest('#nbtw-08-sheet')) { open = false; render(); }
  });
})();

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 {
  --font-nb08-sans: system-ui, 'Segoe UI', sans-serif;
}

What changed in the port

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

  • The container query is stated on the elements that respond to it. @container on the bar, @max-[560px]:hidden on the links, @max-[560px]:flex on the burger — no reflow block at the bottom of a stylesheet to keep in your head.
  • The burger morph is three utilities per bar and no keyframes: translate plus rotate on the outer bars, opacity on the middle one, all driven by the root's data-open.
  • invisible is in the class list and visibility is in the transition list, because an opacity-0 sheet is still focusable — the bug this pattern is famous for.
  • The script is React-shaped on purpose: one boolean, one attribute write, ARIA in sync. In JSX it becomes data-open={String(open)} with the identical class strings.
  • group lives on the root section, not the bar, because both the sheet and the burger bars need to read the same state from different subtrees.
  • Outside-click and Escape are handled in the vanilla version so the port to React does not quietly lose them.
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 Responsive Glass Navbar with Burger
Source: https://codefronts.com/navigation/tailwind-liquid-glass-navbars/tailwind-responsive-glass-navbar-with-burger/

A glass navbar that collapses behind an animated burger &mdash; ported so the container query lives on the elements that respond to it (@max-[560px]:hidden) and the open state lives on one data-open attribute. That pairing is exactly what a React component wants: useState writes the attribute, and every visual consequence is already in the JSX class strings.
## HTML
```html
<section class="nbtw-08 group relative block w-full min-h-svh overflow-hidden font-nb08-sans bg-[linear-gradient(130deg,#1fa2ff,#12d8fa_50%,#a6ffcb)]" id="nbtw-08-root" data-open="false" aria-label="Responsive glass navbar with burger demo">
  <header class="@container relative z-[6] flex items-center gap-4 mx-auto mt-4 max-w-130 px-4 py-3 rounded-[18px] text-[#0a2540] bg-white/[0.24] border border-white/50 shadow-[inset_0_1px_0_rgba(255,255,255,.7),0_16px_36px_-20px_rgba(0,0,0,.4)] backdrop-blur-[20px] backdrop-saturate-[1.8]">
    <a class="text-[17px] font-extrabold no-underline text-[#0a2540] focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">⬡ Stateful</a>
    <nav class="flex gap-1 ms-auto @max-[560px]:hidden" aria-label="Primary">
      <a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-[#0a2540] text-[14.5px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2 aria-[current=page]:bg-white/60" href="#nbtw-08" aria-current="page">Home</a>
      <a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-[#0a2540] text-[14.5px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">Features</a>
      <a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-[#0a2540] text-[14.5px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">Team</a>
      <a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-[#0a2540] text-[14.5px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">Contact</a>
    </nav>
    <button class="hidden ms-auto size-11 flex-col items-center justify-center gap-[5px] border-0 bg-transparent cursor-pointer @max-[560px]:flex focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" id="nbtw-08-burger" type="button" aria-expanded="false" aria-controls="nbtw-08-sheet" aria-label="Menu">
      <span class="w-5.5 h-0.5 rounded-sm bg-[#0a2540] transition-[transform,opacity] duration-300 group-data-[open=true]:translate-y-[7px] group-data-[open=true]:rotate-45 motion-reduce:transition-none"></span>
      <span class="w-5.5 h-0.5 rounded-sm bg-[#0a2540] transition-[transform,opacity] duration-300 group-data-[open=true]:opacity-0 motion-reduce:transition-none"></span>
      <span class="w-5.5 h-0.5 rounded-sm bg-[#0a2540] transition-[transform,opacity] duration-300 group-data-[open=true]:-translate-y-[7px] group-data-[open=true]:-rotate-45 motion-reduce:transition-none"></span>
    </button>
  </header>
  <div class="absolute z-[5] left-1/2 top-[86px] -translate-x-1/2 -translate-y-2.5 w-[calc(100%-32px)] max-w-130 flex flex-col gap-1 p-2.5 rounded-[18px] bg-white/[0.28] border border-white/50 shadow-[0_24px_50px_-24px_rgba(0,0,0,.5)] backdrop-blur-[22px] backdrop-saturate-[1.8] opacity-0 invisible pointer-events-none transition-[opacity,transform,visibility] duration-300 group-data-[open=true]:opacity-100 group-data-[open=true]:visible group-data-[open=true]:pointer-events-auto group-data-[open=true]:translate-y-0 motion-reduce:transition-none" id="nbtw-08-sheet" role="group" aria-label="Mobile menu">
    <a class="flex items-center min-h-11 px-3.5 rounded-xl no-underline text-[#0a2540] text-[15px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2 aria-[current=page]:bg-white/60" href="#nbtw-08" aria-current="page">Home</a>
    <a class="flex items-center min-h-11 px-3.5 rounded-xl no-underline text-[#0a2540] text-[15px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">Features</a>
    <a class="flex items-center min-h-11 px-3.5 rounded-xl no-underline text-[#0a2540] text-[15px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">Team</a>
    <a class="flex items-center min-h-11 px-3.5 rounded-xl no-underline text-[#0a2540] text-[15px] font-semibold transition-colors duration-200 hover:bg-white/45 focus-visible:outline-2 focus-visible:outline-[#0a2540] focus-visible:outline-offset-2" href="#nbtw-08">Contact</a>
  </div>
  <div class="grid place-items-center min-h-[60svh] px-6">
    <p class="max-w-[38ch] text-center text-[#0a2540] text-[15px] font-semibold leading-[1.6]">Narrow the preview below 560px: the links hide and the burger appears, both stated as <code>@max-[560px]:</code> utilities on the elements themselves.</p>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --font-nb08-sans: system-ui, 'Segoe UI', sans-serif;
}
```

## JavaScript
```js
(() => {
  const root = document.querySelector('#nbtw-08-root');
  const burger = document.querySelector('#nbtw-08-burger');
  const sheet = document.querySelector('#nbtw-08-sheet');
  if (!root || !burger || !sheet) return;
  let open = false;                       // React: const [open, setOpen] = useState(false)
  const render = () => {
    root.dataset.open = String(open);     // React: data-open={String(open)}
    burger.setAttribute('aria-expanded', String(open));
    if (open) sheet.querySelector('a').focus();
  };
  burger.addEventListener('click', () => { open = !open; render(); });
  document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape' && open) { open = false; render(); burger.focus(); }
  });
  root.addEventListener('click', (e) => {
    if (open && !e.target.closest('header') && !e.target.closest('#nbtw-08-sheet')) { open = false; render(); }
  });
})();
```

How this works

The container query moves into the markup. The bar declares itself a container; the links and the burger each state what they do below the breakpoint:

<header class="@container ...">
  <nav class="flex @max-[560px]:hidden">…</nav>
  <button class="hidden @max-[560px]:flex ...">…</button>

This is a genuine readability win over the CSS version: the reflow rule is no longer a block at the bottom of the stylesheet that you have to hold in your head while reading the markup.

One attribute drives the sheet and the burger. The root carries group and data-open; the sheet fades and slides, and the three burger bars form an X, all from group-data-[open=true]: variants. The script never touches a class:

let open = false;                 // React: const [open, setOpen] = useState(false)
root.dataset.open = String(open); // React: data-open={String(open)}

Closed means closed. The sheet pairs opacity-0 pointer-events-none invisible at rest with the visible trio when open, so a keyboard user cannot tab into an invisible menu — the failure mode that opacity alone always leaves behind.

Porting to React is a copy-paste. Because the state is an attribute and the design is variants of it, the JSX is the same markup with data-open={String(open)} and onClick={() => setOpen(!open)}. No class-name string juggling, no clsx.

Make it yours

  • Change the breakpoint by editing @max-[560px] on the three elements that use it, or switch to a named container size (@max-md:) if it matches your scale.
  • Swap the sheet's entrance by changing the resting transform: scale-95 to scale-100 reads snappier than a slide.
  • Port to React by rendering data-open from state; every utility in the markup keeps working untouched.
  • Add a scrim by placing a sibling fixed inset-0 bg-black/30 with the same group-data-[open=true]: pair.
  • Keep the links visible at all widths by deleting the two @max-[560px]: utilities on the nav and burger.

Gotchas — read before shipping

  • Container query variants need an ancestor with @container. Without it @max-[560px]: compiles but never matches.
  • Move focus into the sheet on open and back to the burger on close — the script does both, and skipping it is the most common accessibility regression in this pattern.
  • opacity-0 alone leaves a closed sheet focusable. Pair it with invisible and pointer-events-none, and put visibility in the transition list so it does not snap.
  • Lock body scroll while the sheet is open on a real mobile page; a floating glass sheet over a scrolling body feels broken.
  • Keep the burger a 44px button (size-11), not a bare icon — the three bars inside are decorative spans.
  • group-data-* on the burger bars resolves against the root's group, so do not add a second unnamed group on the bar; use a named group if you need both.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Tailwind v4 sets the floor, and container queries are inside it (Chrome 105+, Safari 16+, Firefox 110+). The open state is JS; without it the sheet stays closed and the inline links remain the whole navigation above the breakpoint.

Techniques used in this demo

HTML & JSno Tailwind involved

Search CodeFronts

Loading…