30 CSS Sidebar Layouts
08 / 30

All 30 in 30 CSS Sidebar Layouts

  1. 01Modern SaaS Dashboard Sidebar Menu with Native Dark Mode Switch
  2. 02Accessible Admin Panel Sidebar with CSS Tooltips and Icon Collapse
  3. 03Developer Documentation Sidebar with Scroll-Synced Nesting
  4. 04Mobile-First Responsive Sidebar to Bottom Navigation Switch Layout
  5. 05Data Analytics Sidebar Layout with CSS Subgrid Alignment
  6. 06Smooth Animated Collapsible Sidebar Menu with Icon-Only Mode
  7. 07CSS Position Sticky Sidebar Layout for E-Commerce and Blogs
  8. 08Pure CSS Collapsible Sidebar Drawer Menu without JavaScript
  9. 09CSS Fixed Sidebar Layout with Independent Scrollable Main Content
  10. 10Off-Canvas Navigation Drawer for Mobile & Desktop Web Apps
  11. 11Right-Aligned CSS Sidebar Layout for Secondary Tool Panels
  12. 12Two-Pane Webmail UI Layout with CSS Flexbox & Grid
  13. 13Dual Sidebar App Shell Layout with Left Nav and Right Context Panel
  14. 14Compact Mini Icon Rail Sidebar Navigation for SaaS Web Apps
  15. 15Responsive E-Commerce Product Filter Sidebar with Native Accordion Menu
  16. 16Modern Floating Glassmorphism Sidebar UI Component
  17. 17CSS Sidebar Menu with Sticky User Profile Footer
  18. 18Push Content Sidebar Navigation Layout with Smooth CSS Transitions
  19. 19Off-Canvas Sidebar Overlay Layout with Blur Backdrop
  20. 20Persistent Collapsible State Sidebar with CSS Variables
  21. 21App Sidebar Navigation with Integrated Sticky Search Bar
  22. 22Section-Grouped Sidebar Navigation Menu with Header Labels
  23. 23CSS Target Active Indicator Sidebar Menu without JavaScript
  24. 24Multi-Level Nested Tree Sidebar Navigation Layout
  25. 25Multi-Tab Sidebar UI Component with Dynamic Panel Switching
  26. 26Onboarding Wizard Sidebar Layout with Step Progress Tracking
  27. 27Pure CSS Resizable Sidebar Layout Using Resize Property
  28. 28Three-Pane Productivity Workspace Shell Layout
  29. 29Next-Gen Adaptive Sidebar Layout Using CSS Container Queries
  30. 30Frosted Glass Floating Sidebar Layout with Backdrop Blur
Pure CSSMIT licensed

Pure CSS Collapsible Sidebar Drawer Menu without JavaScript

The zero-JS drawer, done the 2026 way: a hidden checkbox holds the open/closed state, :has() reads it from the body level, and the drawer slides on transform: translate3d() so the whole animation runs on the GPU compositor — 60fps even mid-page-load, nothing for Lighthouse to flag. The hamburger morphs into an X with two transforms.

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="sbl-07">
  <input type="checkbox" id="sbl07-t" class="sbl-07__state">
  <header class="sbl-07__topbar">
    <label for="sbl07-t" class="sbl-07__burger" aria-hidden="true"><span></span><span></span><span></span></label>
    <strong>Fieldnotes</strong>
  </header>
  <label for="sbl07-t" class="sbl-07__scrim" aria-hidden="true"></label>
  <aside class="sbl-07__drawer" aria-label="Menu">
    <nav class="sbl-07__nav">
      <a href="#" aria-current="page">All notes</a><a href="#">Notebooks</a><a href="#">Tags</a><a href="#">Archive</a><a href="#">Trash</a>
    </nav>
  </aside>
  <main class="sbl-07__main"><h1>All notes</h1><p>Open the drawer — the only animated property is transform, so the slide is fully GPU-composited. Zero JavaScript on this page.</p></main>
</section>
.sbl-07,
.sbl-07 *,
.sbl-07 *::before,
.sbl-07 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sbl-07 {
  --panel: oklch(0.18 0.02 300);
  --ink: oklch(0.93 0.005 300);
  --mut: oklch(0.64 0.012 300);
  --line: oklch(0.29 0.02 300);
  --accent: oklch(0.72 0.16 320);
  position: relative;
  display: grid;
  grid-template-rows: auto 1fr;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  font-family: system-ui,'Segoe UI',sans-serif;
  background: oklch(0.14 0.015 300);
  color: var(--ink);
}

.sbl-07__state {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.sbl-07__topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 0 16px;
  height: 54px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  font: 600 15px/1 system-ui,sans-serif;
}

.sbl-07__burger {
  display: grid;
  gap: 4.5px;
  padding: 12px 10px;
  margin-left: -10px;
  cursor: pointer;
  border-radius: 9px;
}

.sbl-07__burger span {
  display: block;
  width: 19px;
  height: 2px;
  border-radius: 2px;
  background: var(--ink);
  transition: rotate .3s ease,translate .3s ease,opacity .2s ease;
}

