12 / 28
All 28 in 28 Tailwind Hamburger Menus
- 01Tailwind Hamburger Menu with Clip-Path Circle Expansion
- 02Tailwind Keyboard Navigable Hamburger Menu with ARIA Attributes
- 03Tailwind Animated 3-Line to X Hamburger Icon Toggle
- 04Tailwind Slide-In Off-Canvas Drawer with Dimmed Backdrop Overlay
- 05Pure Tailwind Checkbox Hack Hamburger Navigation (No JavaScript)
- 06Tailwind Responsive Hamburger Menu with Integrated Search Bar & Social Links
- 07Tailwind Glassmorphism Backdrop Blur Hamburger Navigation
- 08Tailwind Push Page Navigation Menu (Off-Canvas Content Shift)
- 09Pure Tailwind Hamburger Menu Without JavaScript
- 10Tailwind Hamburger Menu Morphing to Close Icon Animation
- 11Tailwind Full Screen Mobile Navigation Overlay
- 12Tailwind Slide Out Sidebar Navigation / Off Canvas Menu
- 13Tailwind Responsive Navbar Mobile Hamburger Toggle Breakpoints
- 14Tailwind Minimalist Floating Hamburger Button
- 15Tailwind CSS Responsive Hamburger Menu Component
- 16Tailwind Rebuild of the Bootstrap 5 Navbar Hamburger Toggle
- 17Tailwind WCAG Accessible Hamburger Menu Example
- 18Pure Tailwind Dropdown Slide Down Mobile Hamburger Menu
- 19Simple 3-Line Tailwind Hamburger Icon Hover Effect
- 20Tailwind Full Screen Blur Overlay Hamburger Menu
- 21Tailwind Mobile Bottom Navigation Bar with Floating Hamburger Toggle
- 22Pure Tailwind Multi-Level Accordion Hamburger Dropdown Menu
- 23Flexbox Navbar Right-Aligned Tailwind Hamburger Icon
- 24Radial Ripple Expand Tailwind Hamburger Menu
- 25Sticky Tailwind Navbar Shrinking Hamburger Icon on Scroll
- 26Neumorphic Tailwind Hamburger Toggle Button
- 27Brutalist Design Bordered Tailwind Hamburger Menu
- 28Tailwind 3D Flip Canvas Mobile Hamburger Navigation
Tailwind Slide Out Sidebar Navigation / Off Canvas Menu
The workhorse app sidebar, structured the way real products need it: grouped sections, icon+label rows with an aria-current-driven indicator bar, shortcut hints, and a user card pinned to the bottom. The slide engine is demo 04's; what this demo teaches is interior architecture — and in utilities the active-state contract (aria-current:) is visible on the row itself.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="thb-12 w-full min-h-svh grid justify-center content-start bg-thb12-bg font-thb12-sans text-thb12-ink px-5 py-9" aria-label="Slide out sidebar navigation demo">
<div class="group relative w-[min(420px,88vw)] h-140 rounded-3xl overflow-hidden bg-thb12-bg border border-thb12-edge" style="--w:288px">
<header class="flex items-center gap-3 h-16 px-4 bg-white border-b border-thb12-edge">
<button type="button" aria-expanded="false" aria-controls="thb12-drawer" aria-label="Open navigation" class="group/btn grid place-items-center w-11 h-11 rounded-xl border-0 bg-transparent text-thb12-ink cursor-pointer transition-[background] duration-200 hover:bg-thb12-hover focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:outline-offset-2">
<span class="relative block w-4.75 h-3.25" aria-hidden="true">
<i class="absolute left-0 top-0 w-4.75 h-0.5 rounded-sm bg-current transition-[width] duration-250"></i>
<i class="absolute left-0 top-[5.5px] w-3.25 h-0.5 rounded-sm bg-current transition-[width] duration-250 group-hover/btn:w-4.75"></i>
<i class="absolute left-0 top-[11px] w-4 h-0.5 rounded-sm bg-current transition-[width] duration-250 group-hover/btn:w-4.75"></i>
</span>
</button>
<strong class="text-[15px] font-bold tracking-[-0.01em]">Tessera</strong>
<span class="text-[13px] text-thb12-mut" aria-hidden="true">/ Q3 planning</span>
</header>
<div class="grid gap-3.25 p-4.5" aria-hidden="true">
<div class="h-40 rounded-[15px] bg-linear-[145deg,oklch(0.92_0.02_190),oklch(0.88_0.035_165)]"></div>
<div class="flex gap-3">
<span class="flex-1 h-19.5 rounded-[13px] bg-white border border-thb12-edge"></span>
<span class="flex-1 h-19.5 rounded-[13px] bg-white border border-thb12-edge"></span>
</div>
<div class="h-2.75 w-[78%] rounded-full bg-thb12-line"></div>
<div class="h-2.75 w-[62%] rounded-full bg-thb12-line"></div>
</div>
<button type="button" tabindex="-1" aria-hidden="true" class="absolute inset-0 z-2 border-0 p-0 cursor-pointer opacity-0 invisible bg-[oklch(0.2_0.02_260/0.4)] [transition:opacity_.32s,visibility_0s_.32s] group-data-open:opacity-100 group-data-open:visible group-data-open:[transition:opacity_.32s,visibility_0s_0s]"></button>
<aside id="thb12-drawer" aria-label="Navigation" class="absolute inset-y-0 left-0 z-3 grid grid-rows-[auto_1fr_auto] w-[min(88%,var(--w))] bg-thb12-dark text-thb12-dink shadow-[24px_0_48px_-22px_oklch(0.15_0.03_262/0.6)] -translate-x-[102%] invisible [transition:translate_.42s_var(--ease-thb12),visibility_0s_.42s] group-data-open:translate-x-0 group-data-open:visible group-data-open:[transition:translate_.42s_var(--ease-thb12),visibility_0s_0s] motion-reduce:[transition:opacity_.15s,visibility_0s_.15s]">
<header class="flex items-center gap-2.5 pt-4.5 pb-3.5 pl-4.5 pr-3.5">
<span class="grid place-items-center w-7 h-7 rounded-[9px] bg-linear-[140deg,var(--color-thb12-acc),oklch(0.6_0.12_210)] text-[13px] font-extrabold text-thb12-dark" aria-hidden="true">T</span>
<b class="text-[15px] font-bold">Tessera</b>
<button type="button" aria-label="Close navigation" class="grid place-items-center ml-auto w-9.5 h-9.5 rounded-[10px] border border-thb12-dedge bg-transparent text-thb12-dmut cursor-pointer transition-[color,border-color] duration-200 hover:text-thb12-dink hover:border-thb12-dedge2 focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:outline-offset-2">
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg>
</button>
</header>
<nav aria-label="Primary" class="min-h-0 overflow-auto overscroll-contain px-3 pt-1.5 pb-3.5">
<div style="--g:0" class="[&+div]:mt-4.5">
<span class="block px-2.5 pb-1.75 font-thb12-mono text-[10px] font-bold tracking-[0.14em] uppercase text-thb12-dcap">Workspace</span>
<a href="#board" aria-current="page" style="--g:0" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 aria-current:text-thb12-dink aria-current:bg-[oklch(1_0_0/0.07)] group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<span class="absolute left-0.5 top-[20%] w-[3.5px] h-[60%] rounded-full bg-thb12-acc scale-y-0 transition-[scale] duration-250 aria-current:scale-y-100" aria-hidden="true"></span>
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round] aria-current:stroke-thb12-acc" aria-hidden="true"><rect x="3.5" y="3.5" width="7.5" height="10" rx="1.5"/><rect x="13" y="3.5" width="7.5" height="6" rx="1.5"/><rect x="13" y="11.5" width="7.5" height="9" rx="1.5"/><rect x="3.5" y="15.5" width="7.5" height="5" rx="1.5"/></svg>Board
<kbd class="ml-auto px-1.5 py-0.5 rounded-[5px] border border-thb12-dedge font-thb12-mono text-[10px] font-semibold text-thb12-dcap" aria-hidden="true">⌘1</kbd>
</a>
<a href="#timeline" style="--g:0" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M3.5 6h11M3.5 12h17M3.5 18h8"/><circle cx="18" cy="6" r="2"/><circle cx="15" cy="18" r="2"/></svg>Timeline
<kbd class="ml-auto px-1.5 py-0.5 rounded-[5px] border border-thb12-dedge font-thb12-mono text-[10px] font-semibold text-thb12-dcap" aria-hidden="true">⌘2</kbd>
</a>
<a href="#backlog" style="--g:0" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/><circle cx="4" cy="6" r="1.4"/><circle cx="4" cy="12" r="1.4"/><circle cx="4" cy="18" r="1.4"/></svg>Backlog
<b class="ml-auto px-1.75 py-0.5 rounded-full bg-thb12-acc text-thb12-dark text-[10.5px] font-bold">24</b>
</a>
</div>
<div style="--g:1" class="[&+div]:mt-4.5">
<span class="block px-2.5 pb-1.75 font-thb12-mono text-[10px] font-bold tracking-[0.14em] uppercase text-thb12-dcap">Library</span>
<a href="#docs" style="--g:1" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M5.5 4h9l4 4v12h-13Z"/><path d="M14 4v4.5h4.5"/></svg>Documents
</a>
<a href="#assets" style="--g:1" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><rect x="3.5" y="5" width="17" height="14" rx="2"/><circle cx="9" cy="10.5" r="1.8"/><path d="m3.5 16.5 5-4 4 3.5 3-2.5 5 3.5"/></svg>Assets
</a>
<a href="#reports" style="--g:1" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M5 20V9M12 20V4M19 20v-7"/></svg>Reports
</a>
</div>
<div style="--g:2">
<span class="block px-2.5 pb-1.75 font-thb12-mono text-[10px] font-bold tracking-[0.14em] uppercase text-thb12-dcap">Support</span>
<a href="#help" style="--g:2" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="12" cy="12" r="8.5"/><path d="M9.6 9.4a2.5 2.5 0 1 1 3.4 3l-1 .8v1.3M12 17.5v.01"/></svg>Help & docs
</a>
<a href="#invite" style="--g:2" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="10" cy="8.5" r="3.5"/><path d="M3.5 20a6.5 6.5 0 0 1 13 0M18 8v6M15 11h6"/></svg>Invite team
</a>
</div>
</nav>
<footer class="flex items-center gap-2.75 px-3.5 py-3.25 border-t border-thb12-dedge">
<span class="grid place-items-center w-8.5 h-8.5 rounded-full bg-linear-[140deg,oklch(0.75_0.12_90),var(--color-thb12-acc)] text-[11.5px] font-bold text-thb12-dark" aria-hidden="true">MK</span>
<span class="grid leading-tight">
<b class="text-[13px] font-bold">Mara Kade</b>
<small class="text-[11px] text-thb12-dmut">mara@tessera.app</small>
</span>
<button type="button" aria-label="Account options" class="ml-auto w-9 h-9 rounded-[9px] border-0 bg-transparent text-thb12-dmut text-base cursor-pointer transition-[background,color] duration-200 hover:bg-[oklch(1_0_0/0.07)] hover:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:outline-offset-2">⋯</button>
</footer>
</aside>
</div>
</section><section class="thb-12 w-full min-h-svh grid justify-center content-start bg-thb12-bg font-thb12-sans text-thb12-ink px-5 py-9" aria-label="Slide out sidebar navigation demo">
<div class="group relative w-[min(420px,88vw)] h-140 rounded-3xl overflow-hidden bg-thb12-bg border border-thb12-edge" style="--w:288px">
<header class="flex items-center gap-3 h-16 px-4 bg-white border-b border-thb12-edge">
<button type="button" aria-expanded="false" aria-controls="thb12-drawer" aria-label="Open navigation" class="group/btn grid place-items-center w-11 h-11 rounded-xl border-0 bg-transparent text-thb12-ink cursor-pointer transition-[background] duration-200 hover:bg-thb12-hover focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:outline-offset-2">
<span class="relative block w-4.75 h-3.25" aria-hidden="true">
<i class="absolute left-0 top-0 w-4.75 h-0.5 rounded-sm bg-current transition-[width] duration-250"></i>
<i class="absolute left-0 top-[5.5px] w-3.25 h-0.5 rounded-sm bg-current transition-[width] duration-250 group-hover/btn:w-4.75"></i>
<i class="absolute left-0 top-[11px] w-4 h-0.5 rounded-sm bg-current transition-[width] duration-250 group-hover/btn:w-4.75"></i>
</span>
</button>
<strong class="text-[15px] font-bold tracking-[-0.01em]">Tessera</strong>
<span class="text-[13px] text-thb12-mut" aria-hidden="true">/ Q3 planning</span>
</header>
<div class="grid gap-3.25 p-4.5" aria-hidden="true">
<div class="h-40 rounded-[15px] bg-linear-[145deg,oklch(0.92_0.02_190),oklch(0.88_0.035_165)]"></div>
<div class="flex gap-3">
<span class="flex-1 h-19.5 rounded-[13px] bg-white border border-thb12-edge"></span>
<span class="flex-1 h-19.5 rounded-[13px] bg-white border border-thb12-edge"></span>
</div>
<div class="h-2.75 w-[78%] rounded-full bg-thb12-line"></div>
<div class="h-2.75 w-[62%] rounded-full bg-thb12-line"></div>
</div>
<button type="button" tabindex="-1" aria-hidden="true" class="absolute inset-0 z-2 border-0 p-0 cursor-pointer opacity-0 invisible bg-[oklch(0.2_0.02_260/0.4)] [transition:opacity_.32s,visibility_0s_.32s] group-data-open:opacity-100 group-data-open:visible group-data-open:[transition:opacity_.32s,visibility_0s_0s]"></button>
<aside id="thb12-drawer" aria-label="Navigation" class="absolute inset-y-0 left-0 z-3 grid grid-rows-[auto_1fr_auto] w-[min(88%,var(--w))] bg-thb12-dark text-thb12-dink shadow-[24px_0_48px_-22px_oklch(0.15_0.03_262/0.6)] -translate-x-[102%] invisible [transition:translate_.42s_var(--ease-thb12),visibility_0s_.42s] group-data-open:translate-x-0 group-data-open:visible group-data-open:[transition:translate_.42s_var(--ease-thb12),visibility_0s_0s] motion-reduce:[transition:opacity_.15s,visibility_0s_.15s]">
<header class="flex items-center gap-2.5 pt-4.5 pb-3.5 pl-4.5 pr-3.5">
<span class="grid place-items-center w-7 h-7 rounded-[9px] bg-linear-[140deg,var(--color-thb12-acc),oklch(0.6_0.12_210)] text-[13px] font-extrabold text-thb12-dark" aria-hidden="true">T</span>
<b class="text-[15px] font-bold">Tessera</b>
<button type="button" aria-label="Close navigation" class="grid place-items-center ml-auto w-9.5 h-9.5 rounded-[10px] border border-thb12-dedge bg-transparent text-thb12-dmut cursor-pointer transition-[color,border-color] duration-200 hover:text-thb12-dink hover:border-thb12-dedge2 focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:outline-offset-2">
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg>
</button>
</header>
<nav aria-label="Primary" class="min-h-0 overflow-auto overscroll-contain px-3 pt-1.5 pb-3.5">
<div style="--g:0" class="[&+div]:mt-4.5">
<span class="block px-2.5 pb-1.75 font-thb12-mono text-[10px] font-bold tracking-[0.14em] uppercase text-thb12-dcap">Workspace</span>
<a href="#board" aria-current="page" style="--g:0" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 aria-current:text-thb12-dink aria-current:bg-[oklch(1_0_0/0.07)] group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<span class="absolute left-0.5 top-[20%] w-[3.5px] h-[60%] rounded-full bg-thb12-acc scale-y-0 transition-[scale] duration-250 aria-current:scale-y-100" aria-hidden="true"></span>
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round] aria-current:stroke-thb12-acc" aria-hidden="true"><rect x="3.5" y="3.5" width="7.5" height="10" rx="1.5"/><rect x="13" y="3.5" width="7.5" height="6" rx="1.5"/><rect x="13" y="11.5" width="7.5" height="9" rx="1.5"/><rect x="3.5" y="15.5" width="7.5" height="5" rx="1.5"/></svg>Board
<kbd class="ml-auto px-1.5 py-0.5 rounded-[5px] border border-thb12-dedge font-thb12-mono text-[10px] font-semibold text-thb12-dcap" aria-hidden="true">⌘1</kbd>
</a>
<a href="#timeline" style="--g:0" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M3.5 6h11M3.5 12h17M3.5 18h8"/><circle cx="18" cy="6" r="2"/><circle cx="15" cy="18" r="2"/></svg>Timeline
<kbd class="ml-auto px-1.5 py-0.5 rounded-[5px] border border-thb12-dedge font-thb12-mono text-[10px] font-semibold text-thb12-dcap" aria-hidden="true">⌘2</kbd>
</a>
<a href="#backlog" style="--g:0" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/><circle cx="4" cy="6" r="1.4"/><circle cx="4" cy="12" r="1.4"/><circle cx="4" cy="18" r="1.4"/></svg>Backlog
<b class="ml-auto px-1.75 py-0.5 rounded-full bg-thb12-acc text-thb12-dark text-[10.5px] font-bold">24</b>
</a>
</div>
<div style="--g:1" class="[&+div]:mt-4.5">
<span class="block px-2.5 pb-1.75 font-thb12-mono text-[10px] font-bold tracking-[0.14em] uppercase text-thb12-dcap">Library</span>
<a href="#docs" style="--g:1" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M5.5 4h9l4 4v12h-13Z"/><path d="M14 4v4.5h4.5"/></svg>Documents
</a>
<a href="#assets" style="--g:1" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><rect x="3.5" y="5" width="17" height="14" rx="2"/><circle cx="9" cy="10.5" r="1.8"/><path d="m3.5 16.5 5-4 4 3.5 3-2.5 5 3.5"/></svg>Assets
</a>
<a href="#reports" style="--g:1" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M5 20V9M12 20V4M19 20v-7"/></svg>Reports
</a>
</div>
<div style="--g:2">
<span class="block px-2.5 pb-1.75 font-thb12-mono text-[10px] font-bold tracking-[0.14em] uppercase text-thb12-dcap">Support</span>
<a href="#help" style="--g:2" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="12" cy="12" r="8.5"/><path d="M9.6 9.4a2.5 2.5 0 1 1 3.4 3l-1 .8v1.3M12 17.5v.01"/></svg>Help & docs
</a>
<a href="#invite" style="--g:2" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="10" cy="8.5" r="3.5"/><path d="M3.5 20a6.5 6.5 0 0 1 13 0M18 8v6M15 11h6"/></svg>Invite team
</a>
</div>
</nav>
<footer class="flex items-center gap-2.75 px-3.5 py-3.25 border-t border-thb12-dedge">
<span class="grid place-items-center w-8.5 h-8.5 rounded-full bg-linear-[140deg,oklch(0.75_0.12_90),var(--color-thb12-acc)] text-[11.5px] font-bold text-thb12-dark" aria-hidden="true">MK</span>
<span class="grid leading-tight">
<b class="text-[13px] font-bold">Mara Kade</b>
<small class="text-[11px] text-thb12-dmut">mara@tessera.app</small>
</span>
<button type="button" aria-label="Account options" class="ml-auto w-9 h-9 rounded-[9px] border-0 bg-transparent text-thb12-dmut text-base cursor-pointer transition-[background,color] duration-200 hover:bg-[oklch(1_0_0/0.07)] hover:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:outline-offset-2">⋯</button>
</footer>
</aside>
</div>
</section>@import "tailwindcss";
@theme {
--color-thb12-bg: #fafafc;
--color-thb12-bg: oklch(0.975 0.004 250);
--color-thb12-edge: #e3e4ea;
--color-thb12-edge: oklch(0.9 0.008 250);
--color-thb12-line: #e8e9ee;
--color-thb12-line: oklch(0.91 0.006 250);
--color-thb12-hover: #eff0f4;
--color-thb12-hover: oklch(0.95 0.006 250);
--color-thb12-ink: #33374a;
--color-thb12-ink: oklch(0.25 0.015 255);
--color-thb12-mut: #757a8b;
--color-thb12-mut: oklch(0.55 0.012 255);
--color-thb12-dark: #2a2d3d;
--color-thb12-dark: oklch(0.21 0.022 262);
--color-thb12-dedge: #43475c;
--color-thb12-dedge: oklch(0.3 0.025 262);
--color-thb12-dedge2: #62677f;
--color-thb12-dedge2: oklch(0.42 0.03 262);
--color-thb12-dink: #eaebf1;
--color-thb12-dink: oklch(0.93 0.008 260);
--color-thb12-dmut: #a1a5b6;
--color-thb12-dmut: oklch(0.66 0.02 260);
--color-thb12-dcap: #737890;
--color-thb12-dcap: oklch(0.52 0.02 262);
--color-thb12-acc: #4ecfc0;
--color-thb12-acc: oklch(0.73 0.14 175);
--font-thb12-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb12-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-thb12: cubic-bezier(0.32, 0.72, 0.28, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-12 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-thb12-bg: #fafafc;
--color-thb12-bg: oklch(0.975 0.004 250);
--color-thb12-edge: #e3e4ea;
--color-thb12-edge: oklch(0.9 0.008 250);
--color-thb12-line: #e8e9ee;
--color-thb12-line: oklch(0.91 0.006 250);
--color-thb12-hover: #eff0f4;
--color-thb12-hover: oklch(0.95 0.006 250);
--color-thb12-ink: #33374a;
--color-thb12-ink: oklch(0.25 0.015 255);
--color-thb12-mut: #757a8b;
--color-thb12-mut: oklch(0.55 0.012 255);
--color-thb12-dark: #2a2d3d;
--color-thb12-dark: oklch(0.21 0.022 262);
--color-thb12-dedge: #43475c;
--color-thb12-dedge: oklch(0.3 0.025 262);
--color-thb12-dedge2: #62677f;
--color-thb12-dedge2: oklch(0.42 0.03 262);
--color-thb12-dink: #eaebf1;
--color-thb12-dink: oklch(0.93 0.008 260);
--color-thb12-dmut: #a1a5b6;
--color-thb12-dmut: oklch(0.66 0.02 260);
--color-thb12-dcap: #737890;
--color-thb12-dcap: oklch(0.52 0.02 262);
--color-thb12-acc: #4ecfc0;
--color-thb12-acc: oklch(0.73 0.14 175);
--font-thb12-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb12-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-thb12: cubic-bezier(0.32, 0.72, 0.28, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-12 {
width: 100%;
min-height: 100svh;
}(function () {
document.querySelectorAll('.thb-12 > .group').forEach(function (stage) {
if (stage.dataset.bound) return; stage.dataset.bound = '1';
var btn = stage.querySelector('button[aria-controls="thb12-drawer"]'),
scrim = stage.querySelector('button[aria-hidden="true"]'),
close = stage.querySelector('button[aria-label="Close navigation"]');
function set(open) {
stage.toggleAttribute('data-open', open);
btn.setAttribute('aria-expanded', String(open));
if (open) close.focus(); else btn.focus();
}
btn.addEventListener('click', function () { set(true); });
scrim.addEventListener('click', function () { set(false); });
close.addEventListener('click', function () { set(false); });
stage.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && stage.hasAttribute('data-open')) set(false);
});
});
})();(function () {
document.querySelectorAll('.thb-12 > .group').forEach(function (stage) {
if (stage.dataset.bound) return; stage.dataset.bound = '1';
var btn = stage.querySelector('button[aria-controls="thb12-drawer"]'),
scrim = stage.querySelector('button[aria-hidden="true"]'),
close = stage.querySelector('button[aria-label="Close navigation"]');
function set(open) {
stage.toggleAttribute('data-open', open);
btn.setAttribute('aria-expanded', String(open));
if (open) close.focus(); else btn.focus();
}
btn.addEventListener('click', function () { set(true); });
scrim.addEventListener('click', function () { set(false); });
close.addEventListener('click', function () { set(false); });
stage.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && stage.hasAttribute('data-open')) set(false);
});
});
})();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-thb12-bg: #fafafc;
--color-thb12-bg: oklch(0.975 0.004 250);
--color-thb12-edge: #e3e4ea;
--color-thb12-edge: oklch(0.9 0.008 250);
--color-thb12-line: #e8e9ee;
--color-thb12-line: oklch(0.91 0.006 250);
--color-thb12-hover: #eff0f4;
--color-thb12-hover: oklch(0.95 0.006 250);
--color-thb12-ink: #33374a;
--color-thb12-ink: oklch(0.25 0.015 255);
--color-thb12-mut: #757a8b;
--color-thb12-mut: oklch(0.55 0.012 255);
--color-thb12-dark: #2a2d3d;
--color-thb12-dark: oklch(0.21 0.022 262);
--color-thb12-dedge: #43475c;
--color-thb12-dedge: oklch(0.3 0.025 262);
--color-thb12-dedge2: #62677f;
--color-thb12-dedge2: oklch(0.42 0.03 262);
--color-thb12-dink: #eaebf1;
--color-thb12-dink: oklch(0.93 0.008 260);
--color-thb12-dmut: #a1a5b6;
--color-thb12-dmut: oklch(0.66 0.02 260);
--color-thb12-dcap: #737890;
--color-thb12-dcap: oklch(0.52 0.02 262);
--color-thb12-acc: #4ecfc0;
--color-thb12-acc: oklch(0.73 0.14 175);
--font-thb12-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb12-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-thb12: cubic-bezier(0.32, 0.72, 0.28, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-12 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-thb12-bg: #fafafc;
--color-thb12-bg: oklch(0.975 0.004 250);
--color-thb12-edge: #e3e4ea;
--color-thb12-edge: oklch(0.9 0.008 250);
--color-thb12-line: #e8e9ee;
--color-thb12-line: oklch(0.91 0.006 250);
--color-thb12-hover: #eff0f4;
--color-thb12-hover: oklch(0.95 0.006 250);
--color-thb12-ink: #33374a;
--color-thb12-ink: oklch(0.25 0.015 255);
--color-thb12-mut: #757a8b;
--color-thb12-mut: oklch(0.55 0.012 255);
--color-thb12-dark: #2a2d3d;
--color-thb12-dark: oklch(0.21 0.022 262);
--color-thb12-dedge: #43475c;
--color-thb12-dedge: oklch(0.3 0.025 262);
--color-thb12-dedge2: #62677f;
--color-thb12-dedge2: oklch(0.42 0.03 262);
--color-thb12-dink: #eaebf1;
--color-thb12-dink: oklch(0.93 0.008 260);
--color-thb12-dmut: #a1a5b6;
--color-thb12-dmut: oklch(0.66 0.02 260);
--color-thb12-dcap: #737890;
--color-thb12-dcap: oklch(0.52 0.02 262);
--color-thb12-acc: #4ecfc0;
--color-thb12-acc: oklch(0.73 0.14 175);
--font-thb12-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb12-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-thb12: cubic-bezier(0.32, 0.72, 0.28, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-12 {
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=Space+Grotesk:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&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=Space+Grotesk:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
a[aria-current="page"]and its three dependent rules (tint, indicator, icon colour) collapse intoaria-current:utilities on the row and its children. The active-state contract is now visible where the row is written.- The indicator moves from a
::beforeto a realaria-hiddenspan, which is what lets it carryaria-current:scale-y-100directly — a pseudo-element cannot read an attribute variant of its own parent without a nested selector. - The per-group cascade keeps
--gbut drops the four-valuetransition-delaylist; one[transition-delay:calc(…)]utility does it. .group + .group{margin-top}has no variant form, so it becomes[&+div]:mt-4.5on each group — the clearest case in the collection of an adjacent-sibling rule staying awkward in utilities.- The dark drawer inside a light stage means two token families in one
@themeblock (thb12-*andthb12-d*). Utilities do not inherit a palette, so naming carries the distinction. - The burger's three unequal bars growing to equal width on hover uses a named group (
group/btn), keeping it independent of the stage'sgroup.
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 Slide Out Sidebar Navigation / Off Canvas Menu
Source: https://codefronts.com/navigation/tailwind-hamburger-menus/tailwind-slide-out-sidebar-navigation-off-canvas-menu/
The workhorse app sidebar, structured the way real products need it: grouped sections, icon+label rows with an aria-current-driven indicator bar, shortcut hints, and a user card pinned to the bottom. The slide engine is demo 04's; what this demo teaches is interior architecture — and in utilities the active-state contract (aria-current:) is visible on the row itself.
## HTML
```html
<section class="thb-12 w-full min-h-svh grid justify-center content-start bg-thb12-bg font-thb12-sans text-thb12-ink px-5 py-9" aria-label="Slide out sidebar navigation demo">
<div class="group relative w-[min(420px,88vw)] h-140 rounded-3xl overflow-hidden bg-thb12-bg border border-thb12-edge" style="--w:288px">
<header class="flex items-center gap-3 h-16 px-4 bg-white border-b border-thb12-edge">
<button type="button" aria-expanded="false" aria-controls="thb12-drawer" aria-label="Open navigation" class="group/btn grid place-items-center w-11 h-11 rounded-xl border-0 bg-transparent text-thb12-ink cursor-pointer transition-[background] duration-200 hover:bg-thb12-hover focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:outline-offset-2">
<span class="relative block w-4.75 h-3.25" aria-hidden="true">
<i class="absolute left-0 top-0 w-4.75 h-0.5 rounded-sm bg-current transition-[width] duration-250"></i>
<i class="absolute left-0 top-[5.5px] w-3.25 h-0.5 rounded-sm bg-current transition-[width] duration-250 group-hover/btn:w-4.75"></i>
<i class="absolute left-0 top-[11px] w-4 h-0.5 rounded-sm bg-current transition-[width] duration-250 group-hover/btn:w-4.75"></i>
</span>
</button>
<strong class="text-[15px] font-bold tracking-[-0.01em]">Tessera</strong>
<span class="text-[13px] text-thb12-mut" aria-hidden="true">/ Q3 planning</span>
</header>
<div class="grid gap-3.25 p-4.5" aria-hidden="true">
<div class="h-40 rounded-[15px] bg-linear-[145deg,oklch(0.92_0.02_190),oklch(0.88_0.035_165)]"></div>
<div class="flex gap-3">
<span class="flex-1 h-19.5 rounded-[13px] bg-white border border-thb12-edge"></span>
<span class="flex-1 h-19.5 rounded-[13px] bg-white border border-thb12-edge"></span>
</div>
<div class="h-2.75 w-[78%] rounded-full bg-thb12-line"></div>
<div class="h-2.75 w-[62%] rounded-full bg-thb12-line"></div>
</div>
<button type="button" tabindex="-1" aria-hidden="true" class="absolute inset-0 z-2 border-0 p-0 cursor-pointer opacity-0 invisible bg-[oklch(0.2_0.02_260/0.4)] [transition:opacity_.32s,visibility_0s_.32s] group-data-open:opacity-100 group-data-open:visible group-data-open:[transition:opacity_.32s,visibility_0s_0s]"></button>
<aside id="thb12-drawer" aria-label="Navigation" class="absolute inset-y-0 left-0 z-3 grid grid-rows-[auto_1fr_auto] w-[min(88%,var(--w))] bg-thb12-dark text-thb12-dink shadow-[24px_0_48px_-22px_oklch(0.15_0.03_262/0.6)] -translate-x-[102%] invisible [transition:translate_.42s_var(--ease-thb12),visibility_0s_.42s] group-data-open:translate-x-0 group-data-open:visible group-data-open:[transition:translate_.42s_var(--ease-thb12),visibility_0s_0s] motion-reduce:[transition:opacity_.15s,visibility_0s_.15s]">
<header class="flex items-center gap-2.5 pt-4.5 pb-3.5 pl-4.5 pr-3.5">
<span class="grid place-items-center w-7 h-7 rounded-[9px] bg-linear-[140deg,var(--color-thb12-acc),oklch(0.6_0.12_210)] text-[13px] font-extrabold text-thb12-dark" aria-hidden="true">T</span>
<b class="text-[15px] font-bold">Tessera</b>
<button type="button" aria-label="Close navigation" class="grid place-items-center ml-auto w-9.5 h-9.5 rounded-[10px] border border-thb12-dedge bg-transparent text-thb12-dmut cursor-pointer transition-[color,border-color] duration-200 hover:text-thb12-dink hover:border-thb12-dedge2 focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:outline-offset-2">
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg>
</button>
</header>
<nav aria-label="Primary" class="min-h-0 overflow-auto overscroll-contain px-3 pt-1.5 pb-3.5">
<div style="--g:0" class="[&+div]:mt-4.5">
<span class="block px-2.5 pb-1.75 font-thb12-mono text-[10px] font-bold tracking-[0.14em] uppercase text-thb12-dcap">Workspace</span>
<a href="#board" aria-current="page" style="--g:0" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 aria-current:text-thb12-dink aria-current:bg-[oklch(1_0_0/0.07)] group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<span class="absolute left-0.5 top-[20%] w-[3.5px] h-[60%] rounded-full bg-thb12-acc scale-y-0 transition-[scale] duration-250 aria-current:scale-y-100" aria-hidden="true"></span>
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round] aria-current:stroke-thb12-acc" aria-hidden="true"><rect x="3.5" y="3.5" width="7.5" height="10" rx="1.5"/><rect x="13" y="3.5" width="7.5" height="6" rx="1.5"/><rect x="13" y="11.5" width="7.5" height="9" rx="1.5"/><rect x="3.5" y="15.5" width="7.5" height="5" rx="1.5"/></svg>Board
<kbd class="ml-auto px-1.5 py-0.5 rounded-[5px] border border-thb12-dedge font-thb12-mono text-[10px] font-semibold text-thb12-dcap" aria-hidden="true">⌘1</kbd>
</a>
<a href="#timeline" style="--g:0" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M3.5 6h11M3.5 12h17M3.5 18h8"/><circle cx="18" cy="6" r="2"/><circle cx="15" cy="18" r="2"/></svg>Timeline
<kbd class="ml-auto px-1.5 py-0.5 rounded-[5px] border border-thb12-dedge font-thb12-mono text-[10px] font-semibold text-thb12-dcap" aria-hidden="true">⌘2</kbd>
</a>
<a href="#backlog" style="--g:0" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/><circle cx="4" cy="6" r="1.4"/><circle cx="4" cy="12" r="1.4"/><circle cx="4" cy="18" r="1.4"/></svg>Backlog
<b class="ml-auto px-1.75 py-0.5 rounded-full bg-thb12-acc text-thb12-dark text-[10.5px] font-bold">24</b>
</a>
</div>
<div style="--g:1" class="[&+div]:mt-4.5">
<span class="block px-2.5 pb-1.75 font-thb12-mono text-[10px] font-bold tracking-[0.14em] uppercase text-thb12-dcap">Library</span>
<a href="#docs" style="--g:1" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M5.5 4h9l4 4v12h-13Z"/><path d="M14 4v4.5h4.5"/></svg>Documents
</a>
<a href="#assets" style="--g:1" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><rect x="3.5" y="5" width="17" height="14" rx="2"/><circle cx="9" cy="10.5" r="1.8"/><path d="m3.5 16.5 5-4 4 3.5 3-2.5 5 3.5"/></svg>Assets
</a>
<a href="#reports" style="--g:1" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M5 20V9M12 20V4M19 20v-7"/></svg>Reports
</a>
</div>
<div style="--g:2">
<span class="block px-2.5 pb-1.75 font-thb12-mono text-[10px] font-bold tracking-[0.14em] uppercase text-thb12-dcap">Support</span>
<a href="#help" style="--g:2" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="12" cy="12" r="8.5"/><path d="M9.6 9.4a2.5 2.5 0 1 1 3.4 3l-1 .8v1.3M12 17.5v.01"/></svg>Help & docs
</a>
<a href="#invite" style="--g:2" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="10" cy="8.5" r="3.5"/><path d="M3.5 20a6.5 6.5 0 0 1 13 0M18 8v6M15 11h6"/></svg>Invite team
</a>
</div>
</nav>
<footer class="flex items-center gap-2.75 px-3.5 py-3.25 border-t border-thb12-dedge">
<span class="grid place-items-center w-8.5 h-8.5 rounded-full bg-linear-[140deg,oklch(0.75_0.12_90),var(--color-thb12-acc)] text-[11.5px] font-bold text-thb12-dark" aria-hidden="true">MK</span>
<span class="grid leading-tight">
<b class="text-[13px] font-bold">Mara Kade</b>
<small class="text-[11px] text-thb12-dmut">mara@tessera.app</small>
</span>
<button type="button" aria-label="Account options" class="ml-auto w-9 h-9 rounded-[9px] border-0 bg-transparent text-thb12-dmut text-base cursor-pointer transition-[background,color] duration-200 hover:bg-[oklch(1_0_0/0.07)] hover:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:outline-offset-2">⋯</button>
</footer>
</aside>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-thb12-bg: #fafafc;
--color-thb12-bg: oklch(0.975 0.004 250);
--color-thb12-edge: #e3e4ea;
--color-thb12-edge: oklch(0.9 0.008 250);
--color-thb12-line: #e8e9ee;
--color-thb12-line: oklch(0.91 0.006 250);
--color-thb12-hover: #eff0f4;
--color-thb12-hover: oklch(0.95 0.006 250);
--color-thb12-ink: #33374a;
--color-thb12-ink: oklch(0.25 0.015 255);
--color-thb12-mut: #757a8b;
--color-thb12-mut: oklch(0.55 0.012 255);
--color-thb12-dark: #2a2d3d;
--color-thb12-dark: oklch(0.21 0.022 262);
--color-thb12-dedge: #43475c;
--color-thb12-dedge: oklch(0.3 0.025 262);
--color-thb12-dedge2: #62677f;
--color-thb12-dedge2: oklch(0.42 0.03 262);
--color-thb12-dink: #eaebf1;
--color-thb12-dink: oklch(0.93 0.008 260);
--color-thb12-dmut: #a1a5b6;
--color-thb12-dmut: oklch(0.66 0.02 260);
--color-thb12-dcap: #737890;
--color-thb12-dcap: oklch(0.52 0.02 262);
--color-thb12-acc: #4ecfc0;
--color-thb12-acc: oklch(0.73 0.14 175);
--font-thb12-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb12-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-thb12: cubic-bezier(0.32, 0.72, 0.28, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-12 {
width: 100%;
min-height: 100svh;
}
```
## JavaScript
```js
(function () {
document.querySelectorAll('.thb-12 > .group').forEach(function (stage) {
if (stage.dataset.bound) return; stage.dataset.bound = '1';
var btn = stage.querySelector('button[aria-controls="thb12-drawer"]'),
scrim = stage.querySelector('button[aria-hidden="true"]'),
close = stage.querySelector('button[aria-label="Close navigation"]');
function set(open) {
stage.toggleAttribute('data-open', open);
btn.setAttribute('aria-expanded', String(open));
if (open) close.focus(); else btn.focus();
}
btn.addEventListener('click', function () { set(true); });
scrim.addEventListener('click', function () { set(false); });
close.addEventListener('click', function () { set(false); });
stage.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && stage.hasAttribute('data-open')) set(false);
});
});
})();
```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 Slide Out Sidebar Navigation / Off Canvas Menu
Source: https://codefronts.com/navigation/tailwind-hamburger-menus/tailwind-slide-out-sidebar-navigation-off-canvas-menu/
The workhorse app sidebar, structured the way real products need it: grouped sections, icon+label rows with an aria-current-driven indicator bar, shortcut hints, and a user card pinned to the bottom. The slide engine is demo 04's; what this demo teaches is interior architecture — and in utilities the active-state contract (aria-current:) is visible on the row itself.
## HTML
```html
<section class="thb-12 w-full min-h-svh grid justify-center content-start bg-thb12-bg font-thb12-sans text-thb12-ink px-5 py-9" aria-label="Slide out sidebar navigation demo">
<div class="group relative w-[min(420px,88vw)] h-140 rounded-3xl overflow-hidden bg-thb12-bg border border-thb12-edge" style="--w:288px">
<header class="flex items-center gap-3 h-16 px-4 bg-white border-b border-thb12-edge">
<button type="button" aria-expanded="false" aria-controls="thb12-drawer" aria-label="Open navigation" class="group/btn grid place-items-center w-11 h-11 rounded-xl border-0 bg-transparent text-thb12-ink cursor-pointer transition-[background] duration-200 hover:bg-thb12-hover focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:outline-offset-2">
<span class="relative block w-4.75 h-3.25" aria-hidden="true">
<i class="absolute left-0 top-0 w-4.75 h-0.5 rounded-sm bg-current transition-[width] duration-250"></i>
<i class="absolute left-0 top-[5.5px] w-3.25 h-0.5 rounded-sm bg-current transition-[width] duration-250 group-hover/btn:w-4.75"></i>
<i class="absolute left-0 top-[11px] w-4 h-0.5 rounded-sm bg-current transition-[width] duration-250 group-hover/btn:w-4.75"></i>
</span>
</button>
<strong class="text-[15px] font-bold tracking-[-0.01em]">Tessera</strong>
<span class="text-[13px] text-thb12-mut" aria-hidden="true">/ Q3 planning</span>
</header>
<div class="grid gap-3.25 p-4.5" aria-hidden="true">
<div class="h-40 rounded-[15px] bg-linear-[145deg,oklch(0.92_0.02_190),oklch(0.88_0.035_165)]"></div>
<div class="flex gap-3">
<span class="flex-1 h-19.5 rounded-[13px] bg-white border border-thb12-edge"></span>
<span class="flex-1 h-19.5 rounded-[13px] bg-white border border-thb12-edge"></span>
</div>
<div class="h-2.75 w-[78%] rounded-full bg-thb12-line"></div>
<div class="h-2.75 w-[62%] rounded-full bg-thb12-line"></div>
</div>
<button type="button" tabindex="-1" aria-hidden="true" class="absolute inset-0 z-2 border-0 p-0 cursor-pointer opacity-0 invisible bg-[oklch(0.2_0.02_260/0.4)] [transition:opacity_.32s,visibility_0s_.32s] group-data-open:opacity-100 group-data-open:visible group-data-open:[transition:opacity_.32s,visibility_0s_0s]"></button>
<aside id="thb12-drawer" aria-label="Navigation" class="absolute inset-y-0 left-0 z-3 grid grid-rows-[auto_1fr_auto] w-[min(88%,var(--w))] bg-thb12-dark text-thb12-dink shadow-[24px_0_48px_-22px_oklch(0.15_0.03_262/0.6)] -translate-x-[102%] invisible [transition:translate_.42s_var(--ease-thb12),visibility_0s_.42s] group-data-open:translate-x-0 group-data-open:visible group-data-open:[transition:translate_.42s_var(--ease-thb12),visibility_0s_0s] motion-reduce:[transition:opacity_.15s,visibility_0s_.15s]">
<header class="flex items-center gap-2.5 pt-4.5 pb-3.5 pl-4.5 pr-3.5">
<span class="grid place-items-center w-7 h-7 rounded-[9px] bg-linear-[140deg,var(--color-thb12-acc),oklch(0.6_0.12_210)] text-[13px] font-extrabold text-thb12-dark" aria-hidden="true">T</span>
<b class="text-[15px] font-bold">Tessera</b>
<button type="button" aria-label="Close navigation" class="grid place-items-center ml-auto w-9.5 h-9.5 rounded-[10px] border border-thb12-dedge bg-transparent text-thb12-dmut cursor-pointer transition-[color,border-color] duration-200 hover:text-thb12-dink hover:border-thb12-dedge2 focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:outline-offset-2">
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg>
</button>
</header>
<nav aria-label="Primary" class="min-h-0 overflow-auto overscroll-contain px-3 pt-1.5 pb-3.5">
<div style="--g:0" class="[&+div]:mt-4.5">
<span class="block px-2.5 pb-1.75 font-thb12-mono text-[10px] font-bold tracking-[0.14em] uppercase text-thb12-dcap">Workspace</span>
<a href="#board" aria-current="page" style="--g:0" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 aria-current:text-thb12-dink aria-current:bg-[oklch(1_0_0/0.07)] group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<span class="absolute left-0.5 top-[20%] w-[3.5px] h-[60%] rounded-full bg-thb12-acc scale-y-0 transition-[scale] duration-250 aria-current:scale-y-100" aria-hidden="true"></span>
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round] aria-current:stroke-thb12-acc" aria-hidden="true"><rect x="3.5" y="3.5" width="7.5" height="10" rx="1.5"/><rect x="13" y="3.5" width="7.5" height="6" rx="1.5"/><rect x="13" y="11.5" width="7.5" height="9" rx="1.5"/><rect x="3.5" y="15.5" width="7.5" height="5" rx="1.5"/></svg>Board
<kbd class="ml-auto px-1.5 py-0.5 rounded-[5px] border border-thb12-dedge font-thb12-mono text-[10px] font-semibold text-thb12-dcap" aria-hidden="true">⌘1</kbd>
</a>
<a href="#timeline" style="--g:0" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M3.5 6h11M3.5 12h17M3.5 18h8"/><circle cx="18" cy="6" r="2"/><circle cx="15" cy="18" r="2"/></svg>Timeline
<kbd class="ml-auto px-1.5 py-0.5 rounded-[5px] border border-thb12-dedge font-thb12-mono text-[10px] font-semibold text-thb12-dcap" aria-hidden="true">⌘2</kbd>
</a>
<a href="#backlog" style="--g:0" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/><circle cx="4" cy="6" r="1.4"/><circle cx="4" cy="12" r="1.4"/><circle cx="4" cy="18" r="1.4"/></svg>Backlog
<b class="ml-auto px-1.75 py-0.5 rounded-full bg-thb12-acc text-thb12-dark text-[10.5px] font-bold">24</b>
</a>
</div>
<div style="--g:1" class="[&+div]:mt-4.5">
<span class="block px-2.5 pb-1.75 font-thb12-mono text-[10px] font-bold tracking-[0.14em] uppercase text-thb12-dcap">Library</span>
<a href="#docs" style="--g:1" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M5.5 4h9l4 4v12h-13Z"/><path d="M14 4v4.5h4.5"/></svg>Documents
</a>
<a href="#assets" style="--g:1" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><rect x="3.5" y="5" width="17" height="14" rx="2"/><circle cx="9" cy="10.5" r="1.8"/><path d="m3.5 16.5 5-4 4 3.5 3-2.5 5 3.5"/></svg>Assets
</a>
<a href="#reports" style="--g:1" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M5 20V9M12 20V4M19 20v-7"/></svg>Reports
</a>
</div>
<div style="--g:2">
<span class="block px-2.5 pb-1.75 font-thb12-mono text-[10px] font-bold tracking-[0.14em] uppercase text-thb12-dcap">Support</span>
<a href="#help" style="--g:2" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="12" cy="12" r="8.5"/><path d="M9.6 9.4a2.5 2.5 0 1 1 3.4 3l-1 .8v1.3M12 17.5v.01"/></svg>Help & docs
</a>
<a href="#invite" style="--g:2" class="relative flex items-center gap-3 min-h-11 pl-3.5 pr-2.5 rounded-[10px] no-underline text-[13.5px] font-semibold text-thb12-dmut opacity-0 -translate-x-3.5 transition-[background,color,opacity,translate] duration-350 ease-thb12 hover:bg-[oklch(1_0_0/0.06)] hover:text-thb12-dink focus-visible:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:-outline-offset-2 group-data-open:opacity-100 group-data-open:translate-x-0 group-data-open:[transition-delay:calc(100ms+var(--g)*60ms)] motion-reduce:translate-x-0 motion-reduce:duration-150">
<svg viewBox="0 0 24 24" class="w-4.5 h-4.5 shrink-0 fill-none stroke-current stroke-[1.7] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="10" cy="8.5" r="3.5"/><path d="M3.5 20a6.5 6.5 0 0 1 13 0M18 8v6M15 11h6"/></svg>Invite team
</a>
</div>
</nav>
<footer class="flex items-center gap-2.75 px-3.5 py-3.25 border-t border-thb12-dedge">
<span class="grid place-items-center w-8.5 h-8.5 rounded-full bg-linear-[140deg,oklch(0.75_0.12_90),var(--color-thb12-acc)] text-[11.5px] font-bold text-thb12-dark" aria-hidden="true">MK</span>
<span class="grid leading-tight">
<b class="text-[13px] font-bold">Mara Kade</b>
<small class="text-[11px] text-thb12-dmut">mara@tessera.app</small>
</span>
<button type="button" aria-label="Account options" class="ml-auto w-9 h-9 rounded-[9px] border-0 bg-transparent text-thb12-dmut text-base cursor-pointer transition-[background,color] duration-200 hover:bg-[oklch(1_0_0/0.07)] hover:text-thb12-dink focus-visible:outline-2 focus-visible:outline-thb12-acc focus-visible:outline-offset-2">⋯</button>
</footer>
</aside>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-thb12-bg: #fafafc;
--color-thb12-bg: oklch(0.975 0.004 250);
--color-thb12-edge: #e3e4ea;
--color-thb12-edge: oklch(0.9 0.008 250);
--color-thb12-line: #e8e9ee;
--color-thb12-line: oklch(0.91 0.006 250);
--color-thb12-hover: #eff0f4;
--color-thb12-hover: oklch(0.95 0.006 250);
--color-thb12-ink: #33374a;
--color-thb12-ink: oklch(0.25 0.015 255);
--color-thb12-mut: #757a8b;
--color-thb12-mut: oklch(0.55 0.012 255);
--color-thb12-dark: #2a2d3d;
--color-thb12-dark: oklch(0.21 0.022 262);
--color-thb12-dedge: #43475c;
--color-thb12-dedge: oklch(0.3 0.025 262);
--color-thb12-dedge2: #62677f;
--color-thb12-dedge2: oklch(0.42 0.03 262);
--color-thb12-dink: #eaebf1;
--color-thb12-dink: oklch(0.93 0.008 260);
--color-thb12-dmut: #a1a5b6;
--color-thb12-dmut: oklch(0.66 0.02 260);
--color-thb12-dcap: #737890;
--color-thb12-dcap: oklch(0.52 0.02 262);
--color-thb12-acc: #4ecfc0;
--color-thb12-acc: oklch(0.73 0.14 175);
--font-thb12-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb12-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-thb12: cubic-bezier(0.32, 0.72, 0.28, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-12 {
width: 100%;
min-height: 100svh;
}
```
## JavaScript
```js
(function () {
document.querySelectorAll('.thb-12 > .group').forEach(function (stage) {
if (stage.dataset.bound) return; stage.dataset.bound = '1';
var btn = stage.querySelector('button[aria-controls="thb12-drawer"]'),
scrim = stage.querySelector('button[aria-hidden="true"]'),
close = stage.querySelector('button[aria-label="Close navigation"]');
function set(open) {
stage.toggleAttribute('data-open', open);
btn.setAttribute('aria-expanded', String(open));
if (open) close.focus(); else btn.focus();
}
btn.addEventListener('click', function () { set(true); });
scrim.addEventListener('click', function () { set(false); });
close.addEventListener('click', function () { set(false); });
stage.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && stage.hasAttribute('data-open')) set(false);
});
});
})();
```How this works
The active row is an attribute, not a class.
<a href="#board" aria-current="page"
class="text-thb12-dmut aria-current:text-thb12-dink
aria-current:bg-[oklch(1_0_0/0.07)]">
<span class="scale-y-0 aria-current:scale-y-100 …"></span>
<svg class="aria-current:stroke-thb12-acc …">…</svg>Board
</a>Move the attribute and the indicator, the tint and the icon colour all follow. No class shuffling, and no possibility of the highlight disagreeing with what a screen reader announces.
Three structural decisions a drawer needs up front. A three-row grid so only the middle scrolls; min-h-0 so that middle row can actually shrink; and rhythm between groups so sections stay readable as the product grows. All three are utilities on the elements themselves.
The cascade is per group, not per row. Rows inside a section share their parent's --g, so a section arrives as a unit — cheaper to author than per-row indices and visually calmer.
Shortcut hints are <kbd> and aria-hidden. Visual users get density; screen-reader users are not told “Projects, command 2” as if the link name were longer. That asymmetry is correct, not lazy.
Make it yours
- Add a section: copy one group
<div>and give it the next--g. The cascade, scrolling and spacing are automatic. - Active page: move
aria-current="page". Nothing else changes. - Width:
w-[min(88%,var(--w))]with--w:288px. Under 260px real product names truncate; over 320px steals canvas on 13-inch laptops. - The ellipsis button in the user card is a real button already wired for focus — mount your account menu there.
- Retheme the drawer through the
thb12-d*tokens; the stage keeps its own light set.
Gotchas — read before shipping
- The scrim and drawer must be SIBLINGS. A scrim wrapping the drawer captures the drawer's own clicks and “close on outside click” becomes “close on any click”.
- Only ONE element per nav may carry
aria-current="page". The variant will happily style several, and screen readers will report a confused page. min-h-0on the scrolling container, again — without it a long section pushes the pinned user card out of view.- The
<kbd>hints must stayaria-hidden, or they are read as part of each link's name. scale-y-0needs a transform origin to grow from the middle as intended; the default centre is right here, but a top-anchored indicator needsorigin-top.- Adjacent-sibling spacing has no variant.
[&+div]:mt-4.5is the honest form — do not fake it with a wrappergapunless every group is the same shape.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Nothing exotic: grid rows, translate transitions and attribute selectors. The floor quoted here is Tailwind v4's own generated sheet.