30 CSS Sidebar Layouts
04 / 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

Mobile-First Responsive Sidebar to Bottom Navigation Switch Layout

One <nav>, two personalities: below 768px it's an iOS-style bottom tab bar with safe-area padding; on desktop the very same markup stands up into a full left sidebar with labels beside icons. No duplicate nav trees to keep in sync, no JS resize listeners — a grid-area swap and a flex-direction flip do all of it.

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-05">
  <nav class="sbl-05__bar" aria-label="Primary">
    <a class="sbl-05__tab" href="#" aria-current="page"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 10.5 12 3l9 7.5M5.5 9.5V21h13V9.5"/></svg><span>Home</span></a>
    <a class="sbl-05__tab" href="#"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><span>Search</span></a>
    <a class="sbl-05__tab" href="#"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M3 9h18M8 2v4m8-4v4"/></svg><span>Bookings</span></a>
    <a class="sbl-05__tab" href="#"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M18 8a6 6 0 1 0-12 0c0 7-3 8-3 8h18s-3-1-3-8m-4.3 11a2 2 0 0 1-3.4 0"/></svg><span>Alerts</span></a>
    <a class="sbl-05__tab" href="#"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-3.9 3.6-6.5 8-6.5s8 2.6 8 6.5"/></svg><span>Profile</span></a>
  </nav>
  <main class="sbl-05__main"><h1>Today</h1><p>Resize the viewport — under 768px this same nav element becomes a bottom tab bar with safe-area padding.</p></main>
</section>
.sbl-05,
.sbl-05 *,
.sbl-05 *::before,
.sbl-05 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sbl-05 {
  --panel: oklch(0.17 0.02 250);
  --ink: oklch(0.93 0.005 250);
  --mut: oklch(0.62 0.012 250);
  --line: oklch(0.28 0.02 250);
  --accent: oklch(0.75 0.14 200);
  display: grid;
  grid-template-areas: "main" "nav";
  grid-template-rows: 1fr auto;
  height: 100vh;
  height: 100dvh;
  font-family: system-ui,'Segoe UI',sans-serif;
  background: oklch(0.13 0.015 250);
  color: var(--ink);
}

.sbl-05__bar {
  grid-area: nav;
  display: flex;
  flex-direction: row;
  background: var(--panel);
  border-top: 1px solid var(--line);
  padding: 6px 8px calc(6px + env(safe-area-inset-bottom,0px));
}

.sbl-05__tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: 48px;
  border-radius: 12px;
  font: 500 10.5px/1 system-ui,sans-serif;
  color: var(--mut);
  text-decoration: none;
  transition: color .2s ease,background-color .2s ease;
}

