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

Onboarding Wizard Sidebar Layout with Step Progress Tracking

The setup-wizard rail: an ordered list of steps with completed/current/upcoming states, checkmark badges, a connecting line that FILLS with accent color up to the current step, and CSS counters numbering the pending steps automatically — add or remove a step and the numbering, line and states all recompute.

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-26">
  <aside class="sbl-26__rail" aria-label="Setup progress">
    <h2 class="sbl-26__title">Workspace setup</h2>
    <ol class="sbl-26__steps">
      <li data-state="done"><span class="sbl-26__badge" aria-hidden="true"></span><div><strong>Create account</strong><small>ada@nimbus.io verified</small></div></li>
      <li data-state="done"><span class="sbl-26__badge" aria-hidden="true"></span><div><strong>Invite your team</strong><small>4 teammates joined</small></div></li>
      <li data-state="current" aria-current="step"><span class="sbl-26__badge" aria-hidden="true"></span><div><strong>Connect data source</strong><small>Postgres, BigQuery or CSV</small></div></li>
      <li data-state="todo"><span class="sbl-26__badge" aria-hidden="true"></span><div><strong>Build first dashboard</strong><small>From a template or scratch</small></div></li>
      <li data-state="todo"><span class="sbl-26__badge" aria-hidden="true"></span><div><strong>Set up alerts</strong><small>Slack or email digests</small></div></li>
    </ol>
  </aside>
  <main class="sbl-26__main"><h1>Connect data source</h1><p>The rail is an &lt;ol&gt; with three data-states — counters number the todos, checkmarks replace done numbers, and the connector fills behind you.</p><button class="sbl-26__btn" type="button">Connect Postgres</button></main>
</section>
.sbl-26,
.sbl-26 *,
.sbl-26 *::before,
.sbl-26 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sbl-26 {
  --panel: oklch(0.965 0.006 275);
  --ink: oklch(0.26 0.02 275);
  --mut: oklch(0.5 0.015 275);
  --line: oklch(0.88 0.008 275);
  --accent: oklch(0.55 0.18 285);
  display: grid;
  grid-template-columns: 300px 1fr;
  height: 100vh;
  height: 100dvh;
  font-family: system-ui,'Segoe UI',sans-serif;
  background: oklch(0.99 0.002 275);
  color: var(--ink);
}

.sbl-26__rail {
  background: var(--panel);
  border-right: 1px solid var(--line);
  padding: 26px 22px;
  overflow-y: auto;
}

.sbl-26__title {
  font: 700 15px/1 system-ui,sans-serif;
  margin-bottom: 24px;
}

.sbl-26__steps {
  list-style: none;
  counter-reset: sbl26;
  display: flex;
  flex-direction: column;
}

.sbl-26__steps li {
  counter-increment: sbl26;
  position: relative;
  display: grid;
  grid-template-columns: 28px 1fr;
  gap: 14px;
  padding-bottom: 28px;
}

.sbl-26__steps li:last-child {
  padding-bottom: 0;
}

.sbl-26__steps li::after {
  content: "";
  position: absolute;
  left: 13.5px;
  top: 32px;
  bottom: 4px;
  width: 2px;
  border-radius: 2px;
  background: var(--line);
  z-index: 0;
}

.sbl-26__steps li:last-child::after {
  display: none;
}

.sbl-26__steps li[data-state="done"]::after {
  background: var(--accent);
}

.sbl-26__badge {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 2px solid var(--line);
  background: #fff;
  font: 700 12px/1 system-ui,sans-serif;
  color: var(--mut);
}

.sbl-26__badge::before {
  content: counter(sbl26);
}

li[data-state="done"] .sbl-26__badge {
  border-color: var(--accent);
  background: var(--accent);
}

li[data-state="done"] .sbl-26__badge::before {
  content: "";
  width: 12px;
  height: 12px;
  background: #fff;
  -webkit-mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="m4 12.5 5.5 5.5L20 6.5"/></svg>') center/contain no-repeat;
  mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="m4 12.5 5.5 5.5L20 6.5"/></svg>') center/contain no-repeat;
}

li[data-state="current"] .sbl-26__badge {
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in oklab,var(--accent) 18%,transparent);
}

