Breakpoints come from the content, not from devices
Naming breakpoints after phones ages badly
A stylesheet with $tablet: 768px was written when there was a tablet at 768 pixels. There are now devices at every width from 320 to 3840, foldables that change width mid-session, split-screen multitasking, and browser windows people resize for no reason.
The method that does not age: widen and narrow the browser slowly, and put a breakpoint wherever the layout stops working. Not where a device sits. Where your content breaks.
This produces breakpoints at odd numbers — 540, 720, 960, 1180 — and that is the sign you did it right. Round numbers usually mean somebody copied them.
What breaking looks like
The specific failures to watch for as you resize:
- The measure exceeds 75 characters. Time to cap the column or add a second one.
- The measure drops below about 35 characters. Time to drop a column.
- A heading breaks into a bad shape — one word on the second line, or a wrap mid-phrase.
- A horizontal layout has less than about 200 pixels per item. Cards stop working somewhere around there.
- Navigation runs out of room. Usually the first thing to break on the way down.
- A table starts scrolling horizontally. Which means it needs a different presentation, not a smaller font.
Each of these is a measurement you can take, which is what makes this a technique rather than an aesthetic judgement.
Container queries change the unit of the question
A media query asks how wide is the window. That is the wrong question for a component, because the same card might appear in a wide main column, a narrow sidebar and a three-across grid, all on the same page at the same viewport width.
Container queries ask how wide is the space this component has been given:
.card-wrap { container-type: inline-size; }
@container (min-width: 28rem) {
.card { display: grid; grid-template-columns: 8rem 1fr; }
}The card becomes horizontal when its own container is wide enough, wherever that container happens to be. This has been available across the major browsers since 2023, and it is the most significant change to responsive layout in a decade — because it means a component can carry its own responsive behaviour and be reused without the page having to know.
Many layouts need no breakpoints at all
Before reaching for a query, check whether the layout can simply be intrinsic:
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
gap: var(--space-4);
}That reads: as many columns as fit, each at least 16rem, sharing the space equally. One declaration, no breakpoints, correct at every width including ones that do not exist yet. Combined with clamp() for type sizes from the type block, a surprising amount of responsive design needs no media queries whatsoever.
The general principle: prefer a rule that describes the constraint over a rule that enumerates the cases.
Small first, for a design reason
Mobile-first and desktop-first are code organisation preferences and neither is meaningfully better as CSS. What is not merely a preference is designing the small version first, and the reason is nothing to do with implementation.
A 360-pixel column forces prioritisation. There is room for one thing at a time, so you have to decide what comes first, second and third — which is the hierarchy work from the first block, made unavoidable. A 1440-pixel canvas lets you avoid the decision by putting four things side by side, and the resulting design has no hierarchy because nobody ever had to choose.
Going small to large is therefore addition: you have a working priority order and you are deciding what can now sit beside what. Going large to small is subtraction under pressure, usually the afternoon before a deadline, and it is where features get hidden behind a menu because nobody can face rethinking the order.
The honest limit
Some content genuinely does not fit a phone. A complex comparison table, a dense schematic, a spreadsheet, a seat map.
The answer there is not a clever responsive trick; it is a different presentation for the small screen — a card per row instead of a table, a filtered subset with a link to the full version, a pinch-zoomable image with explicit controls. Pretending otherwise produces a table at 9 pixels that nobody can read, which is worse than admitting the constraint and designing for it.
The one thing to keep
Put breakpoints where your content measurably breaks rather than where devices sit, prefer intrinsic rules and container queries over enumerated cases, and design the small screen first because a narrow column forces the hierarchy decisions a wide one lets you avoid.
Before you move on
A card component switches to a horizontal layout at a 768px media query. In the main column it looks right; in a 300px sidebar on a wide desktop it also goes horizontal and becomes cramped. What is the correct fix?
Pick the one you would defend. Nobody sees your answer.