Tailwind v4MIT licensed

Tailwind Inner-Glow Glass Nav Links

Nav links that illuminate from within: a soft bloom grows inside the glass capsule on hover and a specular rim brightens, so the bar guides the eye with light rather than fills. The port moves the entire ::before layer into the markup with the before: variant — which is where this pattern reads best, because the glow and the state that triggers it end up on the same line.

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-14 grid place-items-center w-full min-h-svh p-5 font-nb14-sans bg-[radial-gradient(circle_at_25%_25%,#132b4d,#060912_70%),url('https://picsum.photos/seed/nb14/1000/700')] bg-center bg-cover [background-blend-mode:overlay]" aria-label="Inner glow hover links demo">
  <nav class="flex flex-wrap items-center gap-x-5.5 gap-y-3 px-4.5 py-3 rounded-full isolate text-white bg-[color-mix(in_oklab,white_10%,transparent)] border border-white/[0.28] shadow-[inset_0_1px_0_rgba(255,255,255,.4),0_20px_44px_-24px_rgba(0,0,0,.7)] backdrop-blur-[20px] backdrop-saturate-[1.7]" aria-label="Primary">
    <span class="text-base font-extrabold">✺ Lumina</span>
    <div class="flex flex-wrap gap-1.5">
      <a class="relative isolate overflow-hidden flex items-center min-h-[42px] px-4.5 rounded-full no-underline text-white/85 text-[14.5px] font-semibold shadow-[inset_0_0_0_1px_rgba(255,255,255,0)] transition-[color,box-shadow] duration-300 before:content-[''] before:absolute before:inset-0 before:z-[1] before:rounded-[inherit] before:pointer-events-none before:opacity-0 before:scale-40 before:bg-[radial-gradient(60px_60px_at_var(--nb14-mx,50%)_var(--nb14-my,50%),color-mix(in_oklab,var(--color-nb14-glow)_85%,white),transparent_70%)] before:transition-[opacity,transform] before:duration-300 hover:text-[#04141c] hover:before:opacity-95 hover:before:scale-100 hover:shadow-[inset_0_0_0_1px_rgba(255,255,255,.6),0_0_22px_-4px_var(--color-nb14-glow)] focus-visible:text-[#04141c] focus-visible:before:opacity-95 focus-visible:before:scale-100 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 aria-[current=page]:text-[#04141c] aria-[current=page]:before:opacity-95 aria-[current=page]:before:scale-100 motion-reduce:transition-none motion-reduce:before:transition-none" href="#nbtw-14" aria-current="page"><span class="relative z-[2]">Home</span></a>
      <a class="relative isolate overflow-hidden flex items-center min-h-[42px] px-4.5 rounded-full no-underline text-white/85 text-[14.5px] font-semibold shadow-[inset_0_0_0_1px_rgba(255,255,255,0)] transition-[color,box-shadow] duration-300 before:content-[''] before:absolute before:inset-0 before:z-[1] before:rounded-[inherit] before:pointer-events-none before:opacity-0 before:scale-40 before:bg-[radial-gradient(60px_60px_at_var(--nb14-mx,50%)_var(--nb14-my,50%),color-mix(in_oklab,var(--color-nb14-glow)_85%,white),transparent_70%)] before:transition-[opacity,transform] before:duration-300 hover:text-[#04141c] hover:before:opacity-95 hover:before:scale-100 hover:shadow-[inset_0_0_0_1px_rgba(255,255,255,.6),0_0_22px_-4px_var(--color-nb14-glow)] focus-visible:text-[#04141c] focus-visible:before:opacity-95 focus-visible:before:scale-100 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 motion-reduce:transition-none motion-reduce:before:transition-none" href="#nbtw-14"><span class="relative z-[2]">Explore</span></a>
      <a class="relative isolate overflow-hidden flex items-center min-h-[42px] px-4.5 rounded-full no-underline text-white/85 text-[14.5px] font-semibold shadow-[inset_0_0_0_1px_rgba(255,255,255,0)] transition-[color,box-shadow] duration-300 before:content-[''] before:absolute before:inset-0 before:z-[1] before:rounded-[inherit] before:pointer-events-none before:opacity-0 before:scale-40 before:bg-[radial-gradient(60px_60px_at_var(--nb14-mx,50%)_var(--nb14-my,50%),color-mix(in_oklab,var(--color-nb14-glow)_85%,white),transparent_70%)] before:transition-[opacity,transform] before:duration-300 hover:text-[#04141c] hover:before:opacity-95 hover:before:scale-100 hover:shadow-[inset_0_0_0_1px_rgba(255,255,255,.6),0_0_22px_-4px_var(--color-nb14-glow)] focus-visible:text-[#04141c] focus-visible:before:opacity-95 focus-visible:before:scale-100 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 motion-reduce:transition-none motion-reduce:before:transition-none" href="#nbtw-14"><span class="relative z-[2]">Studio</span></a>
      <a class="relative isolate overflow-hidden flex items-center min-h-[42px] px-4.5 rounded-full no-underline text-white/85 text-[14.5px] font-semibold shadow-[inset_0_0_0_1px_rgba(255,255,255,0)] transition-[color,box-shadow] duration-300 before:content-[''] before:absolute before:inset-0 before:z-[1] before:rounded-[inherit] before:pointer-events-none before:opacity-0 before:scale-40 before:bg-[radial-gradient(60px_60px_at_var(--nb14-mx,50%)_var(--nb14-my,50%),color-mix(in_oklab,var(--color-nb14-glow)_85%,white),transparent_70%)] before:transition-[opacity,transform] before:duration-300 hover:text-[#04141c] hover:before:opacity-95 hover:before:scale-100 hover:shadow-[inset_0_0_0_1px_rgba(255,255,255,.6),0_0_22px_-4px_var(--color-nb14-glow)] focus-visible:text-[#04141c] focus-visible:before:opacity-95 focus-visible:before:scale-100 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 motion-reduce:transition-none motion-reduce:before:transition-none" href="#nbtw-14"><span class="relative z-[2]">About</span></a>
    </div>
  </nav>
</section>
@import "tailwindcss";

@theme {
  --color-nb14-glow: #7fd4f0;
  --color-nb14-glow: oklch(0.82 0.16 200);
  --font-nb14-sans: system-ui, 'Segoe 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-nb14-glow: #7fd4f0;
  --color-nb14-glow: oklch(0.82 0.16 200);
  --font-nb14-sans: system-ui, 'Segoe UI', sans-serif;
}

What changed in the port

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

  • The entire ::before block becomes before: variants, which puts the bloom's geometry on the same line as the state that reveals it — the clearest readability win of any port in the collection.
  • before:content-[''] is not optional. Without it the pseudo-element has no box and nothing renders, which is the classic silent failure of this pattern.
  • The bloom scales from 40% as well as fading in, so it grows out of the capsule instead of appearing over it. That is what sells it as light inside glass.
  • Every hover:before: is typed with a focus-visible:before: twin, and the active state reuses the same declarations through aria-[current=page]:before:.
  • The label colour flips to a dark ink on bloom because white on a bright glow fails contrast — check the peak frame, not the resting one.
  • The gradient already reads --nb14-mx / --nb14-my with centre defaults, so adding cursor tracking later is two setProperty calls and no markup change.
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 Inner-Glow Glass Nav Links
Source: https://codefronts.com/navigation/tailwind-liquid-glass-navbars/tailwind-inner-glow-glass-nav-links/

Nav links that illuminate from within: a soft bloom grows inside the glass capsule on hover and a specular rim brightens, so the bar guides the eye with light rather than fills. The port moves the entire ::before layer into the markup with the before: variant &mdash; which is where this pattern reads best, because the glow and the state that triggers it end up on the same line.
## HTML
```html
<section class="nbtw-14 grid place-items-center w-full min-h-svh p-5 font-nb14-sans bg-[radial-gradient(circle_at_25%_25%,#132b4d,#060912_70%),url('https://picsum.photos/seed/nb14/1000/700')] bg-center bg-cover [background-blend-mode:overlay]" aria-label="Inner glow hover links demo">
  <nav class="flex flex-wrap items-center gap-x-5.5 gap-y-3 px-4.5 py-3 rounded-full isolate text-white bg-[color-mix(in_oklab,white_10%,transparent)] border border-white/[0.28] shadow-[inset_0_1px_0_rgba(255,255,255,.4),0_20px_44px_-24px_rgba(0,0,0,.7)] backdrop-blur-[20px] backdrop-saturate-[1.7]" aria-label="Primary">
    <span class="text-base font-extrabold">✺ Lumina</span>
    <div class="flex flex-wrap gap-1.5">
      <a class="relative isolate overflow-hidden flex items-center min-h-[42px] px-4.5 rounded-full no-underline text-white/85 text-[14.5px] font-semibold shadow-[inset_0_0_0_1px_rgba(255,255,255,0)] transition-[color,box-shadow] duration-300 before:content-[''] before:absolute before:inset-0 before:z-[1] before:rounded-[inherit] before:pointer-events-none before:opacity-0 before:scale-40 before:bg-[radial-gradient(60px_60px_at_var(--nb14-mx,50%)_var(--nb14-my,50%),color-mix(in_oklab,var(--color-nb14-glow)_85%,white),transparent_70%)] before:transition-[opacity,transform] before:duration-300 hover:text-[#04141c] hover:before:opacity-95 hover:before:scale-100 hover:shadow-[inset_0_0_0_1px_rgba(255,255,255,.6),0_0_22px_-4px_var(--color-nb14-glow)] focus-visible:text-[#04141c] focus-visible:before:opacity-95 focus-visible:before:scale-100 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 aria-[current=page]:text-[#04141c] aria-[current=page]:before:opacity-95 aria-[current=page]:before:scale-100 motion-reduce:transition-none motion-reduce:before:transition-none" href="#nbtw-14" aria-current="page"><span class="relative z-[2]">Home</span></a>
      <a class="relative isolate overflow-hidden flex items-center min-h-[42px] px-4.5 rounded-full no-underline text-white/85 text-[14.5px] font-semibold shadow-[inset_0_0_0_1px_rgba(255,255,255,0)] transition-[color,box-shadow] duration-300 before:content-[''] before:absolute before:inset-0 before:z-[1] before:rounded-[inherit] before:pointer-events-none before:opacity-0 before:scale-40 before:bg-[radial-gradient(60px_60px_at_var(--nb14-mx,50%)_var(--nb14-my,50%),color-mix(in_oklab,var(--color-nb14-glow)_85%,white),transparent_70%)] before:transition-[opacity,transform] before:duration-300 hover:text-[#04141c] hover:before:opacity-95 hover:before:scale-100 hover:shadow-[inset_0_0_0_1px_rgba(255,255,255,.6),0_0_22px_-4px_var(--color-nb14-glow)] focus-visible:text-[#04141c] focus-visible:before:opacity-95 focus-visible:before:scale-100 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 motion-reduce:transition-none motion-reduce:before:transition-none" href="#nbtw-14"><span class="relative z-[2]">Explore</span></a>
      <a class="relative isolate overflow-hidden flex items-center min-h-[42px] px-4.5 rounded-full no-underline text-white/85 text-[14.5px] font-semibold shadow-[inset_0_0_0_1px_rgba(255,255,255,0)] transition-[color,box-shadow] duration-300 before:content-[''] before:absolute before:inset-0 before:z-[1] before:rounded-[inherit] before:pointer-events-none before:opacity-0 before:scale-40 before:bg-[radial-gradient(60px_60px_at_var(--nb14-mx,50%)_var(--nb14-my,50%),color-mix(in_oklab,var(--color-nb14-glow)_85%,white),transparent_70%)] before:transition-[opacity,transform] before:duration-300 hover:text-[#04141c] hover:before:opacity-95 hover:before:scale-100 hover:shadow-[inset_0_0_0_1px_rgba(255,255,255,.6),0_0_22px_-4px_var(--color-nb14-glow)] focus-visible:text-[#04141c] focus-visible:before:opacity-95 focus-visible:before:scale-100 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 motion-reduce:transition-none motion-reduce:before:transition-none" href="#nbtw-14"><span class="relative z-[2]">Studio</span></a>
      <a class="relative isolate overflow-hidden flex items-center min-h-[42px] px-4.5 rounded-full no-underline text-white/85 text-[14.5px] font-semibold shadow-[inset_0_0_0_1px_rgba(255,255,255,0)] transition-[color,box-shadow] duration-300 before:content-[''] before:absolute before:inset-0 before:z-[1] before:rounded-[inherit] before:pointer-events-none before:opacity-0 before:scale-40 before:bg-[radial-gradient(60px_60px_at_var(--nb14-mx,50%)_var(--nb14-my,50%),color-mix(in_oklab,var(--color-nb14-glow)_85%,white),transparent_70%)] before:transition-[opacity,transform] before:duration-300 hover:text-[#04141c] hover:before:opacity-95 hover:before:scale-100 hover:shadow-[inset_0_0_0_1px_rgba(255,255,255,.6),0_0_22px_-4px_var(--color-nb14-glow)] focus-visible:text-[#04141c] focus-visible:before:opacity-95 focus-visible:before:scale-100 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 motion-reduce:transition-none motion-reduce:before:transition-none" href="#nbtw-14"><span class="relative z-[2]">About</span></a>
    </div>
  </nav>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-nb14-glow: #7fd4f0;
  --color-nb14-glow: oklch(0.82 0.16 200);
  --font-nb14-sans: system-ui, 'Segoe UI', sans-serif;
}
```

How this works

The pseudo-element is markup now. Everything the CSS version puts in a ::before block is typed on the element, which means the bloom's geometry sits next to the state that reveals it:

class="relative isolate overflow-hidden
       before:content-[''] before:absolute before:inset-0 before:z-[1]
       before:rounded-[inherit] before:opacity-0 before:scale-40
       before:bg-[radial-gradient(60px_60px_at_var(--nb14-mx,50%)_var(--nb14-my,50%),color-mix(in_oklab,var(--color-nb14-glow)_85%,white),transparent_70%)]
       before:transition-[opacity,transform] before:duration-300
       hover:before:opacity-95 hover:before:scale-100
       focus-visible:before:opacity-95 focus-visible:before:scale-100"

Scale, not just opacity. The bloom starts at 40% of its size, so it appears to grow out of the capsule rather than materialise over it. That single difference is what makes the effect read as light inside glass.

Text has to stay above it. The label is wrapped in a relative z-[2] span and the glow is z-[1]. Without that ordering the gradient washes over the text at exactly the moment the user is reading it.

No JavaScript, but ready for it. The gradient reads --nb14-mx and --nb14-my with 50% defaults, so it works as pure CSS and becomes cursor-anchored the moment you write those two properties from a pointer handler.

Make it yours

  • Recolour the bloom by editing --color-nb14-glow in @theme; the gradient, the rim and the outer halo all read it.
  • Add pointer tracking by writing --nb14-mx / --nb14-my on pointermove; the markup needs no change at all.
  • Make the bloom larger or tighter by editing the two lengths in the radial gradient (60px_60px).
  • Intensify the rim with a higher alpha in the inset shadow, or drop the outer halo entirely for a more restrained bar.
  • Give the active link a dimmer permanent bloom by lowering only its aria-[current=page]:before:opacity.

Gotchas — read before shipping

  • before:content-[''] is required. A pseudo-element with no content box does not render, and this is the most common reason a ported before: effect appears to do nothing.
  • Keep the glow behind the label with explicit z-indices, or the text washes out under the brightest frame.
  • Check contrast at peak bloom, not at rest. The label colour flips to a dark ink here precisely because white-on-glow fails.
  • Every hover:before: needs its focus-visible:before: twin, or the effect simply does not exist for keyboard users.
  • before:rounded-[inherit] keeps the bloom inside the capsule; without it the gradient corners escape a rounded link.
  • Do not animate the shadow's blur radius. Animate the opacity or scale of a pre-blurred layer, as this demo does.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Tailwind v4 sets the floor. The bloom is decorative enhancement over a fully usable frosted link bar; without color-mix() the gradient falls back to its second stop and the bar still works.

Techniques used in this demo

Theme tokensset in @theme, used by a utility

HTML & JSno Tailwind involved

Search CodeFronts

Loading…