30 Tailwind CSS Sidebar Layouts
07 / 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 Sticky Sidebar Layout with Scroll-Driven Progress Bar

The long-article companion rail: a TOC that rides along as you scroll, with the three constraints people always miss, plus a scroll-driven reading progress bar. The port is a good illustration of the split — sticky is three utilities, the scroll timeline stays CSS because it has no utility form and should not.

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-07 w-full h-svh min-h-svh overflow-y-auto font-tsl07-sans bg-tsl07-bg text-tsl07-ink">
  <div aria-hidden="true" class="sticky top-0 z-10 h-[3px] w-0 bg-[linear-gradient(90deg,var(--color-tsl07-accent),oklch(0.65_0.15_300))] animate-tsl07-grow motion-reduce:animate-none motion-reduce:w-full motion-reduce:opacity-25"></div>
  <div class="grid grid-cols-[230px_minmax(0,1fr)] gap-10 max-w-[920px] mx-auto px-6 pt-8 pb-16">
    <aside aria-label="On this page" class="sticky top-6 self-start max-h-[calc(100svh-3rem)] overflow-y-auto border-r border-tsl07-line pr-5">
      <h2 class="mb-3 text-[11px] font-semibold uppercase tracking-[0.09em] text-tsl07-mut">On this page</h2>
      <nav class="flex flex-col gap-0.5">
        <a href="#tsl07-s1" aria-current="true" class="px-2.5 py-[7px] rounded-lg text-[13px] font-medium leading-snug no-underline text-tsl07-mut transition-colors duration-150 hover:text-tsl07-ink hover:bg-tsl07-hover aria-[current=true]:text-tsl07-accent aria-[current=true]:bg-tsl07-tint aria-[current=true]:font-semibold focus-visible:outline-2 focus-visible:outline-tsl07-accent focus-visible:outline-offset-1">Why grids beat floats</a>
        <a href="#tsl07-s2" class="px-2.5 py-[7px] rounded-lg text-[13px] font-medium leading-snug no-underline text-tsl07-mut transition-colors duration-150 hover:text-tsl07-ink hover:bg-tsl07-hover focus-visible:outline-2 focus-visible:outline-tsl07-accent focus-visible:outline-offset-1">Track sizing deep-dive</a>
        <a href="#tsl07-s3" class="px-2.5 py-[7px] rounded-lg text-[13px] font-medium leading-snug no-underline text-tsl07-mut transition-colors duration-150 hover:text-tsl07-ink hover:bg-tsl07-hover focus-visible:outline-2 focus-visible:outline-tsl07-accent focus-visible:outline-offset-1">Alignment properties</a>
        <a href="#tsl07-s4" class="px-2.5 py-[7px] rounded-lg text-[13px] font-medium leading-snug no-underline text-tsl07-mut transition-colors duration-150 hover:text-tsl07-ink hover:bg-tsl07-hover focus-visible:outline-2 focus-visible:outline-tsl07-accent focus-visible:outline-offset-1">Subgrid in practice</a>
        <a href="#tsl07-s5" class="px-2.5 py-[7px] rounded-lg text-[13px] font-medium leading-snug no-underline text-tsl07-mut transition-colors duration-150 hover:text-tsl07-ink hover:bg-tsl07-hover focus-visible:outline-2 focus-visible:outline-tsl07-accent focus-visible:outline-offset-1">Container queries</a>
      </nav>
    </aside>
    <article>
      <h1 id="tsl07-s1" class="scroll-mt-5 text-[26px] font-bold leading-tight tracking-tight mb-3.5">Modern CSS layout, properly</h1>
      <p class="max-w-[62ch] text-[14.5px] leading-[1.75] text-tsl07-body text-pretty">Scroll this page &mdash; the rail sticks 24px from the top while the article flows past. The bar up top is a scroll-driven animation, no JS.</p>
      <h2 id="tsl07-s2" class="scroll-mt-5 mt-[30px] mb-2.5 text-lg font-bold leading-snug">Track sizing deep-dive</h2>
      <p class="max-w-[62ch] text-[14.5px] leading-[1.75] text-tsl07-body text-pretty">Fixed, fractional, minmax and fit-content tracks each answer a different layout question. Fractional units distribute leftover space, not total space &mdash; the distinction that explains most grid surprises.</p>
      <h2 id="tsl07-s3" class="scroll-mt-5 mt-[30px] mb-2.5 text-lg font-bold leading-snug">Alignment properties</h2>
      <p class="max-w-[62ch] text-[14.5px] leading-[1.75] text-tsl07-body text-pretty">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="tsl07-s4" class="scroll-mt-5 mt-[30px] mb-2.5 text-lg font-bold leading-snug">Subgrid in practice</h2>
      <p class="max-w-[62ch] text-[14.5px] leading-[1.75] text-tsl07-body text-pretty">Subgrid lets nested elements adopt parent rails &mdash; card grids with aligned footers, forms with aligned labels, sidebars with aligned badges (see demo 05).</p>
      <h2 id="tsl07-s5" class="scroll-mt-5 mt-[30px] mb-2.5 text-lg font-bold leading-snug">Container queries</h2>
      <p class="max-w-[62ch] text-[14.5px] leading-[1.75] text-tsl07-body text-pretty">Components respond to their container, not the viewport. Demo 29 rebuilds this sidebar pattern on container queries.</p>
    </article>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tsl07-bg: #fcfcfd;
  --color-tsl07-bg: oklch(0.99 0.002 250);
  --color-tsl07-ink: #3b4049;
  --color-tsl07-ink: oklch(0.27 0.015 250);
  --color-tsl07-body: #565c66;
  --color-tsl07-body: oklch(0.38 0.012 250);
  --color-tsl07-mut: #767b85;
  --color-tsl07-mut: oklch(0.52 0.012 250);
  --color-tsl07-line: #e3e4e9;
  --color-tsl07-line: oklch(0.9 0.007 250);
  --color-tsl07-accent: #4360cb;
  --color-tsl07-accent: oklch(0.55 0.16 250);
  --color-tsl07-hover: rgb(59 64 73 / 0.05);
  --color-tsl07-hover: color-mix(in oklab, oklch(0.27 0.015 250) 5%, transparent);
  --color-tsl07-tint: rgb(67 96 203 / 0.09);
  --color-tsl07-tint: color-mix(in oklab, oklch(0.55 0.16 250) 9%, transparent);
  --font-tsl07-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --animate-tsl07-grow: tsl07-grow linear;
}

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

  to {
    width: 100%;
  }
}

