Tailwind v4MIT licensed

Tailwind Safari Toolbar with SVG Refraction

The toolbar that goes past blur into real optical refraction, ported with the filter reference sitting on the element as an arbitrary utility: [filter:url(#nbtw-03-refract)]. The feTurbulence + feDisplacementMap pair stays in the markup where it belongs, the frost is backdrop-blur-[14px], and the only stylesheet rule is the @supports not fallback — a negated feature query has no variant form.

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-03 relative block w-full min-h-svh overflow-hidden font-nb03-sans bg-[linear-gradient(125deg,#00c6fb,#7b5bff_45%,#ff5ea8),url('https://picsum.photos/seed/nb03/1000/700')] bg-center bg-cover [background-blend-mode:overlay]" aria-label="Safari liquid glass toolbar demo">
  <svg class="absolute w-0 h-0" aria-hidden="true" focusable="false"><filter id="nbtw-03-refract"><feTurbulence type="fractalNoise" baseFrequency="0.008 0.012" numOctaves="2" seed="7" result="n"/><feDisplacementMap in="SourceGraphic" in2="n" scale="16" xChannelSelector="R" yChannelSelector="G"/></filter></svg>
  <header class="sticky top-0 z-[5] flex items-center gap-2.5 h-13 px-3.5 isolate bg-[color-mix(in_oklab,white_12%,transparent)] border-b border-white/30 shadow-[inset_0_1px_0_rgba(255,255,255,.45),0_10px_30px_-18px_rgba(0,0,0,.5)] backdrop-blur-[14px] backdrop-saturate-[1.7]">
    <span class="nbtw-03__glass absolute inset-x-0 -inset-y-10 z-0 pointer-events-none opacity-60 [background:inherit] [filter:url(#nbtw-03-refract)] motion-reduce:[filter:none]" aria-hidden="true"></span>
    <div class="relative z-[2] flex gap-1">
      <button class="size-[34px] rounded-[10px] border border-white/35 bg-white/[0.16] text-white text-lg leading-none cursor-pointer transition-colors duration-200 hover:bg-white/30 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2" type="button" aria-label="Back">‹</button>
      <button class="size-[34px] rounded-[10px] border border-white/35 bg-white/[0.16] text-white text-lg leading-none cursor-pointer transition-colors duration-200 hover:bg-white/30 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2" type="button" aria-label="Forward">›</button>
    </div>
    <label class="relative z-[2] flex flex-1 items-center gap-2 h-[34px] px-3.5 rounded-[10px] bg-white/90 shadow-[inset_0_1px_3px_rgba(0,0,0,.15)] focus-within:shadow-[0_0_0_3px_rgba(255,255,255,.7),inset_0_1px_3px_rgba(0,0,0,.15)]">
      <span class="text-xs" aria-hidden="true">🔒</span>
      <input class="flex-1 min-w-0 border-0 bg-transparent outline-none text-sm text-[#111]" type="text" value="codefronts.com/liquid-glass" aria-label="Address" spellcheck="false">
    </label>
    <button class="relative z-[2] size-[34px] rounded-[10px] border border-white/35 bg-white/[0.16] text-white text-lg leading-none cursor-pointer transition-colors duration-200 hover:bg-white/30 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2" type="button" aria-label="Share">⤴</button>
  </header>
  <div class="px-6 py-10 text-white">
    <p class="max-w-[40ch] text-lg font-semibold leading-[1.6] [text-shadow:0_2px_12px_rgba(0,0,0,.35)]">Watch the background bend through the toolbar — that warp is a live SVG displacement map, not a blur.</p>
  </div>
</section>
@import "tailwindcss";

@theme {
  --font-nb03-sans: -apple-system, system-ui, 'Segoe UI', sans-serif;
}
/* A negated feature query has no variant form, so this is the one rule that
   cannot move into the markup: where filter: url() will not resolve, the
   displaced layer is removed and the plain backdrop-blur tier remains. */

@supports not (filter: url(#nbtw-03-refract)) {
  .nbtw-03__glass {
    display: none;
  }
}

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-nb03-sans: -apple-system, system-ui, 'Segoe UI', sans-serif;
}
/* A negated feature query has no variant form, so this is the one rule that
   cannot move into the markup: where filter: url() will not resolve, the
   displaced layer is removed and the plain backdrop-blur tier remains. */

@supports not (filter: url(#nbtw-03-refract)) {
  .nbtw-03__glass {
    display: none;
  }
}

What changed in the port

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

  • The filter reference is an arbitrary property — [filter:url(#nbtw-03-refract)] — because a document-scoped id is not a design-scale value and Tailwind is right not to ship a utility for it.
  • The SVG primitives stay in the markup unchanged. That is the point of the port: the mechanism is SVG 1.1 and universal, and only its plumbing moves into classes.
  • [background:inherit] on the glass layer is load-bearing. The displacement needs pixels to move, so the layer inherits the root background rather than being transparent.
  • isolate on the toolbar plus relative z-[2] on the controls keeps every piece of live text above the warped layer — displaced text is unreadable, and this is the ordering that prevents it.
  • The @supports not tier is the honest cost of the port. Tailwind has supports-[...] but no negation, so the fallback rule stays as CSS.
  • motion-reduce:[filter:none] removes the refraction outright, because a displacement map has no duration to calm.
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 Safari Toolbar with SVG Refraction
Source: https://codefronts.com/navigation/tailwind-liquid-glass-navbars/tailwind-safari-toolbar-with-svg-refraction/

The toolbar that goes past blur into real optical refraction, ported with the filter reference sitting on the element as an arbitrary utility: [filter:url(#nbtw-03-refract)]. The feTurbulence + feDisplacementMap pair stays in the markup where it belongs, the frost is backdrop-blur-[14px], and the only stylesheet rule is the @supports not fallback &mdash; a negated feature query has no variant form.
## HTML
```html
<section class="nbtw-03 relative block w-full min-h-svh overflow-hidden font-nb03-sans bg-[linear-gradient(125deg,#00c6fb,#7b5bff_45%,#ff5ea8),url('https://picsum.photos/seed/nb03/1000/700')] bg-center bg-cover [background-blend-mode:overlay]" aria-label="Safari liquid glass toolbar demo">
  <svg class="absolute w-0 h-0" aria-hidden="true" focusable="false"><filter id="nbtw-03-refract"><feTurbulence type="fractalNoise" baseFrequency="0.008 0.012" numOctaves="2" seed="7" result="n"/><feDisplacementMap in="SourceGraphic" in2="n" scale="16" xChannelSelector="R" yChannelSelector="G"/></filter></svg>
  <header class="sticky top-0 z-[5] flex items-center gap-2.5 h-13 px-3.5 isolate bg-[color-mix(in_oklab,white_12%,transparent)] border-b border-white/30 shadow-[inset_0_1px_0_rgba(255,255,255,.45),0_10px_30px_-18px_rgba(0,0,0,.5)] backdrop-blur-[14px] backdrop-saturate-[1.7]">
    <span class="nbtw-03__glass absolute inset-x-0 -inset-y-10 z-0 pointer-events-none opacity-60 [background:inherit] [filter:url(#nbtw-03-refract)] motion-reduce:[filter:none]" aria-hidden="true"></span>
    <div class="relative z-[2] flex gap-1">
      <button class="size-[34px] rounded-[10px] border border-white/35 bg-white/[0.16] text-white text-lg leading-none cursor-pointer transition-colors duration-200 hover:bg-white/30 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2" type="button" aria-label="Back">‹</button>
      <button class="size-[34px] rounded-[10px] border border-white/35 bg-white/[0.16] text-white text-lg leading-none cursor-pointer transition-colors duration-200 hover:bg-white/30 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2" type="button" aria-label="Forward">›</button>
    </div>
    <label class="relative z-[2] flex flex-1 items-center gap-2 h-[34px] px-3.5 rounded-[10px] bg-white/90 shadow-[inset_0_1px_3px_rgba(0,0,0,.15)] focus-within:shadow-[0_0_0_3px_rgba(255,255,255,.7),inset_0_1px_3px_rgba(0,0,0,.15)]">
      <span class="text-xs" aria-hidden="true">🔒</span>
      <input class="flex-1 min-w-0 border-0 bg-transparent outline-none text-sm text-[#111]" type="text" value="codefronts.com/liquid-glass" aria-label="Address" spellcheck="false">
    </label>
    <button class="relative z-[2] size-[34px] rounded-[10px] border border-white/35 bg-white/[0.16] text-white text-lg leading-none cursor-pointer transition-colors duration-200 hover:bg-white/30 focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2" type="button" aria-label="Share">⤴</button>
  </header>
  <div class="px-6 py-10 text-white">
    <p class="max-w-[40ch] text-lg font-semibold leading-[1.6] [text-shadow:0_2px_12px_rgba(0,0,0,.35)]">Watch the background bend through the toolbar — that warp is a live SVG displacement map, not a blur.</p>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --font-nb03-sans: -apple-system, system-ui, 'Segoe UI', sans-serif;
}
/* A negated feature query has no variant form, so this is the one rule that
   cannot move into the markup: where filter: url() will not resolve, the
   displaced layer is removed and the plain backdrop-blur tier remains. */

@supports not (filter: url(#nbtw-03-refract)) {
  .nbtw-03__glass {
    display: none;
  }
}
```

How this works

The refraction is not a blur. feTurbulence generates fractal noise; feDisplacementMap uses that noise to move the source pixels around, which is what makes the background bend rather than smear. Both primitives stay in the markup:

<filter id="nbtw-03-refract">
  <feTurbulence type="fractalNoise" baseFrequency="0.008 0.012" numOctaves="2" seed="7" result="n"/>
  <feDisplacementMap in="SourceGraphic" in2="n" scale="16" xChannelSelector="R" yChannelSelector="G"/>
</filter>

The reference is an arbitrary property, and that is the right answer. Tailwind's filter-* scale covers blur, brightness and friends; a url() reference to a namespaced id is not a scale value, so it goes in brackets on the decorative layer: [filter:url(#nbtw-03-refract)] [background:inherit] opacity-60 pointer-events-none.

Never displace live text. The warped layer is an empty <span> that inherits the root background, marked aria-hidden and pointer-events-none. Every control is relative z-[2] above it, so the address bar stays legible while the glass behind it bends.

One rule cannot move. @supports not (filter: url(#nbtw-03-refract)) hides the layer where the filter would not resolve. Tailwind has a supports-[...] variant but no negation of it, so this stays as CSS — the honest cost of the port.

Make it yours

  • Dial refraction strength with scale on feDisplacementMap; 8–20 is the usable band, above which the warp stops reading as glass.
  • Change the lens grain via baseFrequency — lower values give long, watery distortions, higher values give a fine ripple.
  • Soften the effect with opacity-40 on the glass layer instead of reducing scale; it costs less GPU for a similar read.
  • Add a second feGaussianBlur before the displacement for a frostier lens, or drop the layer entirely for the plain backdrop-blur toolbar.
  • Namespace a second filter (nbtw-03-refract-strong) and swap the arbitrary utility if you want per-breakpoint intensity.

Gotchas — read before shipping

  • feDisplacementMap is GPU-heavy. Keep scale modest and the filtered box small — a full-page displaced layer will drop frames on mid-tier hardware.
  • Firefox has historically clamped SVG displacement composited over backdrop-filter. Ship the blur-only tier and treat the warp as a bonus.
  • Filter ids are document-global. Two un-namespaced copies of this demo on one page silently both resolve to the first filter.
  • Never put live text inside the displaced layer — displace a decorative sibling only, or your address bar becomes unreadable.
  • [background:inherit] is doing real work: the layer needs the root's background to have anything to distort. An empty transparent layer displaces nothing.
  • A negated feature query has no variant, so the @supports not rule cannot move into the markup. Do not delete it in the port.

Browser support

ChromeSafariFirefoxEdge
111+16.4+partial (blur tier)111+

Tailwind v4 sets the baseline floor; SVG displacement refraction is the premium tier on top. The @supports not rule drops the warped layer where it would not resolve, leaving every browser a clean frosted toolbar.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…