11 / 24
All 24 in 24 Tailwind Liquid Glass Navbars
- 01Tailwind iOS 26 Floating Tab Bar
- 02Tailwind macOS Tahoe Menu Bar
- 03Tailwind Safari Toolbar with SVG Refraction
- 04Tailwind visionOS Glass Navbar
- 05Tailwind Liquid Glass Bottom Accessory Bar
- 06Tailwind Dynamic Island Morphing Nav
- 07Tailwind Liquid Glass Navbar (Canonical)
- 08Tailwind Responsive Glass Navbar with Burger
- 09Tailwind Next.js Sticky Glass Header
- 10Tailwind Spring Glide Menu (Framer Motion Port)
- 11Tailwind CSS Variables Glass Theme Generator
- 12Tailwind Glass Override for Bootstrap 5 Navbars
- 13Tailwind Scroll-Opacity Sticky Navbar
- 14Tailwind Inner-Glow Glass Nav Links
- 15Tailwind Cursor-Lens Glass Nav (GSAP-ready)
- 16Tailwind Animated Gradient Blobs Glass Navbar
- 17Tailwind Sliding Capsule Tab Bar (group-has)
- 18Tailwind Reflective Glass Sidebar
- 19Tailwind Fallback-First Glass Navbar
- 20Tailwind High-Contrast Accessible Glass Navbar
- 21Tailwind Light / Dark Liquid Glass Navbar
- 22Tailwind Performance-Tuned Glass Navbar
- 23Tailwind Minimalist Dual-Border Glass Header
- 24Tailwind Responsive Glass Sidebar to Bottom Tabs
Tailwind CSS Variables Glass Theme Generator
The demo that draws the most important line in the whole port: @theme is compile-time. Values a user retunes at runtime — blur radius, tint alpha, hue — must stay ordinary custom properties, and utilities read them with backdrop-blur-(--nb11-blur) and bracketed color-mix(). Sliders write the variables; nothing recompiles, nothing re-renders.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="nbtw-11 group relative flex flex-col items-center justify-center gap-5.5 w-full min-h-svh p-6 font-nb11-sans bg-[radial-gradient(circle_at_30%_20%,oklch(0.5_0.22_var(--nb11-hue)),#0a0a12_70%)] transition-[background] duration-400 data-[theme=light]:bg-[radial-gradient(circle_at_30%_20%,oklch(0.9_0.09_var(--nb11-hue)),#e7e7ef_70%)] motion-reduce:transition-none" id="nbtw-11-root" data-theme="dark" style="--nb11-blur:22px;--nb11-alpha:14%;--nb11-hue:265;--nb11-fg:#ffffff;--nb11-accent:oklch(0.66 0.2 265)" aria-label="CSS variables glass theme generator demo">
<header class="flex flex-wrap items-center gap-x-4.5 gap-y-3 w-[min(720px,100%)] px-4 py-3.5 rounded-[18px] text-[var(--nb11-fg)] bg-[color-mix(in_oklab,var(--nb11-fg)_var(--nb11-alpha),transparent)] border border-[color-mix(in_oklab,var(--nb11-fg)_30%,transparent)] shadow-[inset_0_1px_0_color-mix(in_oklab,var(--nb11-fg)_45%,transparent),0_18px_40px_-22px_rgba(0,0,0,.6)] backdrop-blur-(--nb11-blur) backdrop-saturate-[1.8] transition-[background-color,backdrop-filter] duration-300 group-data-[theme=light]:text-[#101018] motion-reduce:transition-none">
<span class="text-base font-extrabold">◈ Themer</span>
<nav class="flex flex-wrap gap-1 ms-auto" aria-label="Primary">
<a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-current opacity-[0.82] text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2 aria-[current=page]:opacity-100 aria-[current=page]:bg-[color-mix(in_oklab,currentColor_18%,transparent)]" href="#nbtw-11" aria-current="page">Home</a>
<a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-current opacity-[0.82] text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2" href="#nbtw-11">Tokens</a>
<a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-current opacity-[0.82] text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2" href="#nbtw-11">Export</a>
</nav>
<button class="min-h-10 px-4.5 rounded-[10px] border-0 bg-[var(--nb11-accent)] text-white text-sm font-bold cursor-pointer focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2" type="button">Save</button>
</header>
<form class="flex flex-wrap items-center gap-x-5.5 gap-y-3.5 w-[min(720px,100%)] p-4 rounded-2xl text-[var(--nb11-fg)] bg-[color-mix(in_oklab,var(--nb11-fg)_8%,transparent)] border border-[color-mix(in_oklab,var(--nb11-fg)_20%,transparent)] backdrop-blur-[12px] group-data-[theme=light]:text-[#101018]" aria-label="Theme controls">
<label class="flex flex-col gap-1.5 text-xs font-semibold">Blur<input class="accent-[var(--nb11-accent)]" type="range" id="nbtw-11-blur" min="4" max="40" value="22"></label>
<label class="flex flex-col gap-1.5 text-xs font-semibold">Tint alpha<input class="accent-[var(--nb11-accent)]" type="range" id="nbtw-11-alpha" min="4" max="40" value="14"></label>
<label class="flex flex-col gap-1.5 text-xs font-semibold">Hue<input class="accent-[var(--nb11-accent)]" type="range" id="nbtw-11-hue" min="0" max="360" value="265"></label>
<fieldset class="flex items-center gap-3.5 border-0 text-[13px]">
<legend class="float-none mr-1 text-xs font-bold">Mode</legend>
<label class="flex items-center gap-1.5 cursor-pointer"><input class="accent-[var(--nb11-accent)]" type="radio" name="nbtw-11-mode" value="dark" checked> Dark</label>
<label class="flex items-center gap-1.5 cursor-pointer"><input class="accent-[var(--nb11-accent)]" type="radio" name="nbtw-11-mode" value="light"> Light</label>
</fieldset>
</form>
</section><section class="nbtw-11 group relative flex flex-col items-center justify-center gap-5.5 w-full min-h-svh p-6 font-nb11-sans bg-[radial-gradient(circle_at_30%_20%,oklch(0.5_0.22_var(--nb11-hue)),#0a0a12_70%)] transition-[background] duration-400 data-[theme=light]:bg-[radial-gradient(circle_at_30%_20%,oklch(0.9_0.09_var(--nb11-hue)),#e7e7ef_70%)] motion-reduce:transition-none" id="nbtw-11-root" data-theme="dark" style="--nb11-blur:22px;--nb11-alpha:14%;--nb11-hue:265;--nb11-fg:#ffffff;--nb11-accent:oklch(0.66 0.2 265)" aria-label="CSS variables glass theme generator demo">
<header class="flex flex-wrap items-center gap-x-4.5 gap-y-3 w-[min(720px,100%)] px-4 py-3.5 rounded-[18px] text-[var(--nb11-fg)] bg-[color-mix(in_oklab,var(--nb11-fg)_var(--nb11-alpha),transparent)] border border-[color-mix(in_oklab,var(--nb11-fg)_30%,transparent)] shadow-[inset_0_1px_0_color-mix(in_oklab,var(--nb11-fg)_45%,transparent),0_18px_40px_-22px_rgba(0,0,0,.6)] backdrop-blur-(--nb11-blur) backdrop-saturate-[1.8] transition-[background-color,backdrop-filter] duration-300 group-data-[theme=light]:text-[#101018] motion-reduce:transition-none">
<span class="text-base font-extrabold">◈ Themer</span>
<nav class="flex flex-wrap gap-1 ms-auto" aria-label="Primary">
<a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-current opacity-[0.82] text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2 aria-[current=page]:opacity-100 aria-[current=page]:bg-[color-mix(in_oklab,currentColor_18%,transparent)]" href="#nbtw-11" aria-current="page">Home</a>
<a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-current opacity-[0.82] text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2" href="#nbtw-11">Tokens</a>
<a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-current opacity-[0.82] text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2" href="#nbtw-11">Export</a>
</nav>
<button class="min-h-10 px-4.5 rounded-[10px] border-0 bg-[var(--nb11-accent)] text-white text-sm font-bold cursor-pointer focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2" type="button">Save</button>
</header>
<form class="flex flex-wrap items-center gap-x-5.5 gap-y-3.5 w-[min(720px,100%)] p-4 rounded-2xl text-[var(--nb11-fg)] bg-[color-mix(in_oklab,var(--nb11-fg)_8%,transparent)] border border-[color-mix(in_oklab,var(--nb11-fg)_20%,transparent)] backdrop-blur-[12px] group-data-[theme=light]:text-[#101018]" aria-label="Theme controls">
<label class="flex flex-col gap-1.5 text-xs font-semibold">Blur<input class="accent-[var(--nb11-accent)]" type="range" id="nbtw-11-blur" min="4" max="40" value="22"></label>
<label class="flex flex-col gap-1.5 text-xs font-semibold">Tint alpha<input class="accent-[var(--nb11-accent)]" type="range" id="nbtw-11-alpha" min="4" max="40" value="14"></label>
<label class="flex flex-col gap-1.5 text-xs font-semibold">Hue<input class="accent-[var(--nb11-accent)]" type="range" id="nbtw-11-hue" min="0" max="360" value="265"></label>
<fieldset class="flex items-center gap-3.5 border-0 text-[13px]">
<legend class="float-none mr-1 text-xs font-bold">Mode</legend>
<label class="flex items-center gap-1.5 cursor-pointer"><input class="accent-[var(--nb11-accent)]" type="radio" name="nbtw-11-mode" value="dark" checked> Dark</label>
<label class="flex items-center gap-1.5 cursor-pointer"><input class="accent-[var(--nb11-accent)]" type="radio" name="nbtw-11-mode" value="light"> Light</label>
</fieldset>
</form>
</section>@import "tailwindcss";
@theme {
--font-nb11-sans: system-ui, 'Segoe UI', sans-serif;
}
/* Registered so the browser can interpolate them while a slider is dragged.
An unregistered custom property is an untyped string: it still resolves,
but nothing tweens and the drag feels stepped. */
@property --nb11-hue {
syntax: '<number>';
inherits: true;
initial-value: 265;
}
@property --nb11-alpha {
syntax: '<percentage>';
inherits: true;
initial-value: 14%;
}
/* The light token set. These are runtime values, so they cannot live in
@theme — a slider has to be able to rewrite them. */
.nbtw-11[data-theme="light"] {
--nb11-fg: #101018;
--nb11-accent: oklch(0.55 0.2 var(--nb11-hue));
}@import "tailwindcss";
@theme {
--font-nb11-sans: system-ui, 'Segoe UI', sans-serif;
}
/* Registered so the browser can interpolate them while a slider is dragged.
An unregistered custom property is an untyped string: it still resolves,
but nothing tweens and the drag feels stepped. */
@property --nb11-hue {
syntax: '<number>';
inherits: true;
initial-value: 265;
}
@property --nb11-alpha {
syntax: '<percentage>';
inherits: true;
initial-value: 14%;
}
/* The light token set. These are runtime values, so they cannot live in
@theme — a slider has to be able to rewrite them. */
.nbtw-11[data-theme="light"] {
--nb11-fg: #101018;
--nb11-accent: oklch(0.55 0.2 var(--nb11-hue));
}(() => {
const root = document.querySelector('#nbtw-11-root');
if (!root) return;
const set = (k, v) => root.style.setProperty(k, v);
root.querySelector('#nbtw-11-blur').addEventListener('input', (e) => set('--nb11-blur', e.target.value + 'px'));
root.querySelector('#nbtw-11-alpha').addEventListener('input', (e) => set('--nb11-alpha', e.target.value + '%'));
root.querySelector('#nbtw-11-hue').addEventListener('input', (e) => {
set('--nb11-hue', e.target.value);
set('--nb11-accent', 'oklch(0.66 0.2 ' + e.target.value + ')');
});
root.querySelectorAll('input[name="nbtw-11-mode"]').forEach((r) =>
r.addEventListener('change', (e) => { root.dataset.theme = e.target.value; }));
})();(() => {
const root = document.querySelector('#nbtw-11-root');
if (!root) return;
const set = (k, v) => root.style.setProperty(k, v);
root.querySelector('#nbtw-11-blur').addEventListener('input', (e) => set('--nb11-blur', e.target.value + 'px'));
root.querySelector('#nbtw-11-alpha').addEventListener('input', (e) => set('--nb11-alpha', e.target.value + '%'));
root.querySelector('#nbtw-11-hue').addEventListener('input', (e) => {
set('--nb11-hue', e.target.value);
set('--nb11-accent', 'oklch(0.66 0.2 ' + e.target.value + ')');
});
root.querySelectorAll('input[name="nbtw-11-mode"]').forEach((r) =>
r.addEventListener('change', (e) => { root.dataset.theme = e.target.value; }));
})();Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<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.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--font-nb11-sans: system-ui, 'Segoe UI', sans-serif;
}
/* Registered so the browser can interpolate them while a slider is dragged.
An unregistered custom property is an untyped string: it still resolves,
but nothing tweens and the drag feels stepped. */
@property --nb11-hue {
syntax: '<number>';
inherits: true;
initial-value: 265;
}
@property --nb11-alpha {
syntax: '<percentage>';
inherits: true;
initial-value: 14%;
}
/* The light token set. These are runtime values, so they cannot live in
@theme — a slider has to be able to rewrite them. */
.nbtw-11[data-theme="light"] {
--nb11-fg: #101018;
--nb11-accent: oklch(0.55 0.2 var(--nb11-hue));
}@import "tailwindcss";
@theme {
--font-nb11-sans: system-ui, 'Segoe UI', sans-serif;
}
/* Registered so the browser can interpolate them while a slider is dragged.
An unregistered custom property is an untyped string: it still resolves,
but nothing tweens and the drag feels stepped. */
@property --nb11-hue {
syntax: '<number>';
inherits: true;
initial-value: 265;
}
@property --nb11-alpha {
syntax: '<percentage>';
inherits: true;
initial-value: 14%;
}
/* The light token set. These are runtime values, so they cannot live in
@theme — a slider has to be able to rewrite them. */
.nbtw-11[data-theme="light"] {
--nb11-fg: #101018;
--nb11-accent: oklch(0.55 0.2 var(--nb11-hue));
}What changed in the port
Which parts became utilities, which stayed CSS, and why.
- This is the entry that marks the boundary:
@themeis compile-time, so anything a user retunes at runtime stays an ordinary custom property that utilities read. backdrop-blur-(--nb11-blur)is v4's variable shorthand and the cleanest way to point a utility at a live value; on v3 it isbackdrop-blur-[var(--nb11-blur)].- The fill is a bracketed
color-mix()so tint alpha is a slider rather than a rebuild — note the underscores standing in for its spaces. - Both animated properties are registered with
@property. Without the registration the values apply but never tween, and dragging a slider feels stepped. - Light mode is
data-themeplusgroup-data-[theme=light]:variants over the same tokens, rather than a duplicated set of rules. - Native
rangeandradioinputs keep the generator keyboard-operable, withaccent-[var(--nb11-accent)]tying them to the live theme.
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 CSS Variables Glass Theme Generator
Source: https://codefronts.com/navigation/tailwind-liquid-glass-navbars/tailwind-css-variables-glass-theme-generator/
The demo that draws the most important line in the whole port: @theme is compile-time. Values a user retunes at runtime — blur radius, tint alpha, hue — must stay ordinary custom properties, and utilities read them with backdrop-blur-(--nb11-blur) and bracketed color-mix(). Sliders write the variables; nothing recompiles, nothing re-renders.
## HTML
```html
<section class="nbtw-11 group relative flex flex-col items-center justify-center gap-5.5 w-full min-h-svh p-6 font-nb11-sans bg-[radial-gradient(circle_at_30%_20%,oklch(0.5_0.22_var(--nb11-hue)),#0a0a12_70%)] transition-[background] duration-400 data-[theme=light]:bg-[radial-gradient(circle_at_30%_20%,oklch(0.9_0.09_var(--nb11-hue)),#e7e7ef_70%)] motion-reduce:transition-none" id="nbtw-11-root" data-theme="dark" style="--nb11-blur:22px;--nb11-alpha:14%;--nb11-hue:265;--nb11-fg:#ffffff;--nb11-accent:oklch(0.66 0.2 265)" aria-label="CSS variables glass theme generator demo">
<header class="flex flex-wrap items-center gap-x-4.5 gap-y-3 w-[min(720px,100%)] px-4 py-3.5 rounded-[18px] text-[var(--nb11-fg)] bg-[color-mix(in_oklab,var(--nb11-fg)_var(--nb11-alpha),transparent)] border border-[color-mix(in_oklab,var(--nb11-fg)_30%,transparent)] shadow-[inset_0_1px_0_color-mix(in_oklab,var(--nb11-fg)_45%,transparent),0_18px_40px_-22px_rgba(0,0,0,.6)] backdrop-blur-(--nb11-blur) backdrop-saturate-[1.8] transition-[background-color,backdrop-filter] duration-300 group-data-[theme=light]:text-[#101018] motion-reduce:transition-none">
<span class="text-base font-extrabold">◈ Themer</span>
<nav class="flex flex-wrap gap-1 ms-auto" aria-label="Primary">
<a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-current opacity-[0.82] text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2 aria-[current=page]:opacity-100 aria-[current=page]:bg-[color-mix(in_oklab,currentColor_18%,transparent)]" href="#nbtw-11" aria-current="page">Home</a>
<a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-current opacity-[0.82] text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2" href="#nbtw-11">Tokens</a>
<a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-current opacity-[0.82] text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2" href="#nbtw-11">Export</a>
</nav>
<button class="min-h-10 px-4.5 rounded-[10px] border-0 bg-[var(--nb11-accent)] text-white text-sm font-bold cursor-pointer focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2" type="button">Save</button>
</header>
<form class="flex flex-wrap items-center gap-x-5.5 gap-y-3.5 w-[min(720px,100%)] p-4 rounded-2xl text-[var(--nb11-fg)] bg-[color-mix(in_oklab,var(--nb11-fg)_8%,transparent)] border border-[color-mix(in_oklab,var(--nb11-fg)_20%,transparent)] backdrop-blur-[12px] group-data-[theme=light]:text-[#101018]" aria-label="Theme controls">
<label class="flex flex-col gap-1.5 text-xs font-semibold">Blur<input class="accent-[var(--nb11-accent)]" type="range" id="nbtw-11-blur" min="4" max="40" value="22"></label>
<label class="flex flex-col gap-1.5 text-xs font-semibold">Tint alpha<input class="accent-[var(--nb11-accent)]" type="range" id="nbtw-11-alpha" min="4" max="40" value="14"></label>
<label class="flex flex-col gap-1.5 text-xs font-semibold">Hue<input class="accent-[var(--nb11-accent)]" type="range" id="nbtw-11-hue" min="0" max="360" value="265"></label>
<fieldset class="flex items-center gap-3.5 border-0 text-[13px]">
<legend class="float-none mr-1 text-xs font-bold">Mode</legend>
<label class="flex items-center gap-1.5 cursor-pointer"><input class="accent-[var(--nb11-accent)]" type="radio" name="nbtw-11-mode" value="dark" checked> Dark</label>
<label class="flex items-center gap-1.5 cursor-pointer"><input class="accent-[var(--nb11-accent)]" type="radio" name="nbtw-11-mode" value="light"> Light</label>
</fieldset>
</form>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--font-nb11-sans: system-ui, 'Segoe UI', sans-serif;
}
/* Registered so the browser can interpolate them while a slider is dragged.
An unregistered custom property is an untyped string: it still resolves,
but nothing tweens and the drag feels stepped. */
@property --nb11-hue {
syntax: '<number>';
inherits: true;
initial-value: 265;
}
@property --nb11-alpha {
syntax: '<percentage>';
inherits: true;
initial-value: 14%;
}
/* The light token set. These are runtime values, so they cannot live in
@theme — a slider has to be able to rewrite them. */
.nbtw-11[data-theme="light"] {
--nb11-fg: #101018;
--nb11-accent: oklch(0.55 0.2 var(--nb11-hue));
}
```
## JavaScript
```js
(() => {
const root = document.querySelector('#nbtw-11-root');
if (!root) return;
const set = (k, v) => root.style.setProperty(k, v);
root.querySelector('#nbtw-11-blur').addEventListener('input', (e) => set('--nb11-blur', e.target.value + 'px'));
root.querySelector('#nbtw-11-alpha').addEventListener('input', (e) => set('--nb11-alpha', e.target.value + '%'));
root.querySelector('#nbtw-11-hue').addEventListener('input', (e) => {
set('--nb11-hue', e.target.value);
set('--nb11-accent', 'oklch(0.66 0.2 ' + e.target.value + ')');
});
root.querySelectorAll('input[name="nbtw-11-mode"]').forEach((r) =>
r.addEventListener('change', (e) => { root.dataset.theme = e.target.value; }));
})();
```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 CSS Variables Glass Theme Generator
Source: https://codefronts.com/navigation/tailwind-liquid-glass-navbars/tailwind-css-variables-glass-theme-generator/
The demo that draws the most important line in the whole port: @theme is compile-time. Values a user retunes at runtime — blur radius, tint alpha, hue — must stay ordinary custom properties, and utilities read them with backdrop-blur-(--nb11-blur) and bracketed color-mix(). Sliders write the variables; nothing recompiles, nothing re-renders.
## HTML
```html
<section class="nbtw-11 group relative flex flex-col items-center justify-center gap-5.5 w-full min-h-svh p-6 font-nb11-sans bg-[radial-gradient(circle_at_30%_20%,oklch(0.5_0.22_var(--nb11-hue)),#0a0a12_70%)] transition-[background] duration-400 data-[theme=light]:bg-[radial-gradient(circle_at_30%_20%,oklch(0.9_0.09_var(--nb11-hue)),#e7e7ef_70%)] motion-reduce:transition-none" id="nbtw-11-root" data-theme="dark" style="--nb11-blur:22px;--nb11-alpha:14%;--nb11-hue:265;--nb11-fg:#ffffff;--nb11-accent:oklch(0.66 0.2 265)" aria-label="CSS variables glass theme generator demo">
<header class="flex flex-wrap items-center gap-x-4.5 gap-y-3 w-[min(720px,100%)] px-4 py-3.5 rounded-[18px] text-[var(--nb11-fg)] bg-[color-mix(in_oklab,var(--nb11-fg)_var(--nb11-alpha),transparent)] border border-[color-mix(in_oklab,var(--nb11-fg)_30%,transparent)] shadow-[inset_0_1px_0_color-mix(in_oklab,var(--nb11-fg)_45%,transparent),0_18px_40px_-22px_rgba(0,0,0,.6)] backdrop-blur-(--nb11-blur) backdrop-saturate-[1.8] transition-[background-color,backdrop-filter] duration-300 group-data-[theme=light]:text-[#101018] motion-reduce:transition-none">
<span class="text-base font-extrabold">◈ Themer</span>
<nav class="flex flex-wrap gap-1 ms-auto" aria-label="Primary">
<a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-current opacity-[0.82] text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2 aria-[current=page]:opacity-100 aria-[current=page]:bg-[color-mix(in_oklab,currentColor_18%,transparent)]" href="#nbtw-11" aria-current="page">Home</a>
<a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-current opacity-[0.82] text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2" href="#nbtw-11">Tokens</a>
<a class="flex items-center min-h-10 px-3.5 rounded-[10px] no-underline text-current opacity-[0.82] text-[14.5px] font-semibold transition-[background-color,opacity] duration-200 hover:opacity-100 focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2" href="#nbtw-11">Export</a>
</nav>
<button class="min-h-10 px-4.5 rounded-[10px] border-0 bg-[var(--nb11-accent)] text-white text-sm font-bold cursor-pointer focus-visible:outline-2 focus-visible:outline-[var(--nb11-accent)] focus-visible:outline-offset-2" type="button">Save</button>
</header>
<form class="flex flex-wrap items-center gap-x-5.5 gap-y-3.5 w-[min(720px,100%)] p-4 rounded-2xl text-[var(--nb11-fg)] bg-[color-mix(in_oklab,var(--nb11-fg)_8%,transparent)] border border-[color-mix(in_oklab,var(--nb11-fg)_20%,transparent)] backdrop-blur-[12px] group-data-[theme=light]:text-[#101018]" aria-label="Theme controls">
<label class="flex flex-col gap-1.5 text-xs font-semibold">Blur<input class="accent-[var(--nb11-accent)]" type="range" id="nbtw-11-blur" min="4" max="40" value="22"></label>
<label class="flex flex-col gap-1.5 text-xs font-semibold">Tint alpha<input class="accent-[var(--nb11-accent)]" type="range" id="nbtw-11-alpha" min="4" max="40" value="14"></label>
<label class="flex flex-col gap-1.5 text-xs font-semibold">Hue<input class="accent-[var(--nb11-accent)]" type="range" id="nbtw-11-hue" min="0" max="360" value="265"></label>
<fieldset class="flex items-center gap-3.5 border-0 text-[13px]">
<legend class="float-none mr-1 text-xs font-bold">Mode</legend>
<label class="flex items-center gap-1.5 cursor-pointer"><input class="accent-[var(--nb11-accent)]" type="radio" name="nbtw-11-mode" value="dark" checked> Dark</label>
<label class="flex items-center gap-1.5 cursor-pointer"><input class="accent-[var(--nb11-accent)]" type="radio" name="nbtw-11-mode" value="light"> Light</label>
</fieldset>
</form>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--font-nb11-sans: system-ui, 'Segoe UI', sans-serif;
}
/* Registered so the browser can interpolate them while a slider is dragged.
An unregistered custom property is an untyped string: it still resolves,
but nothing tweens and the drag feels stepped. */
@property --nb11-hue {
syntax: '<number>';
inherits: true;
initial-value: 265;
}
@property --nb11-alpha {
syntax: '<percentage>';
inherits: true;
initial-value: 14%;
}
/* The light token set. These are runtime values, so they cannot live in
@theme — a slider has to be able to rewrite them. */
.nbtw-11[data-theme="light"] {
--nb11-fg: #101018;
--nb11-accent: oklch(0.55 0.2 var(--nb11-hue));
}
```
## JavaScript
```js
(() => {
const root = document.querySelector('#nbtw-11-root');
if (!root) return;
const set = (k, v) => root.style.setProperty(k, v);
root.querySelector('#nbtw-11-blur').addEventListener('input', (e) => set('--nb11-blur', e.target.value + 'px'));
root.querySelector('#nbtw-11-alpha').addEventListener('input', (e) => set('--nb11-alpha', e.target.value + '%'));
root.querySelector('#nbtw-11-hue').addEventListener('input', (e) => {
set('--nb11-hue', e.target.value);
set('--nb11-accent', 'oklch(0.66 0.2 ' + e.target.value + ')');
});
root.querySelectorAll('input[name="nbtw-11-mode"]').forEach((r) =>
r.addEventListener('change', (e) => { root.dataset.theme = e.target.value; }));
})();
```How this works
Compile-time versus runtime is the whole lesson. A @theme entry mints utilities when Tailwind builds; it cannot be rewritten by a slider. So the retunable values are ordinary properties, and the utilities reference them:
/* root: --nb11-blur: 22px; --nb11-alpha: 14%; --nb11-hue: 265; */
class="backdrop-blur-(--nb11-blur)
bg-[color-mix(in_oklab,var(--nb11-fg)_var(--nb11-alpha),transparent)]"The handler is four setProperty calls. Each slider writes one variable on the root; every utility that reads it updates on the next frame. There is no class swap, no re-render, and no compiled state to invalidate — which is exactly why a "theme generator" is easier in v4 than it sounds.
Registration is what makes it smooth. @property --nb11-hue { syntax: '<number>' } and its alpha twin let the browser interpolate as you drag. Unregistered, both are untyped strings: color-mix() still resolves, but nothing tweens and dragging feels like stepping.
Light mode is a variant, not a fork. data-theme flips on the root and group-data-[theme=light]: retunes the same tokens, so there is one design with two token sets rather than two designs.
Make it yours
- Add a token (radius, saturation) by declaring one more custom property and one more slider — every utility that should follow it references it by name.
- Persist choices to
localStorageand re-apply on load; because the state is four variables, the whole persisted payload is four strings. - Export the computed block as copy-paste theme code — read the four properties off the root and print them as a
:rootrule. - Promote a value to
@themethe moment it stops being user-tunable; that is the signal for moving it from variable to token. - Widen the hue range or clamp it to your brand arc if a full 360° sweep can produce combinations that fail contrast.
Gotchas — read before shipping
- Do not try to make
@themeentries live. They are compile-time; a slider writing--color-*in@themechanges nothing about already-generated utilities. - Register animatable custom properties with
@propertyor dragging feels stepped rather than smooth. backdrop-blur-(--x)is v4-only syntax. On v3 writebackdrop-blur-[var(--x)].color-mix()inside an arbitrary value needs underscores for its spaces, and the percentage must come from a variable that actually carries a unit (14%, not14).- Keep contrast valid across the whole slider range, not just the default position — a generator that can produce an illegible bar is a bug, not a feature.
- The accent colour on native inputs needs
accent-[var(--nb11-accent)]; the standardaccent-*utilities take scale colours, not live variables.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Tailwind v4 sets the floor and color-mix() is inside it. @property (Chrome 85+, Safari 16.4+, Firefox 128+) is what makes the hue and alpha interpolate; without it the values still apply, they just snap.