.sbl-07:has(#sbl07-t:checked) .sbl-07__burger span:nth-child(1) {
  rotate: 45deg;
  translate: 0 6.5px;
}

.sbl-07:has(#sbl07-t:checked) .sbl-07__burger span:nth-child(2) {
  opacity: 0;
}

.sbl-07:has(#sbl07-t:checked) .sbl-07__burger span:nth-child(3) {
  rotate: -45deg;
  translate: 0 -6.5px;
}

.sbl-07:has(#sbl07-t:focus-visible) .sbl-07__burger {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.sbl-07__scrim {
  position: absolute;
  inset: 54px 0 0;
  background: oklch(0 0 0 / .5);
  backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity .3s ease;
  z-index: 1;
}

.sbl-07:has(#sbl07-t:checked) .sbl-07__scrim {
  opacity: 1;
  pointer-events: auto;
}

.sbl-07__drawer {
  position: absolute;
  top: 54px;
  bottom: 0;
  left: 0;
  width: min(280px,85vw);
  background: var(--panel);
  border-right: 1px solid var(--line);
  padding: 16px 12px;
  transform: translate3d(-100%,0,0);
  visibility: hidden;
  transition: transform .32s cubic-bezier(.4,0,.2,1),visibility 0s .32s;
  z-index: 2;
}

.sbl-07:has(#sbl07-t:checked) .sbl-07__drawer {
  transform: translate3d(0,0,0);
  visibility: visible;
  transition: transform .32s cubic-bezier(.4,0,.2,1);
}

.sbl-07__nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.sbl-07__nav a {
  padding: 11px 14px;
  border-radius: 10px;
  font: 500 14px/1 system-ui,sans-serif;
  color: var(--mut);
  text-decoration: none;
  transition: background-color .2s ease,color .2s ease;
}

.sbl-07__nav a:hover {
  color: var(--ink);
  background: color-mix(in oklab,var(--ink) 7%,transparent);
}

.sbl-07__nav a[aria-current="page"] {
  color: var(--ink);
  background: color-mix(in oklab,var(--accent) 18%,transparent);
}

.sbl-07__nav a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.sbl-07__main {
  padding: 28px;
  overflow-y: auto;
}

.sbl-07__main h1 {
  font: 600 22px/1.2 system-ui,sans-serif;
  margin-bottom: 10px;
}

.sbl-07__main p {
  color: var(--mut);
  font-size: 14px;
  line-height: 1.6;
  max-width: 46ch;
  text-wrap: pretty;
}

@media (prefers-reduced-motion: reduce) {
  .sbl-07__drawer,
    .sbl-07__scrim,
    .sbl-07__burger span {
    transition: none;
  }
}
/* No JavaScript — a hidden checkbox holds state, :has(:checked) slides the drawer via GPU-composited translate3d, and the scrim is a second <label>. */
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 Sidebar Layout 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 Collapsible Sidebar Drawer Menu without JavaScript
Source: https://codefronts.com/layouts/css-sidebar-layouts/collapsible-sidebar/

