name: designing-responsive-layouts description: Designs responsive CSS layouts — mobile-first breakpoints, flexbox vs grid decisions, container queries, fluid sizing with clamp(), and intrinsic auto-fit/minmax patterns. Use when the user asks to make a page or component responsive, build a layout or grid, fix overflow or squished content on mobile, choose between flexbox and grid, or add breakpoints or container queries. Do not use for page content composition and conversion structure (creating-landing-pages) or for color/spacing token systems (theming-design-tokens).
Designing Responsive Layouts
When to use / when NOT to use
- Use for: layout mechanics — grids, columns, wrapping, breakpoints, container queries, fluid sizing, overflow fixes.
- Do NOT use for: what content goes where on a marketing page (→ creating-landing-pages); token scales and theming (→ theming-design-tokens); typography scales (→ choosing-typography).
Core rules
-
Mobile-first: base styles are the narrow layout; media queries only add width.
- ✅
.cards { display: grid; } @media (min-width: 48rem) { .cards { grid-template-columns: 1fr 1fr; } } - ❌ Desktop styles first, then
@media (max-width: …)overrides undoing them
- ✅
-
Flexbox vs grid decision rule: one dimension → flexbox; two dimensions or explicit placement → grid. Nav bars, button rows, media objects = flex. Card grids, page shells, dashboards = grid.
-
Prefer intrinsic (no-breakpoint) patterns before adding media queries.
- ✅
grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr)); - ❌ Three hand-written breakpoints to go 1→2→3 columns
- ✅
-
Breakpoints in
rem, chosen where the content breaks — not at device names. Defaults when nothing else is known: 48rem (768px) and 80rem (1280px); always verify at 360px, 768px, 1280px. -
Components respond to their container, not the viewport.
- ✅
.sidebar { container-type: inline-size; } @container (min-width: 24rem) { .card { flex-direction: row; } } - ❌ A viewport media query that breaks the card when it's placed in a narrow sidebar
- ✅
-
Fluid values with
clamp(), not stepped jumps:padding: clamp(1rem, 3vw, 2.5rem);for space that scales; reserve breakpoints for structural change. -
Never fix heights on text containers; let content size the box.
- ✅
min-height: 20rem;(or nothing) - ❌
height: 20rem; overflow: hidden;— clips translated/user content and causes CLS
- ✅
-
Kill accidental horizontal scroll at the source: media
max-width: 100%; height: auto;,min-width: 0on flex/grid children that must shrink,overflow-wrap: break-wordon long strings.
Workflow
- Identify the layout's dimensionality per rule 2 and pick flex or grid.
- Build the narrow (360px) layout first with intrinsic patterns (rule 3).
- Add
@containeror@media (min-width)steps only where the content visibly breaks. - Replace remaining fixed values with
clamp()/minmax()where they should flex. - Validate: test at 360px, 768px, 1280px, and one in-between width; check no horizontal scrollbar, no clipped text, images scale, and layout shift stays visually stable (CLS ≤ 0.1 target).
Edge cases & failure modes
- Flex children overflowing: flex items default to
min-width: auto— setmin-width: 0on the shrinking child. auto-fitcollapsing with one item stretched full width: useauto-fillwhen empty tracks should be preserved.- Container queries need a named/typed container: without
container-type: inline-sizeon an ancestor,@containersilently never matches. 100vwcauses a scrollbar-width overflow on Windows: use100%or100dvw.- Legacy browser support required: container queries and
dvh/dvwneed a media-query fallback — state the assumption before using them.
References
Copy-paste layout recipes (shells, card grids, sidebars, holy grail, media objects): see references/patterns.md