.sbl-26__steps strong {
  display: block;
  font: 600 13.5px/1.3 system-ui,sans-serif;
}

.sbl-26__steps small {
  font: 400 12px/1.45 system-ui,sans-serif;
  color: var(--mut);
}

li[data-state="todo"] strong {
  color: var(--mut);
}

li[data-state="current"] strong {
  color: var(--accent);
}

.sbl-26__main {
  padding: 36px;
  overflow-y: auto;
}

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

.sbl-26__main p {
  color: var(--mut);
  font-size: 14px;
  line-height: 1.65;
  max-width: 52ch;
  margin-bottom: 22px;
  text-wrap: pretty;
}

.sbl-26__btn {
  padding: 12px 22px;
  border: none;
  border-radius: 11px;
  background: var(--accent);
  color: #fff;
  font: 600 14px/1 system-ui,sans-serif;
  cursor: pointer;
  transition: filter .2s ease;
}

.sbl-26__btn:hover {
  filter: brightness(1.1);
}

.sbl-26__btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .sbl-26__btn {
    transition: none;
  }
}
/* No JavaScript — CSS counters number the steps, data-state drives badge/connector styling, aria-current="step" marks the live step for AT. */
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: Onboarding Wizard Sidebar Layout with Step Progress Tracking
Source: https://codefronts.com/layouts/css-sidebar-layouts/onboarding-progress/

