15 / 20
All 20 in 20 Tailwind Liquid Glass Effects
- 01Tailwind Liquid Glass Card with Hover Refraction
- 02Tailwind Liquid Glass Button with Glossy Highlight
- 03Tailwind Liquid Glass Navigation Bar
- 04Tailwind Liquid Gooey Toggle Switch
- 05Tailwind Liquid Glass Modal Dialog
- 06Tailwind Liquid Glass Theme Switcher
- 07Tailwind Liquid Glass Input Form Fields
- 08Tailwind SVG Displacement Liquid Glass Background
- 09Tailwind Liquid Glass Dropdown Menu
- 10Tailwind Liquid Glass Pricing Table
- 11Tailwind Liquid Glass Tooltip Popover
- 12Tailwind Liquid Glass Audio Player UI
- 13Tailwind Liquid Glass Range Slider
- 14Tailwind Liquid Glass Toast Notification
- 15Tailwind Liquid Glass Sidebar Navigation
- 16Tailwind Liquid Glass Accordion
- 17Tailwind Liquid Glass Login Card
- 18Tailwind Liquid Glass Floating Action Button
- 19Tailwind Liquid Glass Loading Skeleton
- 20Tailwind Liquid Glass Multi-Step Form Wizard
Tailwind Liquid Glass Sidebar Navigation
A collapsible dashboard shell that re-tints and re-sizes itself from two checkboxes and zero JavaScript. This is the demo where v4's token model pays off hardest: has-[#lgt-15-theme:checked]: re-points the palette variables, and every bg-lgt15-surface in the markup changes meaning at once.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="lgt-15 relative flex w-full min-h-svh overflow-hidden bg-lgt15-bg font-lgt15-sans" aria-label="Tailwind liquid glass sidebar navigation">
<div aria-hidden="true" class="pointer-events-none absolute inset-0">
<span class="absolute -top-10 left-30 size-70 animate-lgt15-float rounded-full bg-lgt15-blob1 opacity-70 blur-[40px] motion-reduce:animate-none"></span>
<span class="absolute -bottom-8 left-10 size-60 animate-lgt15-float rounded-full bg-lgt15-blob2 opacity-70 blur-[40px] [animation-delay:-4s] motion-reduce:animate-none"></span>
<span class="absolute top-2/5 -right-10 size-75 animate-lgt15-float rounded-full bg-lgt15-blob3 opacity-70 blur-[40px] [animation-delay:-8s] motion-reduce:animate-none"></span>
</div>
<input type="checkbox" id="lgt-15-collapse" class="sr-only">
<input type="checkbox" id="lgt-15-theme" class="sr-only">
<aside class="relative z-2 m-4 flex w-(--w) flex-none flex-col gap-2 rounded-[22px] border border-lgt15-rim bg-lgt15-surface p-3.5 pt-4.5 backdrop-blur-[24px] backdrop-saturate-180 shadow-[inset_0_1px_0_var(--color-lgt15-rim),0_30px_60px_-30px_rgba(0,0,0,.4)] transition-[width] duration-350 ease-out motion-reduce:transition-none">
<div class="flex items-center gap-3 px-2.5 pt-2 pb-3.5 text-lgt15-fg">
<span class="flex-none text-[22px] text-lgt15-active">◆</span>
<span class="lgt-15-label overflow-hidden text-[19px] font-bold whitespace-nowrap transition-opacity duration-200">Nimbus</span>
</div>
<nav class="flex flex-1 flex-col gap-1" aria-label="Dashboard">
<a href="#lgt-15" aria-current="page" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active aria-[current=page]:bg-lgt15-active aria-[current=page]:text-white aria-[current=page]:shadow-[inset_0_1px_0_rgba(255,255,255,.4)]"><span class="w-5 flex-none text-center text-[17px]">▤</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Overview</span></a>
<a href="#lgt-15" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active"><span class="w-5 flex-none text-center text-[17px]">◫</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Projects</span></a>
<a href="#lgt-15" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active"><span class="w-5 flex-none text-center text-[17px]">☰</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Tasks</span></a>
<a href="#lgt-15" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active"><span class="w-5 flex-none text-center text-[17px]">◍</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Reports</span></a>
</nav>
<div class="flex flex-col gap-1 border-t border-lgt15-rim pt-2.5">
<label for="lgt-15-theme" class="flex cursor-pointer items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg"><span class="w-5 flex-none text-center text-[17px]">◐</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Dark mode</span></label>
<label for="lgt-15-collapse" class="flex cursor-pointer items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg"><span class="w-5 flex-none text-center text-[17px]">⇔</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Collapse</span></label>
</div>
</aside>
<div class="relative z-2 flex-1 px-10 py-15 text-lgt15-fg">
<h3 class="mb-2.5 text-3xl font-extrabold">Dashboard</h3>
<p class="max-w-[44ch] text-base leading-relaxed text-lgt15-muted">Toggle dark mode or collapse — the shell re-points its theme variables through CSS <code>:has()</code>, so every utility changes meaning at once. No JavaScript.</p>
</div>
</section><section class="lgt-15 relative flex w-full min-h-svh overflow-hidden bg-lgt15-bg font-lgt15-sans" aria-label="Tailwind liquid glass sidebar navigation">
<div aria-hidden="true" class="pointer-events-none absolute inset-0">
<span class="absolute -top-10 left-30 size-70 animate-lgt15-float rounded-full bg-lgt15-blob1 opacity-70 blur-[40px] motion-reduce:animate-none"></span>
<span class="absolute -bottom-8 left-10 size-60 animate-lgt15-float rounded-full bg-lgt15-blob2 opacity-70 blur-[40px] [animation-delay:-4s] motion-reduce:animate-none"></span>
<span class="absolute top-2/5 -right-10 size-75 animate-lgt15-float rounded-full bg-lgt15-blob3 opacity-70 blur-[40px] [animation-delay:-8s] motion-reduce:animate-none"></span>
</div>
<input type="checkbox" id="lgt-15-collapse" class="sr-only">
<input type="checkbox" id="lgt-15-theme" class="sr-only">
<aside class="relative z-2 m-4 flex w-(--w) flex-none flex-col gap-2 rounded-[22px] border border-lgt15-rim bg-lgt15-surface p-3.5 pt-4.5 backdrop-blur-[24px] backdrop-saturate-180 shadow-[inset_0_1px_0_var(--color-lgt15-rim),0_30px_60px_-30px_rgba(0,0,0,.4)] transition-[width] duration-350 ease-out motion-reduce:transition-none">
<div class="flex items-center gap-3 px-2.5 pt-2 pb-3.5 text-lgt15-fg">
<span class="flex-none text-[22px] text-lgt15-active">◆</span>
<span class="lgt-15-label overflow-hidden text-[19px] font-bold whitespace-nowrap transition-opacity duration-200">Nimbus</span>
</div>
<nav class="flex flex-1 flex-col gap-1" aria-label="Dashboard">
<a href="#lgt-15" aria-current="page" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active aria-[current=page]:bg-lgt15-active aria-[current=page]:text-white aria-[current=page]:shadow-[inset_0_1px_0_rgba(255,255,255,.4)]"><span class="w-5 flex-none text-center text-[17px]">▤</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Overview</span></a>
<a href="#lgt-15" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active"><span class="w-5 flex-none text-center text-[17px]">◫</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Projects</span></a>
<a href="#lgt-15" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active"><span class="w-5 flex-none text-center text-[17px]">☰</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Tasks</span></a>
<a href="#lgt-15" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active"><span class="w-5 flex-none text-center text-[17px]">◍</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Reports</span></a>
</nav>
<div class="flex flex-col gap-1 border-t border-lgt15-rim pt-2.5">
<label for="lgt-15-theme" class="flex cursor-pointer items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg"><span class="w-5 flex-none text-center text-[17px]">◐</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Dark mode</span></label>
<label for="lgt-15-collapse" class="flex cursor-pointer items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg"><span class="w-5 flex-none text-center text-[17px]">⇔</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Collapse</span></label>
</div>
</aside>
<div class="relative z-2 flex-1 px-10 py-15 text-lgt15-fg">
<h3 class="mb-2.5 text-3xl font-extrabold">Dashboard</h3>
<p class="max-w-[44ch] text-base leading-relaxed text-lgt15-muted">Toggle dark mode or collapse — the shell re-points its theme variables through CSS <code>:has()</code>, so every utility changes meaning at once. No JavaScript.</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-lgt15-bg: #dfe6f5;
--color-lgt15-surface: rgb(255 255 255 / 0.55);
--color-lgt15-fg: #1a1730;
--color-lgt15-muted: rgb(26 23 48 / 0.66);
--color-lgt15-rim: rgb(255 255 255 / 0.8);
--color-lgt15-active: #7c5cf0;
--color-lgt15-active: oklch(0.66 0.18 285);
--color-lgt15-blob1: oklch(0.7 0.2 300);
--color-lgt15-blob2: oklch(0.72 0.18 200);
--color-lgt15-blob3: oklch(0.72 0.17 40);
--font-lgt15-sans: 'Manrope', system-ui, sans-serif;
--animate-lgt15-float: lgt-15-float 14s ease-in-out infinite;
}
.lgt-15 {
--w: 230px;
}
/* A descendant checkbox restyling its ancestor: the whole shell, in two rules. */
.lgt-15:has(#lgt-15-theme:checked) {
--color-lgt15-bg: #0e0b1a;
--color-lgt15-surface: rgb(255 255 255 / 0.10);
--color-lgt15-fg: #f2f0fa;
--color-lgt15-muted: rgb(255 255 255 / 0.7);
--color-lgt15-rim: rgb(255 255 255 / 0.3);
}
.lgt-15:has(#lgt-15-collapse:checked) {
--w: 78px;
}
.lgt-15:has(#lgt-15-collapse:checked) .lgt-15-label {
opacity: 0;
}
@keyframes lgt-15-float {
0%,
100% {
transform: translate(0, 0);
}
50% {
transform: translate(24px, -20px);
}
}@import "tailwindcss";
@theme {
--color-lgt15-bg: #dfe6f5;
--color-lgt15-surface: rgb(255 255 255 / 0.55);
--color-lgt15-fg: #1a1730;
--color-lgt15-muted: rgb(26 23 48 / 0.66);
--color-lgt15-rim: rgb(255 255 255 / 0.8);
--color-lgt15-active: #7c5cf0;
--color-lgt15-active: oklch(0.66 0.18 285);
--color-lgt15-blob1: oklch(0.7 0.2 300);
--color-lgt15-blob2: oklch(0.72 0.18 200);
--color-lgt15-blob3: oklch(0.72 0.17 40);
--font-lgt15-sans: 'Manrope', system-ui, sans-serif;
--animate-lgt15-float: lgt-15-float 14s ease-in-out infinite;
}
.lgt-15 {
--w: 230px;
}
/* A descendant checkbox restyling its ancestor: the whole shell, in two rules. */
.lgt-15:has(#lgt-15-theme:checked) {
--color-lgt15-bg: #0e0b1a;
--color-lgt15-surface: rgb(255 255 255 / 0.10);
--color-lgt15-fg: #f2f0fa;
--color-lgt15-muted: rgb(255 255 255 / 0.7);
--color-lgt15-rim: rgb(255 255 255 / 0.3);
}
.lgt-15:has(#lgt-15-collapse:checked) {
--w: 78px;
}
.lgt-15:has(#lgt-15-collapse:checked) .lgt-15-label {
opacity: 0;
}
@keyframes lgt-15-float {
0%,
100% {
transform: translate(0, 0);
}
50% {
transform: translate(24px, -20px);
}
}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 {
--color-lgt15-bg: #dfe6f5;
--color-lgt15-surface: rgb(255 255 255 / 0.55);
--color-lgt15-fg: #1a1730;
--color-lgt15-muted: rgb(26 23 48 / 0.66);
--color-lgt15-rim: rgb(255 255 255 / 0.8);
--color-lgt15-active: #7c5cf0;
--color-lgt15-active: oklch(0.66 0.18 285);
--color-lgt15-blob1: oklch(0.7 0.2 300);
--color-lgt15-blob2: oklch(0.72 0.18 200);
--color-lgt15-blob3: oklch(0.72 0.17 40);
--font-lgt15-sans: 'Manrope', system-ui, sans-serif;
--animate-lgt15-float: lgt-15-float 14s ease-in-out infinite;
}
.lgt-15 {
--w: 230px;
}
/* A descendant checkbox restyling its ancestor: the whole shell, in two rules. */
.lgt-15:has(#lgt-15-theme:checked) {
--color-lgt15-bg: #0e0b1a;
--color-lgt15-surface: rgb(255 255 255 / 0.10);
--color-lgt15-fg: #f2f0fa;
--color-lgt15-muted: rgb(255 255 255 / 0.7);
--color-lgt15-rim: rgb(255 255 255 / 0.3);
}
.lgt-15:has(#lgt-15-collapse:checked) {
--w: 78px;
}
.lgt-15:has(#lgt-15-collapse:checked) .lgt-15-label {
opacity: 0;
}
@keyframes lgt-15-float {
0%,
100% {
transform: translate(0, 0);
}
50% {
transform: translate(24px, -20px);
}
}@import "tailwindcss";
@theme {
--color-lgt15-bg: #dfe6f5;
--color-lgt15-surface: rgb(255 255 255 / 0.55);
--color-lgt15-fg: #1a1730;
--color-lgt15-muted: rgb(26 23 48 / 0.66);
--color-lgt15-rim: rgb(255 255 255 / 0.8);
--color-lgt15-active: #7c5cf0;
--color-lgt15-active: oklch(0.66 0.18 285);
--color-lgt15-blob1: oklch(0.7 0.2 300);
--color-lgt15-blob2: oklch(0.72 0.18 200);
--color-lgt15-blob3: oklch(0.72 0.17 40);
--font-lgt15-sans: 'Manrope', system-ui, sans-serif;
--animate-lgt15-float: lgt-15-float 14s ease-in-out infinite;
}
.lgt-15 {
--w: 230px;
}
/* A descendant checkbox restyling its ancestor: the whole shell, in two rules. */
.lgt-15:has(#lgt-15-theme:checked) {
--color-lgt15-bg: #0e0b1a;
--color-lgt15-surface: rgb(255 255 255 / 0.10);
--color-lgt15-fg: #f2f0fa;
--color-lgt15-muted: rgb(255 255 255 / 0.7);
--color-lgt15-rim: rgb(255 255 255 / 0.3);
}
.lgt-15:has(#lgt-15-collapse:checked) {
--w: 78px;
}
.lgt-15:has(#lgt-15-collapse:checked) .lgt-15-label {
opacity: 0;
}
@keyframes lgt-15-float {
0%,
100% {
transform: translate(0, 0);
}
50% {
transform: translate(24px, -20px);
}
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;800&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
:has()lets a clipped checkbox restyle its ancestor, which is what makes a JavaScript-free theme switch possible at all.- Because utilities read
var(--color-lgt15-*), re-pointing five variables restyles the entire shell — there is not a singledark:variant in the markup. - The sidebar width is a
--wvariable consumed byw-(--w), v4's shorthand forw-[var(--w)]. - Labels fade rather than unmount, so the collapse has something to animate.
- The drifting blobs exist so the frost has colour to sample, and they stop completely under
motion-reduce.
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 Liquid Glass Sidebar Navigation
Source: https://codefronts.com/design-styles/tailwind-liquid-glass-effects/tailwind-liquid-glass-sidebar-navigation/
A collapsible dashboard shell that re-tints and re-sizes itself from two checkboxes and zero JavaScript. This is the demo where v4's token model pays off hardest: has-[#lgt-15-theme:checked]: re-points the palette variables, and every bg-lgt15-surface in the markup changes meaning at once.
## HTML
```html
<section class="lgt-15 relative flex w-full min-h-svh overflow-hidden bg-lgt15-bg font-lgt15-sans" aria-label="Tailwind liquid glass sidebar navigation">
<div aria-hidden="true" class="pointer-events-none absolute inset-0">
<span class="absolute -top-10 left-30 size-70 animate-lgt15-float rounded-full bg-lgt15-blob1 opacity-70 blur-[40px] motion-reduce:animate-none"></span>
<span class="absolute -bottom-8 left-10 size-60 animate-lgt15-float rounded-full bg-lgt15-blob2 opacity-70 blur-[40px] [animation-delay:-4s] motion-reduce:animate-none"></span>
<span class="absolute top-2/5 -right-10 size-75 animate-lgt15-float rounded-full bg-lgt15-blob3 opacity-70 blur-[40px] [animation-delay:-8s] motion-reduce:animate-none"></span>
</div>
<input type="checkbox" id="lgt-15-collapse" class="sr-only">
<input type="checkbox" id="lgt-15-theme" class="sr-only">
<aside class="relative z-2 m-4 flex w-(--w) flex-none flex-col gap-2 rounded-[22px] border border-lgt15-rim bg-lgt15-surface p-3.5 pt-4.5 backdrop-blur-[24px] backdrop-saturate-180 shadow-[inset_0_1px_0_var(--color-lgt15-rim),0_30px_60px_-30px_rgba(0,0,0,.4)] transition-[width] duration-350 ease-out motion-reduce:transition-none">
<div class="flex items-center gap-3 px-2.5 pt-2 pb-3.5 text-lgt15-fg">
<span class="flex-none text-[22px] text-lgt15-active">◆</span>
<span class="lgt-15-label overflow-hidden text-[19px] font-bold whitespace-nowrap transition-opacity duration-200">Nimbus</span>
</div>
<nav class="flex flex-1 flex-col gap-1" aria-label="Dashboard">
<a href="#lgt-15" aria-current="page" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active aria-[current=page]:bg-lgt15-active aria-[current=page]:text-white aria-[current=page]:shadow-[inset_0_1px_0_rgba(255,255,255,.4)]"><span class="w-5 flex-none text-center text-[17px]">▤</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Overview</span></a>
<a href="#lgt-15" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active"><span class="w-5 flex-none text-center text-[17px]">◫</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Projects</span></a>
<a href="#lgt-15" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active"><span class="w-5 flex-none text-center text-[17px]">☰</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Tasks</span></a>
<a href="#lgt-15" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active"><span class="w-5 flex-none text-center text-[17px]">◍</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Reports</span></a>
</nav>
<div class="flex flex-col gap-1 border-t border-lgt15-rim pt-2.5">
<label for="lgt-15-theme" class="flex cursor-pointer items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg"><span class="w-5 flex-none text-center text-[17px]">◐</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Dark mode</span></label>
<label for="lgt-15-collapse" class="flex cursor-pointer items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg"><span class="w-5 flex-none text-center text-[17px]">⇔</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Collapse</span></label>
</div>
</aside>
<div class="relative z-2 flex-1 px-10 py-15 text-lgt15-fg">
<h3 class="mb-2.5 text-3xl font-extrabold">Dashboard</h3>
<p class="max-w-[44ch] text-base leading-relaxed text-lgt15-muted">Toggle dark mode or collapse — the shell re-points its theme variables through CSS <code>:has()</code>, so every utility changes meaning at once. No JavaScript.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-lgt15-bg: #dfe6f5;
--color-lgt15-surface: rgb(255 255 255 / 0.55);
--color-lgt15-fg: #1a1730;
--color-lgt15-muted: rgb(26 23 48 / 0.66);
--color-lgt15-rim: rgb(255 255 255 / 0.8);
--color-lgt15-active: #7c5cf0;
--color-lgt15-active: oklch(0.66 0.18 285);
--color-lgt15-blob1: oklch(0.7 0.2 300);
--color-lgt15-blob2: oklch(0.72 0.18 200);
--color-lgt15-blob3: oklch(0.72 0.17 40);
--font-lgt15-sans: 'Manrope', system-ui, sans-serif;
--animate-lgt15-float: lgt-15-float 14s ease-in-out infinite;
}
.lgt-15 {
--w: 230px;
}
/* A descendant checkbox restyling its ancestor: the whole shell, in two rules. */
.lgt-15:has(#lgt-15-theme:checked) {
--color-lgt15-bg: #0e0b1a;
--color-lgt15-surface: rgb(255 255 255 / 0.10);
--color-lgt15-fg: #f2f0fa;
--color-lgt15-muted: rgb(255 255 255 / 0.7);
--color-lgt15-rim: rgb(255 255 255 / 0.3);
}
.lgt-15:has(#lgt-15-collapse:checked) {
--w: 78px;
}
.lgt-15:has(#lgt-15-collapse:checked) .lgt-15-label {
opacity: 0;
}
@keyframes lgt-15-float {
0%,
100% {
transform: translate(0, 0);
}
50% {
transform: translate(24px, -20px);
}
}
```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 Liquid Glass Sidebar Navigation
Source: https://codefronts.com/design-styles/tailwind-liquid-glass-effects/tailwind-liquid-glass-sidebar-navigation/
A collapsible dashboard shell that re-tints and re-sizes itself from two checkboxes and zero JavaScript. This is the demo where v4's token model pays off hardest: has-[#lgt-15-theme:checked]: re-points the palette variables, and every bg-lgt15-surface in the markup changes meaning at once.
## HTML
```html
<section class="lgt-15 relative flex w-full min-h-svh overflow-hidden bg-lgt15-bg font-lgt15-sans" aria-label="Tailwind liquid glass sidebar navigation">
<div aria-hidden="true" class="pointer-events-none absolute inset-0">
<span class="absolute -top-10 left-30 size-70 animate-lgt15-float rounded-full bg-lgt15-blob1 opacity-70 blur-[40px] motion-reduce:animate-none"></span>
<span class="absolute -bottom-8 left-10 size-60 animate-lgt15-float rounded-full bg-lgt15-blob2 opacity-70 blur-[40px] [animation-delay:-4s] motion-reduce:animate-none"></span>
<span class="absolute top-2/5 -right-10 size-75 animate-lgt15-float rounded-full bg-lgt15-blob3 opacity-70 blur-[40px] [animation-delay:-8s] motion-reduce:animate-none"></span>
</div>
<input type="checkbox" id="lgt-15-collapse" class="sr-only">
<input type="checkbox" id="lgt-15-theme" class="sr-only">
<aside class="relative z-2 m-4 flex w-(--w) flex-none flex-col gap-2 rounded-[22px] border border-lgt15-rim bg-lgt15-surface p-3.5 pt-4.5 backdrop-blur-[24px] backdrop-saturate-180 shadow-[inset_0_1px_0_var(--color-lgt15-rim),0_30px_60px_-30px_rgba(0,0,0,.4)] transition-[width] duration-350 ease-out motion-reduce:transition-none">
<div class="flex items-center gap-3 px-2.5 pt-2 pb-3.5 text-lgt15-fg">
<span class="flex-none text-[22px] text-lgt15-active">◆</span>
<span class="lgt-15-label overflow-hidden text-[19px] font-bold whitespace-nowrap transition-opacity duration-200">Nimbus</span>
</div>
<nav class="flex flex-1 flex-col gap-1" aria-label="Dashboard">
<a href="#lgt-15" aria-current="page" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active aria-[current=page]:bg-lgt15-active aria-[current=page]:text-white aria-[current=page]:shadow-[inset_0_1px_0_rgba(255,255,255,.4)]"><span class="w-5 flex-none text-center text-[17px]">▤</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Overview</span></a>
<a href="#lgt-15" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active"><span class="w-5 flex-none text-center text-[17px]">◫</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Projects</span></a>
<a href="#lgt-15" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active"><span class="w-5 flex-none text-center text-[17px]">☰</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Tasks</span></a>
<a href="#lgt-15" class="flex items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted no-underline transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-lgt15-active"><span class="w-5 flex-none text-center text-[17px]">◍</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Reports</span></a>
</nav>
<div class="flex flex-col gap-1 border-t border-lgt15-rim pt-2.5">
<label for="lgt-15-theme" class="flex cursor-pointer items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg"><span class="w-5 flex-none text-center text-[17px]">◐</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Dark mode</span></label>
<label for="lgt-15-collapse" class="flex cursor-pointer items-center gap-3.5 rounded-[12px] px-3 py-2.75 text-sm font-semibold text-lgt15-muted transition-colors duration-200 hover:bg-lgt15-fg/10 hover:text-lgt15-fg"><span class="w-5 flex-none text-center text-[17px]">⇔</span><span class="lgt-15-label overflow-hidden whitespace-nowrap transition-opacity duration-200">Collapse</span></label>
</div>
</aside>
<div class="relative z-2 flex-1 px-10 py-15 text-lgt15-fg">
<h3 class="mb-2.5 text-3xl font-extrabold">Dashboard</h3>
<p class="max-w-[44ch] text-base leading-relaxed text-lgt15-muted">Toggle dark mode or collapse — the shell re-points its theme variables through CSS <code>:has()</code>, so every utility changes meaning at once. No JavaScript.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-lgt15-bg: #dfe6f5;
--color-lgt15-surface: rgb(255 255 255 / 0.55);
--color-lgt15-fg: #1a1730;
--color-lgt15-muted: rgb(26 23 48 / 0.66);
--color-lgt15-rim: rgb(255 255 255 / 0.8);
--color-lgt15-active: #7c5cf0;
--color-lgt15-active: oklch(0.66 0.18 285);
--color-lgt15-blob1: oklch(0.7 0.2 300);
--color-lgt15-blob2: oklch(0.72 0.18 200);
--color-lgt15-blob3: oklch(0.72 0.17 40);
--font-lgt15-sans: 'Manrope', system-ui, sans-serif;
--animate-lgt15-float: lgt-15-float 14s ease-in-out infinite;
}
.lgt-15 {
--w: 230px;
}
/* A descendant checkbox restyling its ancestor: the whole shell, in two rules. */
.lgt-15:has(#lgt-15-theme:checked) {
--color-lgt15-bg: #0e0b1a;
--color-lgt15-surface: rgb(255 255 255 / 0.10);
--color-lgt15-fg: #f2f0fa;
--color-lgt15-muted: rgb(255 255 255 / 0.7);
--color-lgt15-rim: rgb(255 255 255 / 0.3);
}
.lgt-15:has(#lgt-15-collapse:checked) {
--w: 78px;
}
.lgt-15:has(#lgt-15-collapse:checked) .lgt-15-label {
opacity: 0;
}
@keyframes lgt-15-float {
0%,
100% {
transform: translate(0, 0);
}
50% {
transform: translate(24px, -20px);
}
}
```How this works
:has() is the whole state machine. Two clipped checkboxes sit at the top of the shell. The root asks whether either is checked and re-points its variables accordingly — which restyles every descendant utility that reads them:
.lgt-15:has(#lgt-15-theme:checked) {
--color-lgt15-surface: rgb(255 255 255 / 0.10);
--color-lgt15-fg: #f2f0fa;
--color-lgt15-rim: rgb(255 255 255 / 0.30);
}
.lgt-15:has(#lgt-15-collapse:checked) { --w: 78px; }No dark: variants anywhere. That is the point. A dark: class on every element is the version of this component where the two themes drift apart over time; a token flip is the version where they cannot.
Collapse is one variable. The sidebar's width reads --w and transitions it; the labels fade to opacity-0 while the icons stay. Nothing unmounts, so there is nothing to animate back in.
The blobs are load-bearing. Frosted glass over a flat panel looks like a flat panel. Three slowly drifting colour blobs behind the shell give the backdrop-filter something worth sampling — and they stop entirely under motion-reduce.
Make it yours
- Rebrand both modes by editing the two token blocks; no markup changes at all.
- Change the collapsed width by editing the single
--woverride, and choose what hides by adding or removing the fade utility. - Restyle active items inside the
aria-[current=page]:chain — the semantics stay in the attribute. - Swap the glyph icons for SVGs; the icon slot has a fixed width so the collapsed layout does not shift.
- Persist both toggles by mirroring their state into localStorage if this ships in a real dashboard — the demo stays intentionally stateless.
Gotchas — read before shipping
:has()is the load-bearing selector. Where it is unsupported the shell simply stays in its default expanded theme, which is why the default must be a usable state and not a broken one.- Keep the checkboxes
sr-only, nothidden— a hidden checkbox cannot be reached or toggled by keyboard. - Both themes need independent contrast checks. Muted text at 66% opacity passes on dark glass and often fails on light.
- Fade labels rather than removing them; unmounting on collapse means there is nothing to animate and the sidebar snaps.
- The blobs must not intercept clicks —
pointer-events-noneon the decorative layer. - A
:has()rule that re-points variables is fast, but re-pointing them on a huge subtree still invalidates its paint. Scope the rule to the shell, not to:root.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
:has() (Chrome 105+, Safari 15.4+, Firefox 121+) drives both the theme flip and the collapse, and Tailwind v4's sheet sets the same practical floor. Without it the sidebar stays expanded in its default theme and remains fully navigable.