30 Tailwind CSS Sidebar Layouts
10 / 30

All 30 in 30 Tailwind CSS Sidebar Layouts

  1. 01Tailwind SaaS Dashboard Sidebar with Native Dark Mode Switch
  2. 02Tailwind Collapsible Admin Sidebar with Icon Tooltips
  3. 03Tailwind Documentation Sidebar with Multi-Level :target Tree
  4. 04Tailwind Responsive Sidebar to Bottom Navigation Switch Layout
  5. 05Tailwind Analytics Sidebar with CSS Subgrid Column Alignment
  6. 06Tailwind Animated Collapsible Sidebar with Icon-Only Mode
  7. 07Tailwind Sticky Sidebar Layout with Scroll-Driven Progress Bar
  8. 08Tailwind Pure CSS Collapsible Sidebar Drawer without JavaScript
  9. 09Tailwind Fixed Sidebar Layout with Independently Scrolling Content
  10. 10Tailwind Off-Canvas Navigation Drawer with the Popover API
  11. 11Tailwind Right-Aligned Sidebar Layout for Inspector Panels
  12. 12Tailwind Two-Pane Webmail Layout with List and Reading Pane
  13. 13Tailwind Dual Sidebar App Shell with Left Nav and Context Panel
  14. 14Tailwind Mini Icon Rail Sidebar with Anchor-Positioned Active Pill
  15. 15Tailwind E-Commerce Filter Sidebar with Native Details Accordion
  16. 16Tailwind Floating Glassmorphism Sidebar with Gradient Hairline
  17. 17Tailwind Sidebar Menu with Pinned User Profile Footer
  18. 18Tailwind Push-Content Sidebar Drawer with GPU Transitions
  19. 19Tailwind Off-Canvas Cart Overlay with Blurred Backdrop Scrim
  20. 20Tailwind Sidebar with Persistent Collapsed State in localStorage
  21. 21Tailwind Sidebar with Sticky Search Bar Inside the Rail
  22. 22Tailwind Section-Grouped Sidebar Navigation with Header Labels
  23. 23Tailwind Sliding Active Indicator Sidebar with CSS :target
  24. 24Tailwind Multi-Level Nested Tree Sidebar with Details Branches
  25. 25Tailwind Tabbed Sidebar with Radio-Driven Panel Switching
  26. 26Tailwind Onboarding Wizard Sidebar with Step Progress Tracking
  27. 27Tailwind Resizable Sidebar Layout Using the CSS Resize Property
  28. 28Tailwind Three-Pane Workspace Shell with Icon Rail and List
  29. 29Tailwind Adaptive Sidebar Layout Using CSS Container Queries
  30. 30Tailwind Frosted Glass Floating Sidebar with Animated Aurora
Pure TailwindMIT licensed

Tailwind Off-Canvas Navigation Drawer with the Popover API

The off-canvas drawer rebuilt on the platform's newest primitive. popover="auto" ships Esc-to-close, light dismissal, focus return and top-layer rendering; v4 contributes real variants for the hard part — starting: for the entry animation and an open: variant for the resting state.

Live Demo
Plain CSSTailwind v4
Try it

Mosaic by Cruip: a complete Tailwind admin dashboard template in HTML, React, Next.js and Vue · $69

Cruip · Mosaic by Cruip · Affiliate link

View Mosaic dashboard

The code

