Tailwind v4MIT licensed

Tailwind Animated Gradient Blobs Glass Navbar

The showcase setup for any glass bar: three blurred blobs drift behind it so the frost has rich, moving colour to refract — because liquid glass over flat black is invisible. The port turns each orbit into an --animate-* theme entry, so three long keyframe loops become three single utilities and the blobs themselves are four classes each.

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-16 relative overflow-hidden grid place-items-center w-full min-h-svh p-5 font-nb16-sans bg-[#0a0616]" aria-label="Animated gradient blobs glass navbar demo">
  <div class="absolute inset-0 z-0" aria-hidden="true">
    <span class="absolute -top-20 -left-10 size-85 rounded-full blur-[60px] opacity-85 will-change-transform bg-[radial-gradient(circle,#5b8cff,transparent_70%)] animate-nb16-a motion-reduce:animate-none"></span>
    <span class="absolute -bottom-25 -right-8 size-85 rounded-full blur-[60px] opacity-85 will-change-transform bg-[radial-gradient(circle,#ff5ea8,transparent_70%)] animate-nb16-b motion-reduce:animate-none"></span>
    <span class="absolute top-[40%] left-[45%] size-85 rounded-full blur-[60px] opacity-85 will-change-transform bg-[radial-gradient(circle,#38e8c6,transparent_70%)] animate-nb16-c motion-reduce:animate-none"></span>
  </div>
  <nav class="relative z-[1] flex flex-wrap items-center gap-x-5.5 gap-y-3 px-5 py-3.5 rounded-[20px] text-white bg-[color-mix(in_oklab,white_10%,transparent)] border border-white/30 shadow-[inset_0_1px_0_rgba(255,255,255,.45),0_24px_50px_-26px_rgba(0,0,0,.7)] backdrop-blur-[24px] backdrop-saturate-[1.9]" aria-label="Primary">
    <span class="text-base font-extrabold">◐ Prism</span>
    <div class="flex flex-wrap gap-1.5">
      <a class="flex items-center min-h-10 px-4 rounded-xl no-underline text-white opacity-90 text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 hover:bg-white/[0.18] focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 aria-[current=page]:opacity-100 aria-[current=page]:bg-white/[0.18]" href="#nbtw-16" aria-current="page">Gallery</a>
      <a class="flex items-center min-h-10 px-4 rounded-xl no-underline text-white opacity-90 text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 hover:bg-white/[0.18] focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2" href="#nbtw-16">Artists</a>
      <a class="flex items-center min-h-10 px-4 rounded-xl no-underline text-white opacity-90 text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 hover:bg-white/[0.18] focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2" href="#nbtw-16">Shop</a>
      <a class="flex items-center min-h-10 px-4 rounded-xl no-underline text-white opacity-90 text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 hover:bg-white/[0.18] focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2" href="#nbtw-16">Journal</a>
    </div>
  </nav>
</section>
@import "tailwindcss";

@theme {
  --font-nb16-sans: system-ui, 'Segoe UI', sans-serif;
  --animate-nb16-a: nb16-a 16s ease-in-out infinite;
  --animate-nb16-b: nb16-b 20s ease-in-out infinite;
  --animate-nb16-c: nb16-c 24s ease-in-out infinite;
}
/* Transform-only, so all three layers stay on the compositor. The durations
   are mutually non-divisible on purpose: the composition never realigns, so
   it keeps producing new colour instead of visibly looping. */

@keyframes nb16-a {
  50% {
    transform: translate(120px, 80px) scale(1.2);
  }
}

@keyframes nb16-b {
  50% {
    transform: translate(-120px, -60px) scale(1.15);
  }
}

@keyframes nb16-c {
  50% {
    transform: translate(-80px, 60px) scale(0.85);
  }
}

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-nb16-sans: system-ui, 'Segoe UI', sans-serif;
  --animate-nb16-a: nb16-a 16s ease-in-out infinite;
  --animate-nb16-b: nb16-b 20s ease-in-out infinite;
  --animate-nb16-c: nb16-c 24s ease-in-out infinite;
}
/* Transform-only, so all three layers stay on the compositor. The durations
   are mutually non-divisible on purpose: the composition never realigns, so
   it keeps producing new colour instead of visibly looping. */