.tsl-07 {
  width: 100%;
  height: 100svh;
  min-height: 100svh;
  color-scheme: light;
}
/* No utility names a scroll timeline, and none should — this is plumbing,
   not a design-scale value. The bar's animation itself IS a utility. */

.tsl-07 {
  scroll-timeline: --tsl07-scroll block;
}

.tsl-07 > [aria-hidden="true"]:first-child {
  animation-timeline: --tsl07-scroll;
}

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-tsl07-bg: #fcfcfd;
  --color-tsl07-bg: oklch(0.99 0.002 250);
  --color-tsl07-ink: #3b4049;
  --color-tsl07-ink: oklch(0.27 0.015 250);
  --color-tsl07-body: #565c66;
  --color-tsl07-body: oklch(0.38 0.012 250);
  --color-tsl07-mut: #767b85;
  --color-tsl07-mut: oklch(0.52 0.012 250);
  --color-tsl07-line: #e3e4e9;
  --color-tsl07-line: oklch(0.9 0.007 250);
  --color-tsl07-accent: #4360cb;
  --color-tsl07-accent: oklch(0.55 0.16 250);
  --color-tsl07-hover: rgb(59 64 73 / 0.05);
  --color-tsl07-hover: color-mix(in oklab, oklch(0.27 0.015 250) 5%, transparent);
  --color-tsl07-tint: rgb(67 96 203 / 0.09);
  --color-tsl07-tint: color-mix(in oklab, oklch(0.55 0.16 250) 9%, transparent);
  --font-tsl07-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --animate-tsl07-grow: tsl07-grow linear;
}

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

  to {
    width: 100%;
  }
}

.tsl-07 {
  width: 100%;
  height: 100svh;
  min-height: 100svh;
  color-scheme: light;
}
/* No utility names a scroll timeline, and none should — this is plumbing,
   not a design-scale value. The bar's animation itself IS a utility. */

.tsl-07 {
  scroll-timeline: --tsl07-scroll block;
}

