13 / 17
All 17 in 17 Tailwind CSS Side Menus
- 01Tailwind Slide-In Side Menu with Animated Hamburger Icon
- 02Tailwind Sidebar Drawer with Blurred Backdrop Overlay
- 03Tailwind Off-Canvas Push Sidebar Menu
- 04Tailwind Collapsible Icon Sidebar That Expands on Hover
- 05Responsive Tailwind Sidebar with Mobile Hamburger Toggle
- 06Tailwind Sticky Sidebar Navigation with Scrolling Content
- 07Tailwind Multi-Level Dropdown Sidebar Menu (Nested Accordion)
- 08Tailwind Full-Height Flexbox Sidebar Layout
- 09Tailwind Neumorphism Sidebar Menu (Dark Mode)
- 10Tailwind Glassmorphism Sidebar with Backdrop Blur
- 11Tailwind Cyberpunk Neon Sidebar (Dark Mode)
- 12Brutalist Sidebar Navigation in Tailwind CSS
- 13Pure CSS Hamburger Menu in Tailwind (Checkbox Hack, No JS)
- 14Tailwind Sidebar Drawer That Opens on Hover
- 15Tailwind Collapsible Sidebar with Toggle Button (No JS)
- 16Tailwind Accordion Sidebar Navigation for Documentation
- 17Tailwind macOS-Style Glass Dock Sidebar with Sliding Highlight
Pure CSS Hamburger Menu in Tailwind (Checkbox Hack, No JS)
The checkbox hack, explained by itself: a hamburger that springs into an ✕ on an overshoot curve, a drawer, a click-outside scrim, and a live state pill that flips between “closed” and “open” so you can watch the :checked state drive the UI. The code card shows the actual Tailwind classes doing the work. 0 lines of JavaScript.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tsm-13 group relative isolate w-full min-h-svh overflow-hidden bg-tsm13-bg font-tsm13-sans text-tsm13-ink" aria-label="Pure CSS hamburger menu checkbox hack demo">
<input type="checkbox" id="tsm-13-chk" class="peer sr-only">
<label for="tsm-13-chk" data-tsm13-burger class="absolute top-[18px] left-[18px] z-50 grid size-11 cursor-pointer place-content-center gap-1.5 rounded-[10px] border border-tsm13-acc/20 bg-tsm13-surface transition-colors hover:bg-tsm13-acc/15 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-tsm13-acc2">
<span class="sr-only">Toggle navigation menu</span>
<span aria-hidden="true" class="block h-0.5 w-5 rounded-full bg-tsm13-ink transition-[translate,rotate,opacity,width] duration-400 ease-[cubic-bezier(0.68,-0.55,0.27,1.55)] motion-reduce:transition-none group-has-checked:translate-y-2 group-has-checked:rotate-45"></span>
<span aria-hidden="true" class="block h-0.5 w-5 rounded-full bg-tsm13-ink transition-[translate,rotate,opacity,width] duration-400 ease-[cubic-bezier(0.68,-0.55,0.27,1.55)] motion-reduce:transition-none group-has-checked:w-0 group-has-checked:opacity-0"></span>
<span aria-hidden="true" class="block h-0.5 w-5 rounded-full bg-tsm13-ink transition-[translate,rotate,opacity,width] duration-400 ease-[cubic-bezier(0.68,-0.55,0.27,1.55)] motion-reduce:transition-none group-has-checked:-translate-y-2 group-has-checked:-rotate-45"></span>
</label>
<label for="tsm-13-chk" aria-hidden="true" class="pointer-events-none absolute inset-0 z-40 bg-black/0 transition-colors duration-400 group-has-checked:pointer-events-auto group-has-checked:bg-black/50"></label>
<nav aria-label="Main" class="invisible absolute inset-y-0 left-0 z-50 flex w-[min(270px,86vw)] -translate-x-full flex-col border-r border-tsm13-acc/15 bg-tsm13-nav pt-[78px] pb-5 transition-[translate,visibility,box-shadow] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] before:absolute before:inset-x-0 before:top-0 before:h-0.5 before:bg-linear-to-r before:from-tsm13-acc before:via-tsm13-acc2 before:to-transparent group-has-checked:visible group-has-checked:translate-x-0 group-has-checked:shadow-[8px_0_32px_oklch(0_0_0/0.5)] motion-reduce:transition-none">
<ul class="grid flex-1 content-start gap-1 px-2.5">
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2" aria-current="page"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Dashboard</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Analytics</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Content</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Audience</a></li>
<li aria-hidden="true" class="mx-3.5 my-2 h-px bg-tsm13-acc/15"></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Help</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Settings</a></li>
</ul>
<div class="mx-[18px] flex items-center gap-2.5 border-t border-tsm13-acc/15 pt-4">
<span aria-hidden="true" class="grid size-8 place-items-center rounded-lg bg-linear-135 from-tsm13-acc to-red-500 text-xs font-bold">CK</span>
<div><p class="text-[13px] font-bold">C. Kim</p><p class="text-[11px] text-tsm13-mut">Publisher</p></div>
</div>
</nav>
<div class="mx-auto w-full max-w-2xl px-6 pt-[86px] pb-8">
<div class="flex flex-wrap items-center gap-3">
<h1 class="text-xl font-bold">Checkbox-hack menu</h1>
<span class="inline-flex items-center gap-1.5 rounded-full border border-tsm13-acc/30 px-2.5 py-1 font-tsm13-mono text-[11px] text-tsm13-acc2"><span aria-hidden="true" class="size-1.5 rounded-full bg-tsm13-mut group-has-checked:bg-tsm13-acc"></span>:checked = <span class="group-has-checked:hidden">false</span><span class="hidden group-has-checked:inline">true</span></span>
</div>
<p class="mt-1.5 mb-5 max-w-prose text-[13px] leading-relaxed text-tsm13-mut text-pretty">A hidden checkbox and Tailwind’s <code>group-has-checked:</code> variant drive the whole menu. Tab to the button and press Space — it works from the keyboard too.</p>
<pre class="mb-3.5 overflow-x-auto rounded-xl border border-tsm13-acc/15 bg-tsm13-surface px-4 py-3.5 font-tsm13-mono text-xs leading-[1.8] text-tsm13-mut"><code><span class="text-tsm13-sel"><input</span> <span class="text-tsm13-acc2">class</span>=<span class="text-emerald-300">"peer sr-only"</span><span class="text-tsm13-sel">></span>
<span class="text-tsm13-sel"><nav</span> <span class="text-tsm13-acc2">class</span>=<span class="text-emerald-300">"-translate-x-full
group-has-checked:translate-x-0"</span><span class="text-tsm13-sel">></span></code></pre>
<div class="grid grid-cols-2 gap-2.5">
<div class="rounded-xl border border-tsm13-acc/15 bg-tsm13-surface p-3.5"><p class="text-xl font-bold text-tsm13-acc2">0</p><p class="mt-1 text-[11px] text-tsm13-mut">Lines of JavaScript</p></div>
<div class="rounded-xl border border-tsm13-acc/15 bg-tsm13-surface p-3.5"><p class="text-xl font-bold text-tsm13-acc2">1</p><p class="mt-1 text-[11px] text-tsm13-mut">Checkbox of state</p></div>
</div>
</div>
</section><section class="tsm-13 group relative isolate w-full min-h-svh overflow-hidden bg-tsm13-bg font-tsm13-sans text-tsm13-ink" aria-label="Pure CSS hamburger menu checkbox hack demo">
<input type="checkbox" id="tsm-13-chk" class="peer sr-only">
<label for="tsm-13-chk" data-tsm13-burger class="absolute top-[18px] left-[18px] z-50 grid size-11 cursor-pointer place-content-center gap-1.5 rounded-[10px] border border-tsm13-acc/20 bg-tsm13-surface transition-colors hover:bg-tsm13-acc/15 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-tsm13-acc2">
<span class="sr-only">Toggle navigation menu</span>
<span aria-hidden="true" class="block h-0.5 w-5 rounded-full bg-tsm13-ink transition-[translate,rotate,opacity,width] duration-400 ease-[cubic-bezier(0.68,-0.55,0.27,1.55)] motion-reduce:transition-none group-has-checked:translate-y-2 group-has-checked:rotate-45"></span>
<span aria-hidden="true" class="block h-0.5 w-5 rounded-full bg-tsm13-ink transition-[translate,rotate,opacity,width] duration-400 ease-[cubic-bezier(0.68,-0.55,0.27,1.55)] motion-reduce:transition-none group-has-checked:w-0 group-has-checked:opacity-0"></span>
<span aria-hidden="true" class="block h-0.5 w-5 rounded-full bg-tsm13-ink transition-[translate,rotate,opacity,width] duration-400 ease-[cubic-bezier(0.68,-0.55,0.27,1.55)] motion-reduce:transition-none group-has-checked:-translate-y-2 group-has-checked:-rotate-45"></span>
</label>
<label for="tsm-13-chk" aria-hidden="true" class="pointer-events-none absolute inset-0 z-40 bg-black/0 transition-colors duration-400 group-has-checked:pointer-events-auto group-has-checked:bg-black/50"></label>
<nav aria-label="Main" class="invisible absolute inset-y-0 left-0 z-50 flex w-[min(270px,86vw)] -translate-x-full flex-col border-r border-tsm13-acc/15 bg-tsm13-nav pt-[78px] pb-5 transition-[translate,visibility,box-shadow] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] before:absolute before:inset-x-0 before:top-0 before:h-0.5 before:bg-linear-to-r before:from-tsm13-acc before:via-tsm13-acc2 before:to-transparent group-has-checked:visible group-has-checked:translate-x-0 group-has-checked:shadow-[8px_0_32px_oklch(0_0_0/0.5)] motion-reduce:transition-none">
<ul class="grid flex-1 content-start gap-1 px-2.5">
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2" aria-current="page"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Dashboard</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Analytics</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Content</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Audience</a></li>
<li aria-hidden="true" class="mx-3.5 my-2 h-px bg-tsm13-acc/15"></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Help</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Settings</a></li>
</ul>
<div class="mx-[18px] flex items-center gap-2.5 border-t border-tsm13-acc/15 pt-4">
<span aria-hidden="true" class="grid size-8 place-items-center rounded-lg bg-linear-135 from-tsm13-acc to-red-500 text-xs font-bold">CK</span>
<div><p class="text-[13px] font-bold">C. Kim</p><p class="text-[11px] text-tsm13-mut">Publisher</p></div>
</div>
</nav>
<div class="mx-auto w-full max-w-2xl px-6 pt-[86px] pb-8">
<div class="flex flex-wrap items-center gap-3">
<h1 class="text-xl font-bold">Checkbox-hack menu</h1>
<span class="inline-flex items-center gap-1.5 rounded-full border border-tsm13-acc/30 px-2.5 py-1 font-tsm13-mono text-[11px] text-tsm13-acc2"><span aria-hidden="true" class="size-1.5 rounded-full bg-tsm13-mut group-has-checked:bg-tsm13-acc"></span>:checked = <span class="group-has-checked:hidden">false</span><span class="hidden group-has-checked:inline">true</span></span>
</div>
<p class="mt-1.5 mb-5 max-w-prose text-[13px] leading-relaxed text-tsm13-mut text-pretty">A hidden checkbox and Tailwind’s <code>group-has-checked:</code> variant drive the whole menu. Tab to the button and press Space — it works from the keyboard too.</p>
<pre class="mb-3.5 overflow-x-auto rounded-xl border border-tsm13-acc/15 bg-tsm13-surface px-4 py-3.5 font-tsm13-mono text-xs leading-[1.8] text-tsm13-mut"><code><span class="text-tsm13-sel"><input</span> <span class="text-tsm13-acc2">class</span>=<span class="text-emerald-300">"peer sr-only"</span><span class="text-tsm13-sel">></span>
<span class="text-tsm13-sel"><nav</span> <span class="text-tsm13-acc2">class</span>=<span class="text-emerald-300">"-translate-x-full
group-has-checked:translate-x-0"</span><span class="text-tsm13-sel">></span></code></pre>
<div class="grid grid-cols-2 gap-2.5">
<div class="rounded-xl border border-tsm13-acc/15 bg-tsm13-surface p-3.5"><p class="text-xl font-bold text-tsm13-acc2">0</p><p class="mt-1 text-[11px] text-tsm13-mut">Lines of JavaScript</p></div>
<div class="rounded-xl border border-tsm13-acc/15 bg-tsm13-surface p-3.5"><p class="text-xl font-bold text-tsm13-acc2">1</p><p class="mt-1 text-[11px] text-tsm13-mut">Checkbox of state</p></div>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-tsm13-bg: #111113;
--color-tsm13-bg: oklch(0.179 0.004 286);
--color-tsm13-surface: #1c1c1f;
--color-tsm13-surface: oklch(0.228 0.006 285.9);
--color-tsm13-nav: #18181b;
--color-tsm13-nav: oklch(0.21 0.006 285.9);
--color-tsm13-acc: #f97316;
--color-tsm13-acc: oklch(0.705 0.187 47.6);
--color-tsm13-acc2: #fb923c;
--color-tsm13-acc2: oklch(0.758 0.159 55.9);
--color-tsm13-ink: #f4f4f5;
--color-tsm13-ink: oklch(0.967 0.001 0);
--color-tsm13-mut: #a1a1aa;
--color-tsm13-mut: oklch(0.712 0.013 286.1);
--color-tsm13-sel: #60a5fa;
--color-tsm13-sel: oklch(0.714 0.143 254.6);
--font-tsm13-sans: 'Rubik', system-ui, sans-serif;
--font-tsm13-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tsm-13 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tsm13-bg: #111113;
--color-tsm13-bg: oklch(0.179 0.004 286);
--color-tsm13-surface: #1c1c1f;
--color-tsm13-surface: oklch(0.228 0.006 285.9);
--color-tsm13-nav: #18181b;
--color-tsm13-nav: oklch(0.21 0.006 285.9);
--color-tsm13-acc: #f97316;
--color-tsm13-acc: oklch(0.705 0.187 47.6);
--color-tsm13-acc2: #fb923c;
--color-tsm13-acc2: oklch(0.758 0.159 55.9);
--color-tsm13-ink: #f4f4f5;
--color-tsm13-ink: oklch(0.967 0.001 0);
--color-tsm13-mut: #a1a1aa;
--color-tsm13-mut: oklch(0.712 0.013 286.1);
--color-tsm13-sel: #60a5fa;
--color-tsm13-sel: oklch(0.714 0.143 254.6);
--font-tsm13-sans: 'Rubik', system-ui, sans-serif;
--font-tsm13-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tsm-13 {
width: 100%;
min-height: 100svh;
}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-tsm13-bg: #111113;
--color-tsm13-bg: oklch(0.179 0.004 286);
--color-tsm13-surface: #1c1c1f;
--color-tsm13-surface: oklch(0.228 0.006 285.9);
--color-tsm13-nav: #18181b;
--color-tsm13-nav: oklch(0.21 0.006 285.9);
--color-tsm13-acc: #f97316;
--color-tsm13-acc: oklch(0.705 0.187 47.6);
--color-tsm13-acc2: #fb923c;
--color-tsm13-acc2: oklch(0.758 0.159 55.9);
--color-tsm13-ink: #f4f4f5;
--color-tsm13-ink: oklch(0.967 0.001 0);
--color-tsm13-mut: #a1a1aa;
--color-tsm13-mut: oklch(0.712 0.013 286.1);
--color-tsm13-sel: #60a5fa;
--color-tsm13-sel: oklch(0.714 0.143 254.6);
--font-tsm13-sans: 'Rubik', system-ui, sans-serif;
--font-tsm13-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tsm-13 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tsm13-bg: #111113;
--color-tsm13-bg: oklch(0.179 0.004 286);
--color-tsm13-surface: #1c1c1f;
--color-tsm13-surface: oklch(0.228 0.006 285.9);
--color-tsm13-nav: #18181b;
--color-tsm13-nav: oklch(0.21 0.006 285.9);
--color-tsm13-acc: #f97316;
--color-tsm13-acc: oklch(0.705 0.187 47.6);
--color-tsm13-acc2: #fb923c;
--color-tsm13-acc2: oklch(0.758 0.159 55.9);
--color-tsm13-ink: #f4f4f5;
--color-tsm13-ink: oklch(0.967 0.001 0);
--color-tsm13-mut: #a1a1aa;
--color-tsm13-mut: oklch(0.712 0.013 286.1);
--color-tsm13-sel: #60a5fa;
--color-tsm13-sel: oklch(0.714 0.143 254.6);
--font-tsm13-sans: 'Rubik', system-ui, sans-serif;
--font-tsm13-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tsm-13 {
width: 100%;
min-height: 100svh;
}<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=Rubik:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&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=Rubik:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Seven
#sm-13-chk:checked ~ …rules becamegroup-has-checked:utilities, and the ID-based selector engine disappeared from the stylesheet entirely. - UI upgrade from the CSS original: the checkbox is
sr-onlyinstead ofdisplay:none, so the menu works from the keyboard, and the burger shows apeer-focus-visible:ring. - UI upgrade: a live
:checked = true/falsepill makes the technique visible, and the code card now shows the real Tailwind classes rather than the CSS it replaced. - UI upgrade: the bars morph on a spring curve, the closed drawer is
invisible, and the mono code text uses JetBrains Mono instead of the browser default.
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: Pure CSS Hamburger Menu in Tailwind (Checkbox Hack, No JS)
Source: https://codefronts.com/navigation/tailwind-css-side-menus/pure-css-hamburger-menu-tailwind-checkbox-hack/
The checkbox hack, explained by itself: a hamburger that springs into an ✕ on an overshoot curve, a drawer, a click-outside scrim, and a live state pill that flips between “closed” and “open” so you can watch the :checked state drive the UI. The code card shows the actual Tailwind classes doing the work. 0 lines of JavaScript.
## HTML
```html
<section class="tsm-13 group relative isolate w-full min-h-svh overflow-hidden bg-tsm13-bg font-tsm13-sans text-tsm13-ink" aria-label="Pure CSS hamburger menu checkbox hack demo">
<input type="checkbox" id="tsm-13-chk" class="peer sr-only">
<label for="tsm-13-chk" data-tsm13-burger class="absolute top-[18px] left-[18px] z-50 grid size-11 cursor-pointer place-content-center gap-1.5 rounded-[10px] border border-tsm13-acc/20 bg-tsm13-surface transition-colors hover:bg-tsm13-acc/15 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-tsm13-acc2">
<span class="sr-only">Toggle navigation menu</span>
<span aria-hidden="true" class="block h-0.5 w-5 rounded-full bg-tsm13-ink transition-[translate,rotate,opacity,width] duration-400 ease-[cubic-bezier(0.68,-0.55,0.27,1.55)] motion-reduce:transition-none group-has-checked:translate-y-2 group-has-checked:rotate-45"></span>
<span aria-hidden="true" class="block h-0.5 w-5 rounded-full bg-tsm13-ink transition-[translate,rotate,opacity,width] duration-400 ease-[cubic-bezier(0.68,-0.55,0.27,1.55)] motion-reduce:transition-none group-has-checked:w-0 group-has-checked:opacity-0"></span>
<span aria-hidden="true" class="block h-0.5 w-5 rounded-full bg-tsm13-ink transition-[translate,rotate,opacity,width] duration-400 ease-[cubic-bezier(0.68,-0.55,0.27,1.55)] motion-reduce:transition-none group-has-checked:-translate-y-2 group-has-checked:-rotate-45"></span>
</label>
<label for="tsm-13-chk" aria-hidden="true" class="pointer-events-none absolute inset-0 z-40 bg-black/0 transition-colors duration-400 group-has-checked:pointer-events-auto group-has-checked:bg-black/50"></label>
<nav aria-label="Main" class="invisible absolute inset-y-0 left-0 z-50 flex w-[min(270px,86vw)] -translate-x-full flex-col border-r border-tsm13-acc/15 bg-tsm13-nav pt-[78px] pb-5 transition-[translate,visibility,box-shadow] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] before:absolute before:inset-x-0 before:top-0 before:h-0.5 before:bg-linear-to-r before:from-tsm13-acc before:via-tsm13-acc2 before:to-transparent group-has-checked:visible group-has-checked:translate-x-0 group-has-checked:shadow-[8px_0_32px_oklch(0_0_0/0.5)] motion-reduce:transition-none">
<ul class="grid flex-1 content-start gap-1 px-2.5">
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2" aria-current="page"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Dashboard</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Analytics</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Content</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Audience</a></li>
<li aria-hidden="true" class="mx-3.5 my-2 h-px bg-tsm13-acc/15"></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Help</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Settings</a></li>
</ul>
<div class="mx-[18px] flex items-center gap-2.5 border-t border-tsm13-acc/15 pt-4">
<span aria-hidden="true" class="grid size-8 place-items-center rounded-lg bg-linear-135 from-tsm13-acc to-red-500 text-xs font-bold">CK</span>
<div><p class="text-[13px] font-bold">C. Kim</p><p class="text-[11px] text-tsm13-mut">Publisher</p></div>
</div>
</nav>
<div class="mx-auto w-full max-w-2xl px-6 pt-[86px] pb-8">
<div class="flex flex-wrap items-center gap-3">
<h1 class="text-xl font-bold">Checkbox-hack menu</h1>
<span class="inline-flex items-center gap-1.5 rounded-full border border-tsm13-acc/30 px-2.5 py-1 font-tsm13-mono text-[11px] text-tsm13-acc2"><span aria-hidden="true" class="size-1.5 rounded-full bg-tsm13-mut group-has-checked:bg-tsm13-acc"></span>:checked = <span class="group-has-checked:hidden">false</span><span class="hidden group-has-checked:inline">true</span></span>
</div>
<p class="mt-1.5 mb-5 max-w-prose text-[13px] leading-relaxed text-tsm13-mut text-pretty">A hidden checkbox and Tailwind’s <code>group-has-checked:</code> variant drive the whole menu. Tab to the button and press Space — it works from the keyboard too.</p>
<pre class="mb-3.5 overflow-x-auto rounded-xl border border-tsm13-acc/15 bg-tsm13-surface px-4 py-3.5 font-tsm13-mono text-xs leading-[1.8] text-tsm13-mut"><code><span class="text-tsm13-sel"><input</span> <span class="text-tsm13-acc2">class</span>=<span class="text-emerald-300">"peer sr-only"</span><span class="text-tsm13-sel">></span>
<span class="text-tsm13-sel"><nav</span> <span class="text-tsm13-acc2">class</span>=<span class="text-emerald-300">"-translate-x-full
group-has-checked:translate-x-0"</span><span class="text-tsm13-sel">></span></code></pre>
<div class="grid grid-cols-2 gap-2.5">
<div class="rounded-xl border border-tsm13-acc/15 bg-tsm13-surface p-3.5"><p class="text-xl font-bold text-tsm13-acc2">0</p><p class="mt-1 text-[11px] text-tsm13-mut">Lines of JavaScript</p></div>
<div class="rounded-xl border border-tsm13-acc/15 bg-tsm13-surface p-3.5"><p class="text-xl font-bold text-tsm13-acc2">1</p><p class="mt-1 text-[11px] text-tsm13-mut">Checkbox of state</p></div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tsm13-bg: #111113;
--color-tsm13-bg: oklch(0.179 0.004 286);
--color-tsm13-surface: #1c1c1f;
--color-tsm13-surface: oklch(0.228 0.006 285.9);
--color-tsm13-nav: #18181b;
--color-tsm13-nav: oklch(0.21 0.006 285.9);
--color-tsm13-acc: #f97316;
--color-tsm13-acc: oklch(0.705 0.187 47.6);
--color-tsm13-acc2: #fb923c;
--color-tsm13-acc2: oklch(0.758 0.159 55.9);
--color-tsm13-ink: #f4f4f5;
--color-tsm13-ink: oklch(0.967 0.001 0);
--color-tsm13-mut: #a1a1aa;
--color-tsm13-mut: oklch(0.712 0.013 286.1);
--color-tsm13-sel: #60a5fa;
--color-tsm13-sel: oklch(0.714 0.143 254.6);
--font-tsm13-sans: 'Rubik', system-ui, sans-serif;
--font-tsm13-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tsm-13 {
width: 100%;
min-height: 100svh;
}
```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: Pure CSS Hamburger Menu in Tailwind (Checkbox Hack, No JS)
Source: https://codefronts.com/navigation/tailwind-css-side-menus/pure-css-hamburger-menu-tailwind-checkbox-hack/
The checkbox hack, explained by itself: a hamburger that springs into an ✕ on an overshoot curve, a drawer, a click-outside scrim, and a live state pill that flips between “closed” and “open” so you can watch the :checked state drive the UI. The code card shows the actual Tailwind classes doing the work. 0 lines of JavaScript.
## HTML
```html
<section class="tsm-13 group relative isolate w-full min-h-svh overflow-hidden bg-tsm13-bg font-tsm13-sans text-tsm13-ink" aria-label="Pure CSS hamburger menu checkbox hack demo">
<input type="checkbox" id="tsm-13-chk" class="peer sr-only">
<label for="tsm-13-chk" data-tsm13-burger class="absolute top-[18px] left-[18px] z-50 grid size-11 cursor-pointer place-content-center gap-1.5 rounded-[10px] border border-tsm13-acc/20 bg-tsm13-surface transition-colors hover:bg-tsm13-acc/15 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-tsm13-acc2">
<span class="sr-only">Toggle navigation menu</span>
<span aria-hidden="true" class="block h-0.5 w-5 rounded-full bg-tsm13-ink transition-[translate,rotate,opacity,width] duration-400 ease-[cubic-bezier(0.68,-0.55,0.27,1.55)] motion-reduce:transition-none group-has-checked:translate-y-2 group-has-checked:rotate-45"></span>
<span aria-hidden="true" class="block h-0.5 w-5 rounded-full bg-tsm13-ink transition-[translate,rotate,opacity,width] duration-400 ease-[cubic-bezier(0.68,-0.55,0.27,1.55)] motion-reduce:transition-none group-has-checked:w-0 group-has-checked:opacity-0"></span>
<span aria-hidden="true" class="block h-0.5 w-5 rounded-full bg-tsm13-ink transition-[translate,rotate,opacity,width] duration-400 ease-[cubic-bezier(0.68,-0.55,0.27,1.55)] motion-reduce:transition-none group-has-checked:-translate-y-2 group-has-checked:-rotate-45"></span>
</label>
<label for="tsm-13-chk" aria-hidden="true" class="pointer-events-none absolute inset-0 z-40 bg-black/0 transition-colors duration-400 group-has-checked:pointer-events-auto group-has-checked:bg-black/50"></label>
<nav aria-label="Main" class="invisible absolute inset-y-0 left-0 z-50 flex w-[min(270px,86vw)] -translate-x-full flex-col border-r border-tsm13-acc/15 bg-tsm13-nav pt-[78px] pb-5 transition-[translate,visibility,box-shadow] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] before:absolute before:inset-x-0 before:top-0 before:h-0.5 before:bg-linear-to-r before:from-tsm13-acc before:via-tsm13-acc2 before:to-transparent group-has-checked:visible group-has-checked:translate-x-0 group-has-checked:shadow-[8px_0_32px_oklch(0_0_0/0.5)] motion-reduce:transition-none">
<ul class="grid flex-1 content-start gap-1 px-2.5">
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2" aria-current="page"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Dashboard</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Analytics</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Content</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Audience</a></li>
<li aria-hidden="true" class="mx-3.5 my-2 h-px bg-tsm13-acc/15"></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Help</a></li>
<li><a href="#" class="group/l flex min-h-11 items-center gap-3 rounded-[10px] px-3.5 text-sm font-semibold text-tsm13-mut transition-colors hover:bg-tsm13-acc/10 hover:text-tsm13-ink focus-visible:outline-2 focus-visible:outline-tsm13-acc2 aria-[current=page]:bg-tsm13-acc/12 aria-[current=page]:text-tsm13-acc2"><span aria-hidden="true" class="size-2 shrink-0 rotate-45 rounded-[2px] bg-tsm13-mut/60 transition-colors group-hover/l:bg-tsm13-acc2 group-aria-[current=page]/l:bg-tsm13-acc group-aria-[current=page]/l:shadow-[0_0_6px_var(--color-tsm13-acc)]"></span>Settings</a></li>
</ul>
<div class="mx-[18px] flex items-center gap-2.5 border-t border-tsm13-acc/15 pt-4">
<span aria-hidden="true" class="grid size-8 place-items-center rounded-lg bg-linear-135 from-tsm13-acc to-red-500 text-xs font-bold">CK</span>
<div><p class="text-[13px] font-bold">C. Kim</p><p class="text-[11px] text-tsm13-mut">Publisher</p></div>
</div>
</nav>
<div class="mx-auto w-full max-w-2xl px-6 pt-[86px] pb-8">
<div class="flex flex-wrap items-center gap-3">
<h1 class="text-xl font-bold">Checkbox-hack menu</h1>
<span class="inline-flex items-center gap-1.5 rounded-full border border-tsm13-acc/30 px-2.5 py-1 font-tsm13-mono text-[11px] text-tsm13-acc2"><span aria-hidden="true" class="size-1.5 rounded-full bg-tsm13-mut group-has-checked:bg-tsm13-acc"></span>:checked = <span class="group-has-checked:hidden">false</span><span class="hidden group-has-checked:inline">true</span></span>
</div>
<p class="mt-1.5 mb-5 max-w-prose text-[13px] leading-relaxed text-tsm13-mut text-pretty">A hidden checkbox and Tailwind’s <code>group-has-checked:</code> variant drive the whole menu. Tab to the button and press Space — it works from the keyboard too.</p>
<pre class="mb-3.5 overflow-x-auto rounded-xl border border-tsm13-acc/15 bg-tsm13-surface px-4 py-3.5 font-tsm13-mono text-xs leading-[1.8] text-tsm13-mut"><code><span class="text-tsm13-sel"><input</span> <span class="text-tsm13-acc2">class</span>=<span class="text-emerald-300">"peer sr-only"</span><span class="text-tsm13-sel">></span>
<span class="text-tsm13-sel"><nav</span> <span class="text-tsm13-acc2">class</span>=<span class="text-emerald-300">"-translate-x-full
group-has-checked:translate-x-0"</span><span class="text-tsm13-sel">></span></code></pre>
<div class="grid grid-cols-2 gap-2.5">
<div class="rounded-xl border border-tsm13-acc/15 bg-tsm13-surface p-3.5"><p class="text-xl font-bold text-tsm13-acc2">0</p><p class="mt-1 text-[11px] text-tsm13-mut">Lines of JavaScript</p></div>
<div class="rounded-xl border border-tsm13-acc/15 bg-tsm13-surface p-3.5"><p class="text-xl font-bold text-tsm13-acc2">1</p><p class="mt-1 text-[11px] text-tsm13-mut">Checkbox of state</p></div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tsm13-bg: #111113;
--color-tsm13-bg: oklch(0.179 0.004 286);
--color-tsm13-surface: #1c1c1f;
--color-tsm13-surface: oklch(0.228 0.006 285.9);
--color-tsm13-nav: #18181b;
--color-tsm13-nav: oklch(0.21 0.006 285.9);
--color-tsm13-acc: #f97316;
--color-tsm13-acc: oklch(0.705 0.187 47.6);
--color-tsm13-acc2: #fb923c;
--color-tsm13-acc2: oklch(0.758 0.159 55.9);
--color-tsm13-ink: #f4f4f5;
--color-tsm13-ink: oklch(0.967 0.001 0);
--color-tsm13-mut: #a1a1aa;
--color-tsm13-mut: oklch(0.712 0.013 286.1);
--color-tsm13-sel: #60a5fa;
--color-tsm13-sel: oklch(0.714 0.143 254.6);
--font-tsm13-sans: 'Rubik', system-ui, sans-serif;
--font-tsm13-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tsm-13 {
width: 100%;
min-height: 100svh;
}
```How this works
Why the input must stay focusable. The CSS original hid the checkbox with display:none, which also removes it from the tab order — the menu was mouse-only. Here it is peer sr-only: invisible, but Tab lands on it and Space toggles it, and the adjacent burger shows the ring:
<input id="tsm-13-chk" type="checkbox" class="peer sr-only">
<label for="tsm-13-chk" class="peer-focus-visible:outline-2 …">Peer for neighbours, group for everything else. peer-* only reaches later siblings; the drawer, the scrim, the bars and the state pill all use group-has-checked: on the root instead, so markup order is free.
The spring is a single cubic-bezier with y-values outside 0–1, so the bars overshoot and settle.
Make it yours
- Asterisk instead of ✕: use
rotate-135/-rotate-135on the outer bars. - Calmer morph: replace the spring curve with
ease-out. - Right-hand drawer:
right-0 translate-x-fullon the nav,right-5on the burger. - Remove the state pill once you've understood it — it's a teaching aid, not chrome.
Gotchas — read before shipping
- Never
display:nonethe checkbox; usesr-onlyso keyboard users can reach it. - Keep the input outside any
<form>or give it noname, or it will be submitted. - Screen readers announce it as a checkbox; the label text ("Toggle navigation menu") makes that intelligible.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
:has() is Chrome 105 / Safari 15.4 / Firefox 121; the floor is v4's own sheet.