<section class="tsl-10 w-full h-svh min-h-svh grid grid-rows-[auto_1fr] font-tsl10-sans bg-tsl10-bg text-tsl10-ink">
  <header class="flex items-center gap-4 h-[54px] px-4 bg-tsl10-panel border-b border-tsl10-line text-[15px] font-semibold">
    <button popovertarget="tsl10-d" class="inline-flex items-center gap-2 min-h-11 px-3.5 rounded-[10px] border border-tsl10-line bg-transparent text-tsl10-ink text-[13.5px] font-medium cursor-pointer transition-colors duration-200 hover:bg-tsl10-hover focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[17px] h-[17px] fill-none stroke-current stroke-[1.9] [stroke-linecap:round]"><path d="M4 6h16M4 12h16M4 18h16"/></svg>Menu</button>
    <strong>Waypoint</strong>
  </header>
  <aside popover="auto" id="tsl10-d" aria-label="Navigation drawer" class="fixed inset-y-0 left-0 right-auto w-[min(300px,85vw)] h-full max-h-none m-0 p-4 border-0 border-r border-tsl10-line bg-tsl10-panel text-tsl10-ink -translate-x-full open:translate-x-0 starting:open:-translate-x-full transition-[translate,display,overlay] duration-[320ms] ease-[cubic-bezier(.4,0,.2,1)] [transition-behavior:allow-discrete] backdrop:bg-[oklch(0_0_0/0.45)] backdrop:backdrop-blur-[3px] backdrop:opacity-0 backdrop:open:opacity-100 backdrop:starting:open:opacity-0 backdrop:transition-[opacity,display,overlay] backdrop:duration-[320ms] backdrop:[transition-behavior:allow-discrete] motion-reduce:transition-none">
    <div class="flex items-center justify-between px-1.5 pt-1 pb-4 text-[15px] font-bold">
      <strong>Waypoint</strong>
      <button popovertarget="tsl10-d" popovertargetaction="hide" aria-label="Close menu" class="grid place-items-center w-11 h-11 rounded-[9px] border-0 bg-transparent text-tsl10-mut cursor-pointer transition-colors hover:text-tsl10-ink hover:bg-tsl10-hover focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[17px] h-[17px] fill-none stroke-current stroke-[1.9] [stroke-linecap:round]"><path d="M6 6l12 12M18 6 6 18"/></svg></button>
    </div>
    <nav class="flex flex-col gap-0.5">
      <a href="#" aria-current="page" class="flex items-center min-h-11 px-3.5 rounded-[10px] text-sm font-medium text-tsl10-mut no-underline transition-colors duration-200 hover:text-tsl10-ink hover:bg-tsl10-hover aria-[current=page]:text-tsl10-accent aria-[current=page]:bg-tsl10-tint focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2">Trips</a>
      <a href="#" class="flex items-center min-h-11 px-3.5 rounded-[10px] text-sm font-medium text-tsl10-mut no-underline transition-colors duration-200 hover:text-tsl10-ink hover:bg-tsl10-hover focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2">Explore</a>
      <a href="#" class="flex items-center min-h-11 px-3.5 rounded-[10px] text-sm font-medium text-tsl10-mut no-underline transition-colors duration-200 hover:text-tsl10-ink hover:bg-tsl10-hover focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2">Saved places</a>
      <a href="#" class="flex items-center min-h-11 px-3.5 rounded-[10px] text-sm font-medium text-tsl10-mut no-underline transition-colors duration-200 hover:text-tsl10-ink hover:bg-tsl10-hover focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2">Offline maps</a>
      <a href="#" class="flex items-center min-h-11 px-3.5 rounded-[10px] text-sm font-medium text-tsl10-mut no-underline transition-colors duration-200 hover:text-tsl10-ink hover:bg-tsl10-hover focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2">Account</a>
    </nav>
  </aside>
  <main class="p-7 overflow-y-auto [scrollbar-gutter:stable]">
    <h1 class="text-[22px] font-semibold leading-tight mb-2.5">Trips</h1>
    <p class="max-w-[48ch] text-sm leading-relaxed text-tsl10-mut text-pretty">Open the drawer, then press <kbd class="font-tsl10-mono text-[11px] font-semibold px-1.5 py-0.5 rounded-[5px] border border-tsl10-line bg-tsl10-hover">Esc</kbd> or click outside &mdash; dismissal, focus return and the blurred backdrop are all native Popover API behaviour. No script.</p>
  </main>