.sbl-05__tab svg {
  width: 21px;
  height: 21px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.sbl-05__tab:hover {
  color: var(--ink);
}

.sbl-05__tab[aria-current="page"] {
  color: var(--accent);
  background: color-mix(in oklab,var(--accent) 14%,transparent);
}

.sbl-05__tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.sbl-05__main {
  grid-area: main;
  overflow-y: auto;
  padding: 28px;
}

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

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

@media (min-width: 768px) {
  .sbl-05 {
    grid-template-areas: "nav main";
    grid-template-columns: 248px 1fr;
    grid-template-rows: 1fr;
  }

  .sbl-05__bar {
    flex-direction: column;
    gap: 2px;
    justify-content: flex-start;
    border-top: none;
    border-right: 1px solid var(--line);
    padding: 18px 12px;
  }

  .sbl-05__tab {
    flex: 0 0 auto;
    flex-direction: row;
    justify-content: flex-start;
    gap: 12px;
    min-height: 44px;
    padding: 0 14px;
    font-size: 14px;
  }

  .sbl-05__tab[aria-current="page"] {
    box-shadow: inset 2px 0 0 var(--accent);
  }
}

@media (prefers-reduced-motion: reduce) {
  .sbl-05__tab {
    transition: none;
  }
}
/* No JavaScript — one <nav> re-maps between bottom tab bar and left sidebar via grid-template-areas + a flex-direction flip at 768px. */
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: Mobile-First Responsive Sidebar to Bottom Navigation Switch Layout
Source: https://codefronts.com/layouts/css-sidebar-layouts/mobile-first-responsive-sidebar-to-bottom-navigation-switch-layout/

One <nav>, two personalities: below 768px it's an iOS-style bottom tab bar with safe-area padding; on desktop the very same markup stands up into a full left sidebar with labels beside icons. No duplicate nav trees to keep in sync, no JS resize listeners — a grid-area swap and a flex-direction flip do all of it.
## HTML
```html
<section class="sbl-05">
  <nav class="sbl-05__bar" aria-label="Primary">
    <a class="sbl-05__tab" href="#" aria-current="page"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 10.5 12 3l9 7.5M5.5 9.5V21h13V9.5"/></svg><span>Home</span></a>
    <a class="sbl-05__tab" href="#"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><span>Search</span></a>
    <a class="sbl-05__tab" href="#"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M3 9h18M8 2v4m8-4v4"/></svg><span>Bookings</span></a>
    <a class="sbl-05__tab" href="#"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M18 8a6 6 0 1 0-12 0c0 7-3 8-3 8h18s-3-1-3-8m-4.3 11a2 2 0 0 1-3.4 0"/></svg><span>Alerts</span></a>
    <a class="sbl-05__tab" href="#"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-3.9 3.6-6.5 8-6.5s8 2.6 8 6.5"/></svg><span>Profile</span></a>
  </nav>
  <main class="sbl-05__main"><h1>Today</h1><p>Resize the viewport — under 768px this same nav element becomes a bottom tab bar with safe-area padding.</p></main>
</section>
```
## CSS
```css
.sbl-05,
.sbl-05 *,
.sbl-05 *::before,
.sbl-05 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sbl-05 {
  --panel: oklch(0.17 0.02 250);
  --ink: oklch(0.93 0.005 250);
  --mut: oklch(0.62 0.012 250);
  --line: oklch(0.28 0.02 250);
  --accent: oklch(0.75 0.14 200);
  display: grid;
  grid-template-areas: "main" "nav";
  grid-template-rows: 1fr auto;
  height: 100vh;
  height: 100dvh;
  font-family: system-ui,'Segoe UI',sans-serif;
  background: oklch(0.13 0.015 250);
  color: var(--ink);
}

.sbl-05__bar {
  grid-area: nav;
  display: flex;
  flex-direction: row;
  background: var(--panel);
  border-top: 1px solid var(--line);
  padding: 6px 8px calc(6px + env(safe-area-inset-bottom,0px));
}

.sbl-05__tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: 48px;
  border-radius: 12px;
  font: 500 10.5px/1 system-ui,sans-serif;
  color: var(--mut);
  text-decoration: none;
  transition: color .2s ease,background-color .2s ease;
}

.sbl-05__tab svg {
  width: 21px;
  height: 21px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.sbl-05__tab:hover {
  color: var(--ink);
}

.sbl-05__tab[aria-current="page"] {
  color: var(--accent);
  background: color-mix(in oklab,var(--accent) 14%,transparent);
}

.sbl-05__tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.sbl-05__main {
  grid-area: main;
  overflow-y: auto;
  padding: 28px;
}

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

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

@media (min-width: 768px) {
  .sbl-05 {
    grid-template-areas: "nav main";
    grid-template-columns: 248px 1fr;
    grid-template-rows: 1fr;
  }

  .sbl-05__bar {
    flex-direction: column;
    gap: 2px;
    justify-content: flex-start;
    border-top: none;
    border-right: 1px solid var(--line);
    padding: 18px 12px;
  }

  .sbl-05__tab {
    flex: 0 0 auto;
    flex-direction: row;
    justify-content: flex-start;
    gap: 12px;
    min-height: 44px;
    padding: 0 14px;
    font-size: 14px;
  }

  .sbl-05__tab[aria-current="page"] {
    box-shadow: inset 2px 0 0 var(--accent);
  }
}

@media (prefers-reduced-motion: reduce) {
  .sbl-05__tab {
    transition: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — one <nav> re-maps between bottom tab bar and left sidebar via grid-template-areas + a flex-direction flip at 768px. */
```

How this works

The shell is a CSS Grid with named areas, and the media query rewrites the map: mobile is grid-template-areas:"main" "nav" (nav row pinned to the bottom), desktop is "nav main" with a 248px 1fr column template. The <nav> itself just changes flex-direction — row with evenly stretched tabs on mobile, column on desktop. Because it's one DOM node, focus order, ARIA, and state never fork between breakpoints.

The mobile bar gets the native-app details that make it feel real: padding-bottom:env(safe-area-inset-bottom) clears the iPhone home indicator, labels shrink to 10.5px under the icons (the iOS convention), and the active tab is marked with aria-current — styled as a tinted pill on mobile and a left accent bar on desktop from the same attribute. Icon + label stack vertically in the tab (flex-direction:column) and re-flow horizontally on desktop with one declaration each.

Make it yours

  • Tab count: 4–5 max on mobile (thumb reach); overflow items into a 'More' tab on real products.
  • Swap the 768px breakpoint for a container query (@container (min-width:768px)) if the shell lives inside a resizable pane — see demo 29.
  • The desktop sidebar inherits everything from demos 01/17 — add a footer profile block inside the same nav.
  • Animate the morph with transition on padding/flex-basis for a subtle reflow cue when rotating a tablet.

Gotchas — read before shipping

  • env(safe-area-inset-bottom) only resolves when the page sets viewport-fit=cover — without it iPhones overlap the home indicator.
  • Keep mobile hit targets ≥44px tall INCLUDING the label — icon-only 32px tabs fail WCAG 2.5.8 target size.
  • position:fixed inside transformed ancestors breaks — the bar here is a grid row, not fixed, precisely so it works inside any container.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

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

Named grid areas + env() are long-universal. This is the most portable demo in the collection — no 2024 features required.

Techniques used in this demo

Search CodeFronts

Loading…