CSSThe Sidebar Layout

The Sidebar Layout

The sidebar pattern is one fixed-width column next to one fluid column that takes the rest of the space, with a rule for what happens when the container gets too narrow for both. Every Layout's version of this pattern (see Further Learning Resources) frames it as an intrinsic layout — no media query decides the breakpoint, the content's own width does.

Grid version — fixed + fluid

CSS
.layout {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  gap: 1.5rem;
}
Note
minmax(0, 1fr) rather than plain 1fr matters here — an implicit minimum width of auto on a grid track means wide content (a code block, a long unbreakable URL) can stretch the "fluid" column past the container. Explicitly flooring it at 0 lets it shrink instead.
The intrinsic (Every Layout) version — no fixed breakpoint

The classic Every Layout Sidebar recipe uses flexbox with flex-wrap and a flex-basis calculation so the sidebar collapses to full width automatically once the container gets too narrow to hold the sidebar's minimum width plus the fluid column's minimum width — no @media query at all.

CSS
.sidebar-layout {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
}

.sidebar-layout > .sidebar {
  flex-basis: 240px;
  flex-grow: 1;
}

.sidebar-layout > .content {
  flex-basis: 0;
  flex-grow: 999; /* wins almost all the growth, staying "the main column" */
  min-width: 60%; /* the threshold below which content wraps to its own row */
}
  • The lopsided flex-grow values (1 vs 999) mean the content column claims essentially all extra space while the sidebar only grows if there is truly nothing else competing for room.

  • min-width: 60% on .content is the real trigger: once the container is too narrow for the content column to have 60% of it and the sidebar its 240px, flexbox wraps the sidebar onto its own line.

  • There is no media query anywhere in this recipe — the breakpoint is a function of the sidebar's own declared width, so it "just works" inside any container, including a narrower parent than the whole viewport.

Tip
This is the core idea behind every Every Layout pattern: express the layout rule in terms of the content's own intrinsic sizes, and let the browser compute where it breaks — instead of guessing a pixel breakpoint up front.
Collapsible sidebar (checkbox / JS-light toggle)

For an app shell where the sidebar should be dismissible (docs sites, dashboards), a hidden checkbox plus sibling selectors gets you a toggle with zero JavaScript for the visual state — only a <label> click driving native checkbox behavior.

HTML
<input type="checkbox" id="sidebar-toggle" class="sr-only" />
<label for="sidebar-toggle" class="toggle-btn">☰</label>

<div class="app-shell">
  <aside class="sidebar">Navigation</aside>
  <main class="content">Page content</main>
</div>

CSS
.app-shell {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  transition: grid-template-columns 0.2s ease;
}

#sidebar-toggle:checked ~ .app-shell {
  grid-template-columns: 0px minmax(0, 1fr);
}

#sidebar-toggle:checked ~ .app-shell .sidebar {
  overflow: hidden;
  padding: 0;
}
Note
The checkbox must be a sibling that comes before the element it controls in the DOM for the ~ general sibling combinator (see the Combinators page) to reach it — this is the same trick used for pure-CSS accordions and tab panels.
Responsive breakpoint collapse (media-query version)

When the intrinsic wrap-based approach isn't granular enough — say, the sidebar should become a bottom drawer or an off-canvas panel rather than simply stacking above the content — a plain media query is the more predictable tool.

CSS
.app-shell {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  gap: 1.5rem;
}

@media (max-width: 768px) {
  .app-shell {
    grid-template-columns: minmax(0, 1fr);
  }

  .sidebar {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
  }

  .sidebar.is-open {
    transform: translateX(0);
  }
}
Sticky sidebar

CSS
.sidebar {
  position: sticky;
  top: 1.5rem;
  align-self: start; /* prevents grid stretch from cancelling out sticky */
  max-height: calc(100dvh - 3rem);
  overflow-y: auto;
}
Note
align-self: start is easy to forget: by default a grid item stretches to fill its row's height, and a stretched element has nowhere left to "stick" as you scroll — it's already as tall as its track. See the position: sticky page for the full mechanics.
Flexbox vs intrinsic vs media-query — choosing

Approach

Breakpoint driven by

Best for

Grid (fixed px + minmax(0, 1fr))

Never collapses on its own

Layouts that should always show both columns (desktop-only tools)

Intrinsic flex-wrap (Every Layout)

The content's own minimum width

Reusable components dropped into unpredictable containers

Media query

A fixed viewport width you choose

App shells needing an off-canvas/drawer transformation, not just stacking