Foundations / Responsive
Design for the content, not the device.
There is no fixed set of screens, and there never was. Start from the smallest reasonable screen, let the content decide where it strains, and reach for a fixed breakpoint last. A layout keyed to a few famous device widths breaks the moment someone opens a split view, rotates a foldable, or reads at 200% zoom.
Mobile first is a discipline, not a phone rule
The small screen first forces the hard question early: what is genuinely essential. What survives is the core; what a wider screen adds is progressive enhancement layered on a base that already works. Start wide and squeeze down and you treat the small screen as an afterthought, which is where most people actually are.
Write the small-screen layout with no media query. That is the default every browser gets, and it must stand on its own.
Each min-width query only adds: more columns, a sidebar that was stacked. Never subtract from wide to reach narrow.
Add a breakpoint where the layout actually strains, when a line runs long or a card cramps, not where a popular phone happens to end.
Five breakpoints, each a structural move
The system ships exactly five named tokens, all min-width thresholds. Below the first is the base, the no-query mobile layout. Treat them as the coarse points where the whole shape of a page may shift, not a grid you snap every element to.
| Token | Min width | Structural role |
|---|---|---|
base | 0 | The default. Single column, thumb-reachable, no media query. Everything enhances this. |
--breakpoint-sm | 576px | Large phones. Room for a two-up grid and side-by-side fields. |
--breakpoint-md | 769px | Tablets. The first real shift: a stacked shell becomes two panes, nav moves inline. |
--breakpoint-lg | 992px | Laptops. Persistent sidebars, three columns, the full desktop chrome. |
--breakpoint-xl | 1200px | Wide desktops. Cap line length and center the shell; comfortable, not stretched. |
--breakpoint-2xl | 1366px | Very wide. Add breathing room and max-widths, not more columns for their own sake. |
Rule Key page-shell breakpoints to the five named min-width tokens, and no others.Why There is no xs because the base below sm needs no query to name it. A media condition cannot read a custom property, so the query carries the literal value, @media (min-width: 769px), while the token stays the single source of that number.
Media query or container query
A media query asks how wide the viewport is; right for the page shell, wrong for a component. A card does not care how wide the window is, it cares how much room it was given. A container query lets an element respond to its own container, so the same card lays out one way in a sidebar and another in the main column. Baseline since 2023.
- Asks about the whole viewport.
- Right for the page shell: global grid, top nav, where the sidebar lives.
- Home of capability and preference features:
prefers-color-scheme,prefers-reduced-motion,pointer,hover. - Keyed to the five breakpoint tokens.
- Asks how much space this element was actually given.
- Right for reusable parts: cards, tiles, widgets that appear in many slots.
- The parent opts in with
container-type; children size tocqwunits. - The component becomes portable: drop it anywhere and it adapts.
Rule Match the query to its scope: a media query against a token for the page shell, a container query for any part that must work in more than one slot.Why A media query asks how wide the viewport is, which is what the shell responds to. A container query asks how much room an element was actually given, which is what a portable component responds to.
The best breakpoint is the one you never wrote
Between the structural thresholds, layout should flow, not jump. Intrinsic layouts lean on what CSS already does: a grid that fits as many columns as comfortably fit, type and spacing that scale as a range. The result adapts at every width, not just five, with far fewer media queries.
Live, one rule, no media query: grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)). Narrow the docs panel and the columns reflow on their own. Reach for this before any breakpoint.
Fluid type scales with the screen
This heading is clamp(1.5rem, 3vw + 1rem, 2.75rem): it grows and shrinks smoothly, held between a floor and a ceiling. Use clamp(), min(), and max() for type, spacing, and measure to erase the step changes a stack of breakpoints would cause.
Rule Reach for fluid sizing and an intrinsic grid before you write any breakpoint.Why An auto-fit grid and clamped type adapt at every width, not just at the five thresholds, and with far fewer queries to keep in sync.
repeat(auto-fit, minmax(160px, 1fr)) for columns, clamp() for type and spacing.Safe areas and notches
On modern phones the screen is not a clean rectangle. Notches, rounded corners, and the home indicator carve in, and a layout drawn to the physical edge can bury a primary action under a system gesture. Honor the insets for anything anchored to a screen edge, and add the inset to your spacing token rather than replacing it.
The dashed inset is the safe area. The button clears the home indicator because its padding is calc(var(--space-4) + env(safe-area-inset-bottom)), not a bare token. Insets are unlocked by viewport-fit=cover; on a device with none, env() resolves to zero and spacing is unchanged.
Thumb-reach and target sizing for these anchored controls live in Foundations / Spacing. This page claims only the responsibility: keep every screen-anchored control clear of system chrome.
Rule Honor the safe-area insets for anything anchored to a screen edge.Why Notches, rounded corners, and the home indicator carve into the screen, so a control drawn to the physical edge can land under a system gesture.
calc(var(--space-4) + env(safe-area-inset-bottom)). Where a device has no insets, env() resolves to zero and the spacing is unchanged.env() alone, or drawing to the physical edge. Where there are no insets the spacing collapses to zero and the control can fall under system chrome.Sources
Mobile-first and progressive enhancement: Wroblewski, Mobile First (lukew.com) and Marcotte, Responsive Web Design (alistapart.com). Let content, not device class, set breakpoints: web.dev responsive basics (web.dev). Container queries, Baseline since 2023: MDN (developer.mozilla.org) and web.dev (web.dev/blog/cq-stable). Intrinsic layouts: Jen Simmons (talks.jensimmons.com). Fluid sizing and reflow: MDN clamp() and grid auto-fit / minmax (developer.mozilla.org). Safe areas: MDN env() (developer.mozilla.org) and Apple HIG Layout (developer.apple.com). SPA the five values and the reading of them as coarse structural ceilings are specific to this system's tokens; intrinsic web design is Simmons' coinage, not a spec term.