.tsl-07 > [aria-hidden="true"]:first-child {
  animation-timeline: --tsl07-scroll;
}
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.

  • Sticky is the port's best advertisement: sticky top-6 self-start max-h-[…] overflow-y-auto puts all three silent preconditions in one place, where a reviewer can see one is missing.
  • --animate-tsl07-grow in @theme mints the animate-* utility, but animation-timeline has to be attached in CSS. Splitting an animation across both layers is normal in v4 and worth doing: the markup still says what animates.
  • UI change from the CSS original: the article column is minmax(0,1fr) so a long unbroken token cannot widen the grid, and the reduced-motion fallback shows a static full-width bar at 25% opacity rather than nothing at all.
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 Sticky Sidebar Layout with Scroll-Driven Progress Bar
Source: https://codefronts.com/layouts/tailwind-css-sidebar-layouts/tailwind-sticky-sidebar-scroll-driven-progress-bar/

The long-article companion rail: a TOC that rides along as you scroll, with the three constraints people always miss, plus a scroll-driven reading progress bar. The port is a good illustration of the split &mdash; sticky is three utilities, the scroll timeline stays CSS because it has no utility form and should not.
## HTML
```html
<section class="tsl-07 w-full h-svh min-h-svh overflow-y-auto font-tsl07-sans bg-tsl07-bg text-tsl07-ink">
  <div aria-hidden="true" class="sticky top-0 z-10 h-[3px] w-0 bg-[linear-gradient(90deg,var(--color-tsl07-accent),oklch(0.65_0.15_300))] animate-tsl07-grow motion-reduce:animate-none motion-reduce:w-full motion-reduce:opacity-25"></div>
  <div class="grid grid-cols-[230px_minmax(0,1fr)] gap-10 max-w-[920px] mx-auto px-6 pt-8 pb-16">
    <aside aria-label="On this page" class="sticky top-6 self-start max-h-[calc(100svh-3rem)] overflow-y-auto border-r border-tsl07-line pr-5">
      <h2 class="mb-3 text-[11px] font-semibold uppercase tracking-[0.09em] text-tsl07-mut">On this page</h2>
      <nav class="flex flex-col gap-0.5">
        <a href="#tsl07-s1" aria-current="true" class="px-2.5 py-[7px] rounded-lg text-[13px] font-medium leading-snug no-underline text-tsl07-mut transition-colors duration-150 hover:text-tsl07-ink hover:bg-tsl07-hover aria-[current=true]:text-tsl07-accent aria-[current=true]:bg-tsl07-tint aria-[current=true]:font-semibold focus-visible:outline-2 focus-visible:outline-tsl07-accent focus-visible:outline-offset-1">Why grids beat floats</a>
        <a href="#tsl07-s2" class="px-2.5 py-[7px] rounded-lg text-[13px] font-medium leading-snug no-underline text-tsl07-mut transition-colors duration-150 hover:text-tsl07-ink hover:bg-tsl07-hover focus-visible:outline-2 focus-visible:outline-tsl07-accent focus-visible:outline-offset-1">Track sizing deep-dive</a>
        <a href="#tsl07-s3" class="px-2.5 py-[7px] rounded-lg text-[13px] font-medium leading-snug no-underline text-tsl07-mut transition-colors duration-150 hover:text-tsl07-ink hover:bg-tsl07-hover focus-visible:outline-2 focus-visible:outline-tsl07-accent focus-visible:outline-offset-1">Alignment properties</a>
        <a href="#tsl07-s4" class="px-2.5 py-[7px] rounded-lg text-[13px] font-medium leading-snug no-underline text-tsl07-mut transition-colors duration-150 hover:text-tsl07-ink hover:bg-tsl07-hover focus-visible:outline-2 focus-visible:outline-tsl07-accent focus-visible:outline-offset-1">Subgrid in practice</a>
        <a href="#tsl07-s5" class="px-2.5 py-[7px] rounded-lg text-[13px] font-medium leading-snug no-underline text-tsl07-mut transition-colors duration-150 hover:text-tsl07-ink hover:bg-tsl07-hover focus-visible:outline-2 focus-visible:outline-tsl07-accent focus-visible:outline-offset-1">Container queries</a>
      </nav>
    </aside>
    <article>
      <h1 id="tsl07-s1" class="scroll-mt-5 text-[26px] font-bold leading-tight tracking-tight mb-3.5">Modern CSS layout, properly</h1>
      <p class="max-w-[62ch] text-[14.5px] leading-[1.75] text-tsl07-body text-pretty">Scroll this page &mdash; the rail sticks 24px from the top while the article flows past. The bar up top is a scroll-driven animation, no JS.</p>
      <h2 id="tsl07-s2" class="scroll-mt-5 mt-[30px] mb-2.5 text-lg font-bold leading-snug">Track sizing deep-dive</h2>
      <p class="max-w-[62ch] text-[14.5px] leading-[1.75] text-tsl07-body text-pretty">Fixed, fractional, minmax and fit-content tracks each answer a different layout question. Fractional units distribute leftover space, not total space &mdash; the distinction that explains most grid surprises.</p>
      <h2 id="tsl07-s3" class="scroll-mt-5 mt-[30px] mb-2.5 text-lg font-bold leading-snug">Alignment properties</h2>
      <p class="max-w-[62ch] text-[14.5px] leading-[1.75] text-tsl07-body text-pretty">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="tsl07-s4" class="scroll-mt-5 mt-[30px] mb-2.5 text-lg font-bold leading-snug">Subgrid in practice</h2>
      <p class="max-w-[62ch] text-[14.5px] leading-[1.75] text-tsl07-body text-pretty">Subgrid lets nested elements adopt parent rails &mdash; card grids with aligned footers, forms with aligned labels, sidebars with aligned badges (see demo 05).</p>
      <h2 id="tsl07-s5" class="scroll-mt-5 mt-[30px] mb-2.5 text-lg font-bold leading-snug">Container queries</h2>
      <p class="max-w-[62ch] text-[14.5px] leading-[1.75] text-tsl07-body text-pretty">Components respond to their container, not the viewport. Demo 29 rebuilds this sidebar pattern on container queries.</p>
    </article>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tsl07-bg: #fcfcfd;
  --color-tsl07-bg: oklch(0.99 0.002 250);
  --color-tsl07-ink: #3b4049;
  --color-tsl07-ink: oklch(0.27 0.015 250);
  --color-tsl07-body: #565c66;
  --color-tsl07-body: oklch(0.38 0.012 250);
  --color-tsl07-mut: #767b85;
  --color-tsl07-mut: oklch(0.52 0.012 250);
  --color-tsl07-line: #e3e4e9;
  --color-tsl07-line: oklch(0.9 0.007 250);
  --color-tsl07-accent: #4360cb;
  --color-tsl07-accent: oklch(0.55 0.16 250);
  --color-tsl07-hover: rgb(59 64 73 / 0.05);
  --color-tsl07-hover: color-mix(in oklab, oklch(0.27 0.015 250) 5%, transparent);
  --color-tsl07-tint: rgb(67 96 203 / 0.09);
  --color-tsl07-tint: color-mix(in oklab, oklch(0.55 0.16 250) 9%, transparent);
  --font-tsl07-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --animate-tsl07-grow: tsl07-grow linear;
}

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

  to {
    width: 100%;
  }
}

.tsl-07 {
  width: 100%;
  height: 100svh;
  min-height: 100svh;
  color-scheme: light;
}
/* No utility names a scroll timeline, and none should — this is plumbing,
   not a design-scale value. The bar's animation itself IS a utility. */

.tsl-07 {
  scroll-timeline: --tsl07-scroll block;
}

.tsl-07 > [aria-hidden="true"]:first-child {
  animation-timeline: --tsl07-scroll;
}
```

