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

CSS Position Sticky Sidebar Layout for E-Commerce and Blogs

The long-article companion rail: a table of contents that rides along as you scroll — position:sticky with the three constraints people always miss (top offset, self-alignment, max-height) — plus a scroll-driven reading progress bar powered by CSS scroll-timeline. The page scrolls as one document; only the rail sticks.

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-15">
  <div class="sbl-15__progress" aria-hidden="true"></div>
  <div class="sbl-15__page">
    <aside class="sbl-15__rail" aria-label="On this page">
      <h2>On this page</h2>
      <nav><a href="#sbl15-s1" aria-current="true">Why grids beat floats</a><a href="#sbl15-s2">Track sizing deep-dive</a><a href="#sbl15-s3">Alignment properties</a><a href="#sbl15-s4">Subgrid in practice</a><a href="#sbl15-s5">Container queries</a></nav>
    </aside>
    <article class="sbl-15__article">
      <h1 id="sbl15-s1">Modern CSS layout, properly</h1>
      <p>Scroll this page — the rail sticks at 24px from the top while the article flows past. The progress bar up top is a scroll-driven animation, no JS.</p>
      <h2 id="sbl15-s2">Track sizing deep-dive</h2>
      <p>Fixed, fractional, minmax and fit-content tracks each answer a different layout question. Fractional units distribute leftover space, not total space — the distinction that explains most grid surprises.</p>
      <h2 id="sbl15-s3">Alignment properties</h2>
      <p>justify- controls the inline axis, align- the block axis. The -content properties move tracks; -items move children within their cells; -self overrides per child.</p>
      <h2 id="sbl15-s4">Subgrid in practice</h2>
      <p>Subgrid lets nested elements adopt parent rails — card grids with aligned footers, forms with aligned labels, sidebars with aligned badges (see demo 06).</p>
      <h2 id="sbl15-s5">Container queries</h2>
      <p>Components respond to their container, not the viewport. Demo 29 rebuilds this sidebar pattern on @container rules.</p>
    </article>
  </div>
</section>
.sbl-15,
.sbl-15 *,
.sbl-15 *::before,
.sbl-15 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sbl-15 {
  --ink: oklch(0.27 0.015 250);
  --mut: oklch(0.52 0.012 250);
  --line: oklch(0.9 0.007 250);
  --accent: oklch(0.55 0.16 250);
  height: 100vh;
  height: 100dvh;
  overflow-y: auto;
  scroll-timeline: --sbl15-scroll block;
  font-family: system-ui,'Segoe UI',sans-serif;
  background: oklch(0.99 0.002 250);
  color: var(--ink);
}

.sbl-15__progress {
  position: sticky;
  top: 0;
  height: 3px;
  background: linear-gradient(90deg,var(--accent),oklch(0.65 0.15 300));
  width: 0;
  z-index: 5;
  animation: sbl15-grow linear;
  animation-timeline: --sbl15-scroll;
}

@keyframes sbl15-grow {
  from {
    width: 0;
  }

  to {
    width: 100%;
  }
}

.sbl-15__page {
  display: grid;
  grid-template-columns: 230px minmax(0,1fr);
  gap: 40px;
  max-width: 920px;
  margin: 0 auto;
  padding: 32px 24px 60px;
}

.sbl-15__rail {
  position: sticky;
  top: 24px;
  align-self: start;
  max-height: calc(100dvh - 48px);
  overflow-y: auto;
  border-right: 1px solid var(--line);
  padding-right: 20px;
}

.sbl-15__rail h2 {
  font: 600 11px/1 system-ui,sans-serif;
  text-transform: uppercase;
  letter-spacing: .09em;
  color: var(--mut);
  margin-bottom: 12px;
}

.sbl-15__rail nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.sbl-15__rail a {
  padding: 7px 10px;
  border-radius: 8px;
  font: 500 13px/1.4 system-ui,sans-serif;
  color: var(--mut);
  text-decoration: none;
  transition: color .15s ease,background-color .15s ease;
}

.sbl-15__rail a:hover {
  color: var(--ink);
  background: color-mix(in oklab,var(--ink) 5%,transparent);
}

