Breakpoints: Content-Driven vs. Device-Driven
Choosing where layouts shift based on what the content needs—not which device it runs on—is one of the most consequential decisions in responsive design.
7 min read
The full lesson
Every responsive layout has moments where something must shift — a sidebar collapses, a two-column grid stacks, a font grows. The point where that shift happens is called a breakpoint. For most of responsive design’s history, designers picked breakpoints by matching common device widths: 320px for older iPhones, 768px for tablets, 1024px for laptops. That feels logical, but it produces layouts that fit imaginary buckets instead of actual content. The better approach — content-driven breakpoints — places each shift where the content itself starts to break, not where a device spec says it should.
Why Device-Driven Breakpoints Fail
The device-driven model was popular because it was simple. Pick three or four common screen widths, write a media query for each, and ship. It worked when the device landscape was small — a few phone sizes, one tablet, one laptop.
That landscape is now enormous. A typical site reaches watches, foldables, TVs, kiosks, car dashboards, and a 4K monitor with a narrow browser window open on it. No fixed list of device widths covers that range reliably.
More importantly, device-driven breakpoints confuse two different questions:
- Which device is this? — a question about hardware
- Does this layout still work? — a question about the design
Those questions have different answers. A two-column text layout might start to look wrong at 680px on one page and at 820px on another, depending on line length, image proportions, and sidebar content. No device-width bucket captures that.
The Content-Driven Alternative
Content-driven breakpoints are set by watching when a layout actually stops working — as the viewport shrinks or grows — and placing the media query at that natural failure point.
The workflow is straightforward:
- Build the component without any constraints.
- Slowly resize the viewport (or the container, with container queries).
- Note the exact width where something breaks — text gets too dense, a button wraps awkwardly, an image loses its meaningful shape.
- Place the breakpoint there, at the precise pixel or rem value where the fix is needed.
- Repeat until the layout holds across the full range.
The breakpoints you end up with will be specific, irregular numbers like 52rem or 71ch. That is correct. They describe your content, not a device category.
Major vs. Minor Breakpoints
Content-driven design has two kinds of layout shifts.
Major breakpoints restructure the whole page skeleton. Examples: switching from one column to many, collapsing a navigation drawer, toggling between a card grid and a list view. These tend to be few in number and have the biggest visual impact.
Minor breakpoints are small, targeted adjustments. Examples: slightly increasing font size, loosening a padding value, changing a flex-wrap threshold. These should be narrow and surgical — not broad layout rewrites.
A well-structured CSS file might have two or three major breakpoints and a handful of minor ones, all at irregular, content-specific values.
Do
/* two-col threshold: card text crowded below this */.Don't
Viewport Queries vs. Container Queries
The biggest shift in breakpoint thinking in recent years is the arrival of container queries. Viewport media queries (@media (min-width: ...)) answer a question about the page. Container queries (@container (min-width: ...)) answer a question about the component’s own available space.
This distinction matters for reusable components. Consider a card:
- In a full-width layout, the card has 900px — it should show an image alongside text.
- In a sidebar, it has 240px — it should stack vertically.
- In a modal, it might have 480px — a third layout entirely.
With viewport queries, the card has no way to know which context it is in. With container queries, the card responds to its containing block — wherever it is placed on the page.
.card-container {
container-type: inline-size;
}
@container (min-width: 36rem) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
Container queries reached broad browser support in late 2023. They should now be the default tool for component-level responsiveness. Viewport queries remain the right choice for page-skeleton decisions — overall layout structure that depends on the full viewport, not on a single component’s context.
Setting Breakpoints in Practice: Rem vs. Px
Breakpoints written in px are static. They do not respect the user’s root font-size preference. A user who has set their browser default to 20px will hit the same breakpoint thresholds as someone at 16px — even though their actual readable content width is different.
Breakpoints in rem stay in sync with typography. A breakpoint at 52rem means “52 times the user’s preferred font size,” so layout and readability move together. This is especially important for accessibility: users who increase their font size to 200% (required for WCAG 2.2 reflow) should naturally trigger narrower layout thresholds without any custom overrides.
| Unit | Respects user font-size pref | Scales with content | Recommended use |
|---|---|---|---|
px | No | No | Fixed UI chrome (borders, icons) |
rem | Yes | Yes | Breakpoints, spacing, container thresholds |
em | Yes (relative to parent) | Yes | Component-local spacing |
ch | Yes | Yes (character-width) | Prose line-length constraints |
ch units are especially useful for text containers. max-width: 75ch constrains a paragraph to roughly 75 characters per line — the typographic sweet spot — at any font size.
Fluid Ranges Between Breakpoints
Content-driven breakpoints do not have to be binary on/off switches. CSS clamp() lets values transition smoothly between two breakpoints, removing the abrupt jumps that make layouts feel mechanical.
/* Font size scales fluidly from 1rem at 20rem viewport to 1.25rem at 80rem viewport */
font-size: clamp(1rem, 0.875rem + 0.625vw, 1.25rem);
The same pattern applies to spacing, column counts, and component sizing. The result is a layout that feels designed for every viewport width, not just a few. Use clamp() for gradual transitions. Reserve explicit breakpoints for structural changes that cannot be smoothly interpolated.
Coordinating Breakpoints Across a Design System
When a design system serves dozens of product teams, content-driven breakpoints need coordination. Left to each component author independently, they proliferate and become inconsistent.
A practical approach is a two-tier system:
Tier 1: Layout tokens — a small set of named breakpoints for page-skeleton decisions, defined once in the system. Call them --bp-compact, --bp-comfortable, and --bp-spacious instead of sm, md, lg. The names describe content behavior, not device categories.
Tier 2: Component container queries — each component manages its own layout response relative to its container. These are not shared across the system, because each component has its own content geometry.
This keeps the page level coherent while allowing components to stay genuinely portable. In W3C DTCG token format, layout tokens follow the same $value / $type structure as color and spacing tokens, so they travel with the system to any platform.
Testing Breakpoints Thoroughly
A content-driven breakpoint is only as good as the content tested against it. Build these habits:
- Test with real content at extremes — a 90-character headline, a 3-character username, a one-word product description. Edge cases reveal whether a breakpoint was set against real content or idealized placeholder text.
- Test at 200% browser zoom (the WCAG 2.2 reflow threshold, equivalent to a 320 CSS px effective width). Many breakpoints that look fine at native size fail when a user zooms in.
- Test with system font size overrides — set the browser default to 20px or 24px and verify that rem-based breakpoints still fire at the right moment for the actual content width.
- Use browser DevTools responsive mode to sweep continuously, not to jump between preset device sizes. The continuous sweep reveals failures that live between the presets.
Do
Don't
Documenting Breakpoints for Handoff
Breakpoints are frequently lost in the design-to-development handoff. They live in the space between screens — the intermediate widths that static mockups never show. To prevent that:
- Document breakpoints as ranges with annotated rationale, not just pixel values. “Sidebar collapses below 52rem because the text column becomes too narrow for comfortable reading” is actionable. “768px breakpoint” is not.
- Include in-between viewport states in Figma alongside standard screen sizes. A frame at 600px viewport width, mid-transition, communicates intent far better than two endpoint states alone.
- In Storybook, add a viewport addon configured with the actual content-driven breakpoint values so developers can verify components at the exact thresholds the design system specifies.
Living documentation co-located with components — rather than a separate PDF that drifts — is the modern standard. When breakpoints change, the Storybook viewport config updates alongside the code.