@keyframes nb16-a {
  50% {
    transform: translate(120px, 80px) scale(1.2);
  }
}

@keyframes nb16-b {
  50% {
    transform: translate(-120px, -60px) scale(1.15);
  }
}

@keyframes nb16-c {
  50% {
    transform: translate(-80px, 60px) scale(0.85);
  }
}

What changed in the port

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

  • Three orbits become three --animate-* theme entries, so each blob carries one animation utility instead of a shorthand — and retiming the whole composition happens in the token block.
  • The durations (16s, 20s, 24s) are mutually non-divisible so the loops never realign, which is the cheapest way to keep an ambient backdrop from reading as a repeat.
  • will-change-transform is a first-class utility and sits only on the three animated layers — applied broadly it is memory spent for nothing.
  • backdrop-saturate-[1.9] is higher here than in any other demo because there is genuinely saturated colour underneath; the same value over a flat backdrop does nothing at all.
  • Every blob carries motion-reduce:animate-none, freezing the composition into a static gradient rather than slowing it down.
  • The blob container is aria-hidden: three empty spans have nothing to announce, and the nav beside them is the real landmark.
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 Animated Gradient Blobs Glass Navbar
Source: https://codefronts.com/navigation/tailwind-liquid-glass-navbars/tailwind-animated-gradient-blobs-glass-navbar/

The showcase setup for any glass bar: three blurred blobs drift behind it so the frost has rich, moving colour to refract &mdash; because liquid glass over flat black is invisible. The port turns each orbit into an --animate-* theme entry, so three long keyframe loops become three single utilities and the blobs themselves are four classes each.
## HTML
```html
<section class="nbtw-16 relative overflow-hidden grid place-items-center w-full min-h-svh p-5 font-nb16-sans bg-[#0a0616]" aria-label="Animated gradient blobs glass navbar demo">
  <div class="absolute inset-0 z-0" aria-hidden="true">
    <span class="absolute -top-20 -left-10 size-85 rounded-full blur-[60px] opacity-85 will-change-transform bg-[radial-gradient(circle,#5b8cff,transparent_70%)] animate-nb16-a motion-reduce:animate-none"></span>
    <span class="absolute -bottom-25 -right-8 size-85 rounded-full blur-[60px] opacity-85 will-change-transform bg-[radial-gradient(circle,#ff5ea8,transparent_70%)] animate-nb16-b motion-reduce:animate-none"></span>
    <span class="absolute top-[40%] left-[45%] size-85 rounded-full blur-[60px] opacity-85 will-change-transform bg-[radial-gradient(circle,#38e8c6,transparent_70%)] animate-nb16-c motion-reduce:animate-none"></span>
  </div>
  <nav class="relative z-[1] flex flex-wrap items-center gap-x-5.5 gap-y-3 px-5 py-3.5 rounded-[20px] text-white bg-[color-mix(in_oklab,white_10%,transparent)] border border-white/30 shadow-[inset_0_1px_0_rgba(255,255,255,.45),0_24px_50px_-26px_rgba(0,0,0,.7)] backdrop-blur-[24px] backdrop-saturate-[1.9]" aria-label="Primary">
    <span class="text-base font-extrabold">◐ Prism</span>
    <div class="flex flex-wrap gap-1.5">
      <a class="flex items-center min-h-10 px-4 rounded-xl no-underline text-white opacity-90 text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 hover:bg-white/[0.18] focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 aria-[current=page]:opacity-100 aria-[current=page]:bg-white/[0.18]" href="#nbtw-16" aria-current="page">Gallery</a>
      <a class="flex items-center min-h-10 px-4 rounded-xl no-underline text-white opacity-90 text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 hover:bg-white/[0.18] focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2" href="#nbtw-16">Artists</a>
      <a class="flex items-center min-h-10 px-4 rounded-xl no-underline text-white opacity-90 text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 hover:bg-white/[0.18] focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2" href="#nbtw-16">Shop</a>
      <a class="flex items-center min-h-10 px-4 rounded-xl no-underline text-white opacity-90 text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 hover:bg-white/[0.18] focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2" href="#nbtw-16">Journal</a>
    </div>
  </nav>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --font-nb16-sans: system-ui, 'Segoe UI', sans-serif;
  --animate-nb16-a: nb16-a 16s ease-in-out infinite;
  --animate-nb16-b: nb16-b 20s ease-in-out infinite;
  --animate-nb16-c: nb16-c 24s ease-in-out infinite;
}
/* Transform-only, so all three layers stay on the compositor. The durations
   are mutually non-divisible on purpose: the composition never realigns, so
   it keeps producing new colour instead of visibly looping. */

@keyframes nb16-a {
  50% {
    transform: translate(120px, 80px) scale(1.2);
  }
}

@keyframes nb16-b {
  50% {
    transform: translate(-120px, -60px) scale(1.15);
  }
}

@keyframes nb16-c {
  50% {
    transform: translate(-80px, 60px) scale(0.85);
  }
}
```

