---
name: responsive-layout
category: frontend
description: Use when you build or change a layout, page, section, navigation, table, form or image - mobile-first rules, breakpoints and the overflow traps.
source: pbakaus/impeccable (Apache-2.0), vercel-labs/web-interface-guidelines (MIT), ibelick/ui-skills (MIT), nextlevelbuilder/ui-ux-pro-max-skill (MIT), adapted
---
# Responsive Layout
## Overview
Every layout is designed at 360px first, then widened. Desktop-first layouts that get "patched down" with `max-*:` overrides are where almost every overflow bug comes from, because the narrow case was never actually built — it was reverse-engineered from the wide one under time pressure.
**Core principle:** Build the 360px layout first, make it correct, then add `md:`/`lg:` to use the extra space. Never write desktop styles first and patch down.
## Rules
- Unprefixed Tailwind utilities are the phone layout. `sm:` (640) `md:` (768) `lg:` (1024) `xl:` (1280) `2xl:` (1536) add capability upward — they never subtract it.
- Breakpoints follow Tailwind's default scale; pick one by where *your content* breaks (a nav that collapses, a table that gets cramped), not by matching a specific device.
- Never `max-*:` as the primary layout strategy — it means the base case is the wrong one.
## Layout primitives
- **Container**: `mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8` for page content; narrower for prose (`max-w-prose` / `max-w-3xl`) in marketing sections.
- **Section rhythm**: `py-12 md:py-16 lg:py-24` — more space grows with viewport, it doesn't appear from nothing at `md`.
- **Grid recipes**: `grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3` for card/list grids; `grid-cols-[repeat(auto-fit,minmax(min(100%,16rem),1fr))]` when the item count is unknown and a fixed column count would leave gaps or force scroll (ui-guard's allow-listed arbitrary value).
- **Flex rows that wrap**: `flex flex-wrap gap-2` for chip/tag/button rows — never a fixed-height row that clips.
- **Sidebars**: stack below `lg` (`flex flex-col lg:flex-row`), sidebar full-width on phone, fixed-width only at `lg:` and up.
- **Container queries**: an organism placed in genuinely different-width contexts (a dashboard grid cell vs. a full-width page) sizes off its own box — `@container` on the wrapper, `@md:`/`@lg:` on the organism — instead of the page's breakpoints; the viewport can be 1440px while the card is 300px wide.
## Overflow trap catalogue
Each trap below was reproduced in a real browser at 360–1440px and its fix confirmed clean.
| Symptom | Cause | Fix |
|---|---|---|
| Flex/grid item refuses to shrink, pushes siblings off-screen | Flex/grid items default to `min-width: auto`; a `truncate`/`whitespace-nowrap` descendant sets that item's min-content to its full unbroken width | `min-w-0` on the item that holds the text (not just the text itself); `shrink-0` on siblings that must stay fixed |
| Page scrolls sideways with no single element flagged as overflowing | A long word or URL with no space for the browser to wrap on — the block keeps its layout width while the text spills out of it, so `browser_set_viewport` reports that the page scrolls horizontally but names no overflowing element. Search the page for long unbroken strings | `break-words` (`overflow-wrap: anywhere`) on the text node; never `break-all` (it breaks mid-word even when wrapping wasn't needed) |
| A panel is too wide below some breakpoint | A literal pixel width (`w-[600px]`) | `w-full` + a `max-w-*` cap so it still has a ceiling on wide screens |
| Image forces horizontal scroll | Something overrides Tailwind v4's own preflight, which already sets `img, video { max-width: 100%; height: auto }` — a stray `max-w-none`, a reset library, or a hard inline `maxWidth` | Give the image `w-full h-auto max-w-full`, explicit `width`/`height` attributes (prevents CLS), and `object-cover` + `aspect-*` when it fills a fixed box. A bare `` with no extra classes does **not** overflow by default in Tailwind v4 — if one does, something upstream overrides preflight |
| A row of buttons/chips runs off the right edge | `whitespace-nowrap` on the row instead of letting items wrap | `flex flex-wrap gap-2` — wrap the row, don't force it onto one line |
| A decorative shape (blob, ring, gradient) pokes past the viewport edge even on desktop | An absolutely-positioned decoration with a negative offset (`-right-10`) and no clip on its own stacking context | `overflow-x-clip` on the **section** that owns the decoration, never on `body` (that breaks every `position: sticky` on the page) |
| Full-bleed section overflows as soon as it's not a direct child of `body` | `w-screen` (100vw) ignores every ancestor's padding/offset — it ties to the viewport, not the nearest positioned parent, so it overflows the moment it's nested inside anything with horizontal padding | `w-full` (ties to the parent instead); reach for `w-screen` only with a negative-margin bleed technique on a direct full-width wrapper, and test it |
| `grid-cols-3` (or any fixed count) is unusable at 360px | Short, wrapping text in a 3-up grid just looks cramped — it does **not** overflow by itself. It becomes an actual overflow once any cell holds one long unbreakable token (a code snippet, a slug, a username): grid items default to `min-width: auto` too, so that one cell's min-content can force the whole track wider than the viewport | `grid-cols-1 sm:grid-cols-2 lg:grid-cols-3` (mobile-first column count) **and** `min-w-0 break-words` on every cell, so neither the column count nor a stray long token can force the overflow |
| A data table forces the whole page to scroll sideways | The `