The setup-wizard rail: an ordered list of steps with completed/current/upcoming states, checkmark badges, a connecting line that FILLS with accent color up to the current step, and CSS counters numbering the pending steps automatically — add or remove a step and the numbering, line and states all recompute.
## HTML
```html
<section class="sbl-26">
  <aside class="sbl-26__rail" aria-label="Setup progress">
    <h2 class="sbl-26__title">Workspace setup</h2>
    <ol class="sbl-26__steps">
      <li data-state="done"><span class="sbl-26__badge" aria-hidden="true"></span><div><strong>Create account</strong><small>ada@nimbus.io verified</small></div></li>
      <li data-state="done"><span class="sbl-26__badge" aria-hidden="true"></span><div><strong>Invite your team</strong><small>4 teammates joined</small></div></li>
      <li data-state="current" aria-current="step"><span class="sbl-26__badge" aria-hidden="true"></span><div><strong>Connect data source</strong><small>Postgres, BigQuery or CSV</small></div></li>
      <li data-state="todo"><span class="sbl-26__badge" aria-hidden="true"></span><div><strong>Build first dashboard</strong><small>From a template or scratch</small></div></li>
      <li data-state="todo"><span class="sbl-26__badge" aria-hidden="true"></span><div><strong>Set up alerts</strong><small>Slack or email digests</small></div></li>
    </ol>
  </aside>
  <main class="sbl-26__main"><h1>Connect data source</h1><p>The rail is an &lt;ol&gt; with three data-states — counters number the todos, checkmarks replace done numbers, and the connector fills behind you.</p><button class="sbl-26__btn" type="button">Connect Postgres</button></main>
</section>
```
## CSS
```css
.sbl-26,
.sbl-26 *,
.sbl-26 *::before,
.sbl-26 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sbl-26 {
  --panel: oklch(0.965 0.006 275);
  --ink: oklch(0.26 0.02 275);
  --mut: oklch(0.5 0.015 275);
  --line: oklch(0.88 0.008 275);
  --accent: oklch(0.55 0.18 285);
  display: grid;
  grid-template-columns: 300px 1fr;
  height: 100vh;
  height: 100dvh;
  font-family: system-ui,'Segoe UI',sans-serif;
  background: oklch(0.99 0.002 275);
  color: var(--ink);
}

.sbl-26__rail {
  background: var(--panel);
  border-right: 1px solid var(--line);
  padding: 26px 22px;
  overflow-y: auto;
}

.sbl-26__title {
  font: 700 15px/1 system-ui,sans-serif;
  margin-bottom: 24px;
}

.sbl-26__steps {
  list-style: none;
  counter-reset: sbl26;
  display: flex;
  flex-direction: column;
}

.sbl-26__steps li {
  counter-increment: sbl26;
  position: relative;
  display: grid;
  grid-template-columns: 28px 1fr;
  gap: 14px;
  padding-bottom: 28px;
}

.sbl-26__steps li:last-child {
  padding-bottom: 0;
}

.sbl-26__steps li::after {
  content: "";
  position: absolute;
  left: 13.5px;
  top: 32px;
  bottom: 4px;
  width: 2px;
  border-radius: 2px;
  background: var(--line);
  z-index: 0;
}

.sbl-26__steps li:last-child::after {
  display: none;
}

.sbl-26__steps li[data-state="done"]::after {
  background: var(--accent);
}

.sbl-26__badge {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 2px solid var(--line);
  background: #fff;
  font: 700 12px/1 system-ui,sans-serif;
  color: var(--mut);
}

.sbl-26__badge::before {
  content: counter(sbl26);
}

li[data-state="done"] .sbl-26__badge {
  border-color: var(--accent);
  background: var(--accent);
}

li[data-state="done"] .sbl-26__badge::before {
  content: "";
  width: 12px;
  height: 12px;
  background: #fff;
  -webkit-mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="m4 12.5 5.5 5.5L20 6.5"/></svg>') center/contain no-repeat;
  mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="m4 12.5 5.5 5.5L20 6.5"/></svg>') center/contain no-repeat;
}

li[data-state="current"] .sbl-26__badge {
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in oklab,var(--accent) 18%,transparent);
}

.sbl-26__steps strong {
  display: block;
  font: 600 13.5px/1.3 system-ui,sans-serif;
}

.sbl-26__steps small {
  font: 400 12px/1.45 system-ui,sans-serif;
  color: var(--mut);
}

li[data-state="todo"] strong {
  color: var(--mut);
}

li[data-state="current"] strong {
  color: var(--accent);
}

.sbl-26__main {
  padding: 36px;
  overflow-y: auto;
}

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

.sbl-26__main p {
  color: var(--mut);
  font-size: 14px;
  line-height: 1.65;
  max-width: 52ch;
  margin-bottom: 22px;
  text-wrap: pretty;
}

.sbl-26__btn {
  padding: 12px 22px;
  border: none;
  border-radius: 11px;
  background: var(--accent);
  color: #fff;
  font: 600 14px/1 system-ui,sans-serif;
  cursor: pointer;
  transition: filter .2s ease;
}

.sbl-26__btn:hover {
  filter: brightness(1.1);
}

.sbl-26__btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

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

## JavaScript
```js
/* No JavaScript — CSS counters number the steps, data-state drives badge/connector styling, aria-current="step" marks the live step for AT. */
```

How this works

Semantics carry the structure: an <ol> where each <li> holds a badge and text, with state expressed as data-state="done|current|todo" and the current step ALSO marked aria-current="step" — the attribute invented for exactly this. Numbering is a CSS counter (counter(sbl26)) rendered in the badge's ::before for todo steps; done steps override the counter with an SVG-mask checkmark; the current step shows its number in accent. Reorder the list, the numbers follow.

The connector is each item's ::after — a 2px vertical line from the badge's bottom to the next badge. Its color encodes progress: [data-state="done"]::after is accent (the path behind you is lit), others are hairline gray. The current badge gets a breathing halo (4px color-mix ring) — a focal cue that reads instantly which step is live. The whole rail is generated from three states + one counter, which is why it survives edits that break hand-built wizards.

Make it yours

  • Steps are data: your framework maps state per step; the CSS handles any count without edits.
  • Clickable completed steps (jump back): wrap done items' text in links — leave todo steps as plain text so users can't skip ahead.
  • Horizontal variant for top-bars: same counter/connector logic with ::after rotated to horizontal.
  • Sub-steps: nest a second ol with a scoped counter — counters namespace automatically per counter-reset scope.

Gotchas — read before shipping

  • aria-current="step" goes on ONE element only — duplicating it makes screen readers announce multiple 'current' steps.
  • The connector ::after needs the li to be position:relative and the line z-indexed BEHIND badges, or it strikes through the checkmarks.
  • Don't encode done-ness in color alone: the checkmark glyph + the number removal is the double signal.

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 111+.

CSS counters are ancient; color-mix sets the practical floor. mask-based checkmarks need 2023 engines — or inline the SVG instead.

Techniques used in this demo

Search CodeFronts

Loading…