The Switcher Layout
The Switcher is another Every Layout pattern: a row of items that automatically becomes a column once the container gets too narrow to fit them comfortably — with no media query deciding where that happens. The trick is a flex-basis calculation that flips sign at exactly the width you choose.
The core recipe
.switcher {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.switcher > * {
flex-grow: 1;
flex-basis: calc((32rem - 100%) * 999);
}That one calc() line is the whole trick — worth reading carefully.
How the calc() flip works
32remis the threshold — the container width below which items should stack. Call it the target.When the container is wider than the target,
32rem - 100%is a negative number. Multiplied by 999, it becomes an enormous negativeflex-basis— flex clamps a negative basis to 0, so items shrink to their content width and sit in a row.When the container is narrower than the target,
32rem - 100%is a small positive number. Multiplied by 999, it becomes an enormous positiveflex-basis— far bigger than any container could satisfy on one line, so each item is forced onto its own row.The multiplier (999) just needs to be big enough that the resulting basis is always either deeply negative or deeply positive — a "digital" switch rather than a graceful one, which is exactly the point.
@media query appears anywhere in this pattern. The break point is expressed once, as the 32rem constant inside the calculation, and it reacts to the switcher's own container width — not the viewport — so it works correctly nested inside a narrow sidebar too.Controlling how many columns appear
Left alone, a Switcher with many children just keeps them all on one row once past the threshold — useful for a button group, less useful for a card grid that should wrap into multiple rows of two or three. Cap it by fixing a max child count per row with nth-child.
/* limit to a max of 3 per row above the threshold */
.switcher > :nth-last-child(n + 4),
.switcher > :nth-last-child(n + 4) ~ * {
flex-basis: 100%;
}:nth-last-child(n + 4) matches only when there are 4 or more items total, letting a 2- or 3-item row stay a simple row while a 4+ item row forces additional wrapping.Use case — form rows that collapse to a column
<div class="switcher"> <label>First name<input /></label> <label>Last name<input /></label> <label>Email<input type="email" /></label> </div>
On a wide form the three fields sit in a row; embedded in a narrow modal or a mobile viewport, the same markup and CSS stack them vertically automatically — no separate mobile stylesheet needed.
Use case — a card row that becomes a card stack
.card-row {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.card-row > .card {
flex-grow: 1;
flex-basis: calc((40rem - 100%) * 999);
}Drop this exact component into a full-width page (stays a row of cards) or into a narrow sidebar widget (collapses to a stack) — the same rule adapts to whatever container it lands in, which is the entire value proposition of an intrinsic, container-aware layout.
Switcher vs media-query row/column toggling
Switcher (calc trick) | Media query toggle | |
|---|---|---|
Breakpoint tied to | The container itself | The viewport, regardless of container |
Reusable inside any container width | Yes | Only if you re-tune the breakpoint per context |
Extra selectors needed for column cap | One nth-child rule (optional) | None — usually already per-breakpoint |
Readability for future maintainers | Requires understanding the calc trick | Immediately obvious |
container-type: inline-size and container queries (see the Container Queries page) are already a safe baseline, a container query can express the same "switch at this container width" intent more readably than the calc trick — the Switcher pattern remains valuable mainly where you want zero extra wrapping elements or need to support older browsers.Related pages: Container Queries (@container), The Sidebar Layout, The Cluster Layout, and calc().