Sidebar utility
A flexbox layout that places a sidebar beside main content at wide widths and stacks them at narrow widths — no media queries needed.
Drag the bottom-right corner of the box to resize and watch the layout respond.
Quick implementation
.with-sidebar {
display: flex;
flex-wrap: wrap;
gap: var(--sidebar-gap, 1rem);
}
.with-sidebar > :first-child {
flex-basis: var(--sidebar-width, 15rem);
flex-grow: 1;
}
.with-sidebar > :last-child {
flex-basis: 0;
flex-grow: 999;
min-inline-size: 50%;
}
Prompt this to your LLM
Includes role, constraints, two framework variants, and edge cases to handle.
You are a senior frontend engineer building intrinsic layout utilities.
Goal: A sidebar layout utility using CSS flexbox that places a narrower
sidebar beside a wider content area at large widths and stacks them
vertically at narrow widths — with no media queries.
Technical constraints:
- The wrapper gets display: flex; flex-wrap: wrap and a gap.
- The sidebar (first child) gets flex-basis equal to the desired sidebar
width (e.g. 15rem) and flex-grow: 1 so it can expand when there is room.
- The content area (last child) gets flex-basis: 0 and flex-grow: 999 so
it greedily claims all remaining space, plus min-inline-size: 50% so
that it never shrinks below half the container — this triggers the wrap.
- Use CSS custom properties (--sidebar-width, --sidebar-gap) with
fallback values for easy per-instance overrides.
- Use oklch() for any color values — no hex or rgba.
Framework variant (pick one):
A) Vanilla CSS utility classes .with-sidebar, usable on any container
element with exactly two children.
B) React component — accept sidebarWidth (string, default "15rem"),
gap (string, default "1rem"), and children props; render a div with
inline custom properties forwarding those values.
Edge cases to handle:
- If the sidebar content is taller than the main content at narrow widths,
flex-wrap handles the stack gracefully — no extra CSS needed.
- When nesting .with-sidebar inside another flex or grid context, the
flex-grow: 999 on the content area may interact unexpectedly — scope
the selector to direct children (> :last-child) to prevent leakage.
- RTL support: using min-inline-size instead of min-width ensures the
breakpoint works correctly in right-to-left writing modes.
- For three-column layouts (left sidebar, content, right sidebar), nest
two .with-sidebar wrappers rather than extending this pattern.
Return CSS only (or a React component if variant B is chosen).
How flex-grow: 999 creates a breakpoint-free sidebar
The sidebar utility is built on a clever flexbox trick: the content area is given flex-grow: 999 while the sidebar gets flex-grow: 1. Because flex-grow is a ratio, the content area claims 999 parts of any leftover space while the sidebar claims only 1. In practice this means the content area expands to fill virtually all remaining room, leaving the sidebar at its declared flex-basis width.
The real magic is in min-inline-size: 50% on the content area. This single declaration is the implicit breakpoint. When the container is wide enough for both items to sit side by side, flex-wrap: wrap keeps them on one line. The moment the container narrows to the point where the content area cannot reach its minimum inline size of 50%, the flexbox algorithm wraps the items — the sidebar moves above the content and both stretch to full width. No @media query needed; the layout responds to the container's own width, not the viewport.
This technique comes from "Every Layout" by Heydon Pickering and Andy Bell. The --sidebar-width custom property defaults to 15rem but can be overridden per instance: <div class="with-sidebar" style="--sidebar-width: 20rem">. The breakpoint shifts automatically to wherever the content area can no longer hold 50% of that container.
Content-based vs. viewport breakpoints
Traditional media queries fire at fixed viewport widths — @media (min-width: 48rem) — regardless of the element's actual context. A sidebar inside a modal, a card, or a narrow page column will still switch at 48 rem of viewport width, not at 48 rem of available space. This becomes a problem in component-driven UIs where the same layout utility appears in containers of varying width.
Content-based breakpoints, as used here, solve this because min-inline-size: 50% is relative to the flex container itself. Place the same .with-sidebar component in a full-width page or a 400 px card — it wraps at the right moment in both contexts. This is the same problem that container queries (@container) solve, but the flex-grow: 999 pattern achieves it without any additional browser feature, making it useful in environments where container query support cannot be assumed.
Viewport breakpoints are still the right tool when layout decisions genuinely depend on viewport size — for example, showing or hiding a navigation overlay on mobile. But for intrinsic component layouts like sidebars, using the container's own dimensions produces more predictable, composable results.
RTL, nesting, and other edge cases
Using min-inline-size rather than min-width is intentional: inline-size maps to the inline axis, which is horizontal in left-to-right languages but can differ in right-to-left or vertical writing modes. This ensures the breakpoint fires correctly regardless of writing direction.
Scope the child selectors with > :first-child and > :last-child (direct children only) to prevent the flex rules from leaking into nested .with-sidebar instances. If you need a three-column layout — left sidebar, content, right sidebar — nest two wrappers: the outer wrapper places a left sidebar beside a right column, and the inner wrapper inside the right column places the content beside a right sidebar.
The utility assumes exactly two children. If you add a third child, it will wrap into a new flex row and receive its own share of the container width. For variable numbers of children in a row that should wrap and fill space, the switcher utility is a better fit.