.sbl-15__rail a[aria-current="true"] {
  color: var(--accent);
  background: color-mix(in oklab,var(--accent) 9%,transparent);
  font-weight: 600;
}

.sbl-15__rail a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.sbl-15__article h1 {
  font: 700 26px/1.2 system-ui,sans-serif;
  margin-bottom: 14px;
}

.sbl-15__article h2 {
  font: 650 18px/1.3 system-ui,sans-serif;
  margin: 30px 0 10px;
  scroll-margin-top: 20px;
}

.sbl-15__article p {
  font-size: 14.5px;
  line-height: 1.75;
  color: oklch(0.38 0.012 250);
  max-width: 62ch;
  text-wrap: pretty;
}

@media (prefers-reduced-motion: reduce) {
  .sbl-15__progress {
    animation: none;
    width: 100%;
    opacity: .25;
  }
}
/* No JavaScript — sticky needs top + align-self:start + max-height (all three here); the progress bar runs on a named CSS scroll-timeline. */
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: CSS Position Sticky Sidebar Layout for E-Commerce and Blogs
Source: https://codefronts.com/layouts/css-sidebar-layouts/css-position-sticky-sidebar-layout-for-e-commerce-and-blogs/

The long-article companion rail: a table of contents that rides along as you scroll — position:sticky with the three constraints people always miss (top offset, self-alignment, max-height) — plus a scroll-driven reading progress bar powered by CSS scroll-timeline. The page scrolls as one document; only the rail sticks.
## HTML
```html
<section class="sbl-15">
  <div class="sbl-15__progress" aria-hidden="true"></div>
  <div class="sbl-15__page">
    <aside class="sbl-15__rail" aria-label="On this page">
      <h2>On this page</h2>
      <nav><a href="#sbl15-s1" aria-current="true">Why grids beat floats</a><a href="#sbl15-s2">Track sizing deep-dive</a><a href="#sbl15-s3">Alignment properties</a><a href="#sbl15-s4">Subgrid in practice</a><a href="#sbl15-s5">Container queries</a></nav>
    </aside>
    <article class="sbl-15__article">
      <h1 id="sbl15-s1">Modern CSS layout, properly</h1>
      <p>Scroll this page — the rail sticks at 24px from the top while the article flows past. The progress bar up top is a scroll-driven animation, no JS.</p>
      <h2 id="sbl15-s2">Track sizing deep-dive</h2>
      <p>Fixed, fractional, minmax and fit-content tracks each answer a different layout question. Fractional units distribute leftover space, not total space — the distinction that explains most grid surprises.</p>
      <h2 id="sbl15-s3">Alignment properties</h2>
      <p>justify- controls the inline axis, align- the block axis. The -content properties move tracks; -items move children within their cells; -self overrides per child.</p>
      <h2 id="sbl15-s4">Subgrid in practice</h2>
      <p>Subgrid lets nested elements adopt parent rails — card grids with aligned footers, forms with aligned labels, sidebars with aligned badges (see demo 06).</p>
      <h2 id="sbl15-s5">Container queries</h2>
      <p>Components respond to their container, not the viewport. Demo 29 rebuilds this sidebar pattern on @container rules.</p>
    </article>
  </div>
</section>
```
## CSS
```css
.sbl-15,
.sbl-15 *,
.sbl-15 *::before,
.sbl-15 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sbl-15 {
  --ink: oklch(0.27 0.015 250);
  --mut: oklch(0.52 0.012 250);
  --line: oklch(0.9 0.007 250);
  --accent: oklch(0.55 0.16 250);
  height: 100vh;
  height: 100dvh;
  overflow-y: auto;
  scroll-timeline: --sbl15-scroll block;
  font-family: system-ui,'Segoe UI',sans-serif;
  background: oklch(0.99 0.002 250);
  color: var(--ink);
}

.sbl-15__progress {
  position: sticky;
  top: 0;
  height: 3px;
  background: linear-gradient(90deg,var(--accent),oklch(0.65 0.15 300));
  width: 0;
  z-index: 5;
  animation: sbl15-grow linear;
  animation-timeline: --sbl15-scroll;
}

@keyframes sbl15-grow {
  from {
    width: 0;
  }

  to {
    width: 100%;
  }
}

.sbl-15__page {
  display: grid;
  grid-template-columns: 230px minmax(0,1fr);
  gap: 40px;
  max-width: 920px;
  margin: 0 auto;
  padding: 32px 24px 60px;
}

.sbl-15__rail {
  position: sticky;
  top: 24px;
  align-self: start;
  max-height: calc(100dvh - 48px);
  overflow-y: auto;
  border-right: 1px solid var(--line);
  padding-right: 20px;
}

.sbl-15__rail h2 {
  font: 600 11px/1 system-ui,sans-serif;
  text-transform: uppercase;
  letter-spacing: .09em;
  color: var(--mut);
  margin-bottom: 12px;
}

.sbl-15__rail nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.sbl-15__rail a {
  padding: 7px 10px;
  border-radius: 8px;
  font: 500 13px/1.4 system-ui,sans-serif;
  color: var(--mut);
  text-decoration: none;
  transition: color .15s ease,background-color .15s ease;
}

.sbl-15__rail a:hover {
  color: var(--ink);
  background: color-mix(in oklab,var(--ink) 5%,transparent);
}

.sbl-15__rail a[aria-current="true"] {
  color: var(--accent);
  background: color-mix(in oklab,var(--accent) 9%,transparent);
  font-weight: 600;
}

.sbl-15__rail a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.sbl-15__article h1 {
  font: 700 26px/1.2 system-ui,sans-serif;
  margin-bottom: 14px;
}

.sbl-15__article h2 {
  font: 650 18px/1.3 system-ui,sans-serif;
  margin: 30px 0 10px;
  scroll-margin-top: 20px;
}

.sbl-15__article p {
  font-size: 14.5px;
  line-height: 1.75;
  color: oklch(0.38 0.012 250);
  max-width: 62ch;
  text-wrap: pretty;
}

@media (prefers-reduced-motion: reduce) {
  .sbl-15__progress {
    animation: none;
    width: 100%;
    opacity: .25;
  }
}
```