How this works

Sticky fails silently unless three things hold, and the utility form makes all three visible on one element: an explicit inset (top-6), self-start (grid items stretch by default, and a stretched rail has no room to travel), and a height cap with internal scrolling. Reading them in a single class list is the strongest argument for the port — in the CSS original the three declarations were three lines that a refactor could separate.

The progress bar cannot be a utility, and should not be. scroll-timeline: --tsl07-scroll block names the scroller's axis as a timeline; the bar's keyframes run on it. Naming a timeline is not a design-scale value. What v4 does contribute is --animate-tsl07-grow in @theme, which mints animate-tsl07-grow so the markup still declares the animation.

Make it yours

  • Match the top offset to your real header: top-[calc(var(--header-h)+1rem)].
  • Compose with demo 03's :target highlighting for a scroll-synced TOC.
  • Blogs conventionally put the TOC on the right — swap to grid-cols-[minmax(0,1fr)_230px] and order-*.

Gotchas — read before shipping

  • Any ancestor with overflow-hidden between the sticky element and its scroller kills sticking — the most-Googled sticky bug, and overflow-hidden is an easy utility to add absent-mindedly.
  • self-start (or an explicit height) is mandatory in grid and flex parents.
  • The scroll timeline must be declared on the element that actually scrolls — here the section, not the window.

Browser support

ChromeSafariFirefoxEdge
115+26+—115+

position:sticky is universal since 2017; the progress bar is progressive enhancement (Firefox behind a flag) and vanishes cleanly where unsupported.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…