How this works

Each orbit is a token. The three loops differ only in path and duration, and naming them in @theme is what keeps the markup to one utility per blob:

@theme {
  --animate-nb16-a: nb16-a 16s ease-in-out infinite;
  --animate-nb16-b: nb16-b 20s ease-in-out infinite;
  --animate-nb16-c: nb16-c 24s ease-in-out infinite;
}
@keyframes nb16-a { 50% { transform: translate(120px, 80px) scale(1.2); } }

Deliberately mismatched durations. 16s, 20s and 24s never realign, so the composition keeps producing new colour combinations instead of visibly looping — the cheapest way to make an ambient background feel alive.

Only transforms animate. Each keyframe moves and scales; nothing touches top, left or the blur radius. That is what lets three 340px blurred layers run at once without dropping frames.

The bar is the same glass as everywhere else, with one change: saturation is pushed to 1.9 because there is actually saturated colour behind it. On a flat backdrop that setting does nothing; here it is the difference between frost and a lens.

Make it yours

  • Recolour and reposition the blobs freely — each is one gradient utility and two offset utilities.
  • Retime the drift in @theme; keep the three durations mutually non-divisible so the composition never visibly repeats.
  • Add a fourth blob by declaring one more --animate-* entry and its keyframes; the markup cost is a single span.
  • Lower blur-[60px] to [40px] for defined colour fields, or raise it for a softer wash — higher blur is more expensive.
  • Add a grain overlay above the blobs and below the bar for a more photographic backdrop.

Gotchas — read before shipping

  • Blob blur is paint-heavy. Keep the count low and the sizes bounded — three 340px layers is a considered budget, not an arbitrary number.
  • Animate transforms only. Animating top/left takes all three layers off the compositor and onto the main thread.
  • will-change-transform belongs only on the animated layers. Applied broadly it costs memory for nothing.
  • Always ship the reduced-motion freeze. Three drifting blobs behind a nav is exactly the kind of ambient motion that triggers discomfort.
  • The blob container must be aria-hidden; it is three empty spans and would otherwise be announced as nothing at all.
  • Keep the bar above the blobs with an explicit z-index. A blob composited over the nav destroys its contrast.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Tailwind v4 sets the floor. Blobs plus backdrop-filter degrade to a static gradient behind a tinted bar, which is still a perfectly reasonable design.

Techniques used in this demo

Plain CSSno Tailwind surface

Search CodeFronts

Loading…