</section>
@import "tailwindcss";

@theme {
  --color-tsl10-bg: #111a16;
  --color-tsl10-bg: oklch(0.14 0.015 165);
  --color-tsl10-panel: #17221d;
  --color-tsl10-panel: oklch(0.18 0.02 165);
  --color-tsl10-ink: #e8efea;
  --color-tsl10-ink: oklch(0.94 0.005 165);
  --color-tsl10-mut: #93a099;
  --color-tsl10-mut: oklch(0.65 0.012 165);
  --color-tsl10-line: #35423b;
  --color-tsl10-line: oklch(0.29 0.02 165);
  --color-tsl10-accent: #5fdcae;
  --color-tsl10-accent: oklch(0.78 0.15 160);
  --color-tsl10-hover: rgb(232 239 234 / 0.07);
  --color-tsl10-hover: color-mix(in oklab, oklch(0.94 0.005 165) 7%, transparent);
  --color-tsl10-tint: rgb(95 220 174 / 0.14);
  --color-tsl10-tint: color-mix(in oklab, oklch(0.78 0.15 160) 14%, transparent);
  --font-tsl10-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tsl10-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tsl-10 {
  width: 100%;
  height: 100svh;
  min-height: 100svh;
  color-scheme: dark;
}
/* A popover's closed state is :not(:popover-open), and v4 has no negated
   open variant — two lines of CSS are cheaper than an arbitrary variant here. */

.tsl-10 [popover]:not(:popover-open) {
  display: none;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-tsl10-bg: #111a16;
  --color-tsl10-bg: oklch(0.14 0.015 165);
  --color-tsl10-panel: #17221d;
  --color-tsl10-panel: oklch(0.18 0.02 165);
  --color-tsl10-ink: #e8efea;
  --color-tsl10-ink: oklch(0.94 0.005 165);
  --color-tsl10-mut: #93a099;
  --color-tsl10-mut: oklch(0.65 0.012 165);
  --color-tsl10-line: #35423b;
  --color-tsl10-line: oklch(0.29 0.02 165);
  --color-tsl10-accent: #5fdcae;
  --color-tsl10-accent: oklch(0.78 0.15 160);
  --color-tsl10-hover: rgb(232 239 234 / 0.07);
  --color-tsl10-hover: color-mix(in oklab, oklch(0.94 0.005 165) 7%, transparent);
  --color-tsl10-tint: rgb(95 220 174 / 0.14);
  --color-tsl10-tint: color-mix(in oklab, oklch(0.78 0.15 160) 14%, transparent);
  --font-tsl10-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tsl10-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tsl-10 {
  width: 100%;
  height: 100svh;
  min-height: 100svh;
  color-scheme: dark;
}
/* A popover's closed state is :not(:popover-open), and v4 has no negated
   open variant — two lines of CSS are cheaper than an arbitrary variant here. */

.tsl-10 [popover]:not(:popover-open) {
  display: none;
}
3Fonts used by this demooptional — skip if you have your own type stack
<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=Plus+Jakarta+Sans: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.

  • starting:open: is the headline: @starting-style used to force a separate rule block far from the element, and its most common bug was writing the closed-state selector inside it. As a variant prefix the order is unambiguous and the whole animation reads on one line.
  • The backdrop: variant composes with open: and starting:, which means the backdrop fade lives on the drawer element too. Six utilities replace two rule blocks plus a @starting-style wrapper.
  • UI change from the CSS original: the trigger and close buttons are min-h-11/w-11 h-11 (the original close button was 34px, under the WCAG target minimum), and the drawer keeps an explicit labelled close affordance rather than relying on light dismiss alone.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Off-Canvas Navigation Drawer with the Popover API
Source: https://codefronts.com/layouts/tailwind-css-sidebar-layouts/tailwind-off-canvas-navigation-drawer-popover-api/

The off-canvas drawer rebuilt on the platform's newest primitive. popover="auto" ships Esc-to-close, light dismissal, focus return and top-layer rendering; v4 contributes real variants for the hard part &mdash; starting: for the entry animation and an open: variant for the resting state.
## HTML
```html
<section class="tsl-10 w-full h-svh min-h-svh grid grid-rows-[auto_1fr] font-tsl10-sans bg-tsl10-bg text-tsl10-ink">
  <header class="flex items-center gap-4 h-[54px] px-4 bg-tsl10-panel border-b border-tsl10-line text-[15px] font-semibold">
    <button popovertarget="tsl10-d" class="inline-flex items-center gap-2 min-h-11 px-3.5 rounded-[10px] border border-tsl10-line bg-transparent text-tsl10-ink text-[13.5px] font-medium cursor-pointer transition-colors duration-200 hover:bg-tsl10-hover focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[17px] h-[17px] fill-none stroke-current stroke-[1.9] [stroke-linecap:round]"><path d="M4 6h16M4 12h16M4 18h16"/></svg>Menu</button>
    <strong>Waypoint</strong>
  </header>
  <aside popover="auto" id="tsl10-d" aria-label="Navigation drawer" class="fixed inset-y-0 left-0 right-auto w-[min(300px,85vw)] h-full max-h-none m-0 p-4 border-0 border-r border-tsl10-line bg-tsl10-panel text-tsl10-ink -translate-x-full open:translate-x-0 starting:open:-translate-x-full transition-[translate,display,overlay] duration-[320ms] ease-[cubic-bezier(.4,0,.2,1)] [transition-behavior:allow-discrete] backdrop:bg-[oklch(0_0_0/0.45)] backdrop:backdrop-blur-[3px] backdrop:opacity-0 backdrop:open:opacity-100 backdrop:starting:open:opacity-0 backdrop:transition-[opacity,display,overlay] backdrop:duration-[320ms] backdrop:[transition-behavior:allow-discrete] motion-reduce:transition-none">
    <div class="flex items-center justify-between px-1.5 pt-1 pb-4 text-[15px] font-bold">
      <strong>Waypoint</strong>
      <button popovertarget="tsl10-d" popovertargetaction="hide" aria-label="Close menu" class="grid place-items-center w-11 h-11 rounded-[9px] border-0 bg-transparent text-tsl10-mut cursor-pointer transition-colors hover:text-tsl10-ink hover:bg-tsl10-hover focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2"><svg viewBox="0 0 24 24" aria-hidden="true" class="w-[17px] h-[17px] fill-none stroke-current stroke-[1.9] [stroke-linecap:round]"><path d="M6 6l12 12M18 6 6 18"/></svg></button>
    </div>
    <nav class="flex flex-col gap-0.5">
      <a href="#" aria-current="page" class="flex items-center min-h-11 px-3.5 rounded-[10px] text-sm font-medium text-tsl10-mut no-underline transition-colors duration-200 hover:text-tsl10-ink hover:bg-tsl10-hover aria-[current=page]:text-tsl10-accent aria-[current=page]:bg-tsl10-tint focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2">Trips</a>
      <a href="#" class="flex items-center min-h-11 px-3.5 rounded-[10px] text-sm font-medium text-tsl10-mut no-underline transition-colors duration-200 hover:text-tsl10-ink hover:bg-tsl10-hover focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2">Explore</a>
      <a href="#" class="flex items-center min-h-11 px-3.5 rounded-[10px] text-sm font-medium text-tsl10-mut no-underline transition-colors duration-200 hover:text-tsl10-ink hover:bg-tsl10-hover focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2">Saved places</a>
      <a href="#" class="flex items-center min-h-11 px-3.5 rounded-[10px] text-sm font-medium text-tsl10-mut no-underline transition-colors duration-200 hover:text-tsl10-ink hover:bg-tsl10-hover focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2">Offline maps</a>
      <a href="#" class="flex items-center min-h-11 px-3.5 rounded-[10px] text-sm font-medium text-tsl10-mut no-underline transition-colors duration-200 hover:text-tsl10-ink hover:bg-tsl10-hover focus-visible:outline-2 focus-visible:outline-tsl10-accent focus-visible:outline-offset-2">Account</a>
    </nav>
  </aside>
  <main class="p-7 overflow-y-auto [scrollbar-gutter:stable]">
    <h1 class="text-[22px] font-semibold leading-tight mb-2.5">Trips</h1>
    <p class="max-w-[48ch] text-sm leading-relaxed text-tsl10-mut text-pretty">Open the drawer, then press <kbd class="font-tsl10-mono text-[11px] font-semibold px-1.5 py-0.5 rounded-[5px] border border-tsl10-line bg-tsl10-hover">Esc</kbd> or click outside &mdash; dismissal, focus return and the blurred backdrop are all native Popover API behaviour. No script.</p>
  </main>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tsl10-bg: #111a16;
  --color-tsl10-bg: oklch(0.14 0.015 165);
  --color-tsl10-panel: #17221d;
  --color-tsl10-panel: oklch(0.18 0.02 165);
  --color-tsl10-ink: #e8efea;
  --color-tsl10-ink: oklch(0.94 0.005 165);
  --color-tsl10-mut: #93a099;
  --color-tsl10-mut: oklch(0.65 0.012 165);
  --color-tsl10-line: #35423b;
  --color-tsl10-line: oklch(0.29 0.02 165);
  --color-tsl10-accent: #5fdcae;
  --color-tsl10-accent: oklch(0.78 0.15 160);
  --color-tsl10-hover: rgb(232 239 234 / 0.07);
  --color-tsl10-hover: color-mix(in oklab, oklch(0.94 0.005 165) 7%, transparent);
  --color-tsl10-tint: rgb(95 220 174 / 0.14);
  --color-tsl10-tint: color-mix(in oklab, oklch(0.78 0.15 160) 14%, transparent);
  --font-tsl10-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tsl10-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tsl-10 {
  width: 100%;
  height: 100svh;
  min-height: 100svh;
  color-scheme: dark;
}
/* A popover's closed state is :not(:popover-open), and v4 has no negated
   open variant — two lines of CSS are cheaper than an arbitrary variant here. */

.tsl-10 [popover]:not(:popover-open) {
  display: none;
}
```

How this works

The browser owns the behaviour. <aside popover="auto"> opened by <button popovertarget> renders in the top layer above every z-index, closes on Esc or an outside click, and returns focus to the trigger. None of that is styling, and none of it is JavaScript.

Animating top-layer entry is the part that needed 2024 CSS, and v4 has variants for all of it. starting: compiles to @starting-style and supplies the pre-open values; open: matches :popover-open; [transition-behavior:allow-discrete] keeps display animatable. The whole drawer is one element with one class list — no keyframes, no state classes, no script.

Make it yours

  • Bottom sheet: inset-x-0 bottom-0 top-auto translate-y-full open:translate-y-0.
  • popover="manual" disables light dismiss when the drawer must survive a multi-step flow.
  • Keep the explicit close button (popovertargetaction="hide") — light dismiss alone is not a discoverable exit.

Gotchas — read before shipping

  • Popovers are NOT modal: the background stays interactive and focus is not trapped. Use <dialog>.showModal() for a true modal drawer.
  • starting: must be combined with the OPEN state (starting:open:), not the closed one — the classic @starting-style mistake, now compressed into variant order.
  • The top layer ignores your z-index entirely; anything that must paint above the drawer has to be a popover or dialog itself.

Browser support

ChromeSafariFirefoxEdge
125+17.4+129+125+

Popover API + @starting-style + allow-discrete — the full 2024 stack. Older engines should fall back to demo 08's checkbox drawer.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…