The zero-JS drawer, done the 2026 way: a hidden checkbox holds the open/closed state, :has() reads it from the body level, and the drawer slides on transform: translate3d() so the whole animation runs on the GPU compositor — 60fps even mid-page-load, nothing for Lighthouse to flag. The hamburger morphs into an X with two transforms.
## HTML
```html
<section class="sbl-07">
  <input type="checkbox" id="sbl07-t" class="sbl-07__state">
  <header class="sbl-07__topbar">
    <label for="sbl07-t" class="sbl-07__burger" aria-hidden="true"><span></span><span></span><span></span></label>
    <strong>Fieldnotes</strong>
  </header>
  <label for="sbl07-t" class="sbl-07__scrim" aria-hidden="true"></label>
  <aside class="sbl-07__drawer" aria-label="Menu">
    <nav class="sbl-07__nav">
      <a href="#" aria-current="page">All notes</a><a href="#">Notebooks</a><a href="#">Tags</a><a href="#">Archive</a><a href="#">Trash</a>
    </nav>
  </aside>
  <main class="sbl-07__main"><h1>All notes</h1><p>Open the drawer — the only animated property is transform, so the slide is fully GPU-composited. Zero JavaScript on this page.</p></main>
</section>
```
## CSS
```css
.sbl-07,
.sbl-07 *,
.sbl-07 *::before,
.sbl-07 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sbl-07 {
  --panel: oklch(0.18 0.02 300);
  --ink: oklch(0.93 0.005 300);
  --mut: oklch(0.64 0.012 300);
  --line: oklch(0.29 0.02 300);
  --accent: oklch(0.72 0.16 320);
  position: relative;
  display: grid;
  grid-template-rows: auto 1fr;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  font-family: system-ui,'Segoe UI',sans-serif;
  background: oklch(0.14 0.015 300);
  color: var(--ink);
}

.sbl-07__state {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.sbl-07__topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 0 16px;
  height: 54px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  font: 600 15px/1 system-ui,sans-serif;
}

.sbl-07__burger {
  display: grid;
  gap: 4.5px;
  padding: 12px 10px;
  margin-left: -10px;
  cursor: pointer;
  border-radius: 9px;
}

.sbl-07__burger span {
  display: block;
  width: 19px;
  height: 2px;
  border-radius: 2px;
  background: var(--ink);
  transition: rotate .3s ease,translate .3s ease,opacity .2s ease;
}

.sbl-07:has(#sbl07-t:checked) .sbl-07__burger span:nth-child(1) {
  rotate: 45deg;
  translate: 0 6.5px;
}

.sbl-07:has(#sbl07-t:checked) .sbl-07__burger span:nth-child(2) {
  opacity: 0;
}

.sbl-07:has(#sbl07-t:checked) .sbl-07__burger span:nth-child(3) {
  rotate: -45deg;
  translate: 0 -6.5px;
}

.sbl-07:has(#sbl07-t:focus-visible) .sbl-07__burger {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.sbl-07__scrim {
  position: absolute;
  inset: 54px 0 0;
  background: oklch(0 0 0 / .5);
  backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity .3s ease;
  z-index: 1;
}

.sbl-07:has(#sbl07-t:checked) .sbl-07__scrim {
  opacity: 1;
  pointer-events: auto;
}

.sbl-07__drawer {
  position: absolute;
  top: 54px;
  bottom: 0;
  left: 0;
  width: min(280px,85vw);
  background: var(--panel);
  border-right: 1px solid var(--line);
  padding: 16px 12px;
  transform: translate3d(-100%,0,0);
  visibility: hidden;
  transition: transform .32s cubic-bezier(.4,0,.2,1),visibility 0s .32s;
  z-index: 2;
}

.sbl-07:has(#sbl07-t:checked) .sbl-07__drawer {
  transform: translate3d(0,0,0);
  visibility: visible;
  transition: transform .32s cubic-bezier(.4,0,.2,1);
}

.sbl-07__nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.sbl-07__nav a {
  padding: 11px 14px;
  border-radius: 10px;
  font: 500 14px/1 system-ui,sans-serif;
  color: var(--mut);
  text-decoration: none;
  transition: background-color .2s ease,color .2s ease;
}

.sbl-07__nav a:hover {
  color: var(--ink);
  background: color-mix(in oklab,var(--ink) 7%,transparent);
}

.sbl-07__nav a[aria-current="page"] {
  color: var(--ink);
  background: color-mix(in oklab,var(--accent) 18%,transparent);
}

.sbl-07__nav a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.sbl-07__main {
  padding: 28px;
  overflow-y: auto;
}

.sbl-07__main h1 {
  font: 600 22px/1.2 system-ui,sans-serif;
  margin-bottom: 10px;
}

.sbl-07__main p {
  color: var(--mut);
  font-size: 14px;
  line-height: 1.6;
  max-width: 46ch;
  text-wrap: pretty;
}

@media (prefers-reduced-motion: reduce) {
  .sbl-07__drawer,
    .sbl-07__scrim,
    .sbl-07__burger span {
    transition: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — a hidden checkbox holds state, :has(:checked) slides the drawer via GPU-composited translate3d, and the scrim is a second <label>. */
```

How this works

State lives in <input type="checkbox" id="sbl07-t" hidden-style>; the visible <label for="sbl07-t"> is the hamburger button. The modern upgrade over the classic sibling-combinator hack is :has(): .sbl-07:has(#sbl07-t:checked) .sbl-07__drawer{transform:translate3d(0,0,0)} works regardless of where the checkbox sits in the DOM — no more contorting your markup so the input precedes the drawer.

Performance is the point of this pattern, so the drawer animates ONLY transform (from translate3d(-100%,0,0)) — never left, width, or margin, which force layout every frame. translate3d (vs plain translateX) promotes the layer to the compositor up front. The scrim is a sibling div fading via opacity + pointer-events toggling, and clicking it closes the drawer because the scrim is also a label for the same checkbox. The hamburger's three bars collapse into an X purely with rotate/translate/opacity on :checked.

Make it yours

  • Right-hand drawer: flip the resting transform to translate3d(100%,0,0) and anchor right:0.
  • Spring feel: swap the cubic-bezier for linear(0, 1.2 60%, 1) — CSS linear() easing gives you a real overshoot.
  • Push instead of overlay: transition margin-left off — see demo 18 for the transform-based push variant.
  • Width: 280px is the mobile sweet spot; cap with min(280px, 85vw) so it never swallows small screens.

Gotchas — read before shipping

  • The label needs a real focusable proxy for keyboards: the input here is visually hidden but NOT display:none, so Space toggles it and :focus-visible styles the hamburger via :has().
  • translate3d(-100%…) still renders the drawer offscreen — add visibility:hidden with a transition-delay so hidden links aren't tab-stops while closed.
  • Scrim-as-label is a click target, not a semantic button — keep Esc-to-close out of scope for pure CSS or add the 4-line JS listener; document the tradeoff.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

Floor set by :has(), oklch(), color-mix(), backdrop-filter, text-wrap as this demo is written. The core technique itself goes back further — Chrome 105+.

:has() is the only gate; swap it for the sibling combinator (#t:checked ~ .drawer) and this runs in anything from the last decade.

Techniques used in this demo

Search CodeFronts

Loading…