## JavaScript
```js
/* No JavaScript — sticky needs top + align-self:start + max-height (all three here); the progress bar runs on a named CSS scroll-timeline. */
```

How this works

Sticky fails silently unless three conditions hold, and this demo encodes them all: (1) top:24px — a sticky element needs an explicit inset to know where to stick; (2) align-self:start — in a grid, items stretch to row height by default, and a stretched sidebar is as tall as its track, leaving nothing to stick within; (3) max-height:calc(100dvh - 48px); overflow-y:auto — a rail taller than the viewport can never stick usefully, so it caps itself and scrolls internally. Unlike the app-shell demos, the CONTAINER here scrolls as one document — sticky is the right tool when the sidebar accompanies flowing content rather than framing an app.

The progress bar is 2026-native: scroll-timeline on the scrolling container names the scroll axis as an animation timeline, and the bar's width animates over it (animation-timeline:--sbl15-scroll) — scroll-linked animation with zero scroll listeners, running off-main-thread. Engines without it just show no bar.

Make it yours

  • The 24px top offset should match your real header height + breathing room: top:calc(var(--header-h) + 16px).
  • Add demo 04's :target highlighting for a scroll-synced TOC — the two demos compose directly.
  • The progress bar generalizes: animate opacity/scale of any element over the same named timeline.
  • Blogs: put the rail on the right (swap columns) — readers expect TOC right, filters left.

Gotchas — read before shipping

  • ANY ancestor with overflow:hidden (or clip/auto) between the sticky element and the scroller kills sticking — the most-Googled sticky bug. Audit the chain first.
  • align-self:start (or an explicit height) is mandatory in grid/flex parents — stretched children have no room to travel.
  • Scroll-driven animations need the timeline on the actual scroller; here that's the section (100dvh overflow-y:auto), not the window.

Browser support

ChromeSafariFirefoxEdge
115+26+—115+

position:sticky is universal (2017+); the scroll-timeline progress bar is progressive enhancement (Firefox behind flag) that vanishes cleanly.

Techniques used in this demo

Search CodeFronts

Loading…