Three frames don’t make a site fully responsive
A designer makes three frames: desktop, tablet, phone. Say 1440, 768, 375. Looks done — responsive design is covered, ready to hand off to development or build in a site builder. And often people make just two — desktop and phone — as if whatever’s between them doesn’t matter.
A user opens the site on a desktop (a laptop) — but not full-screen, in a window taking up half the display. Or they just drag the browser by its corner, or change the zoom to suit themselves. And somewhere between those three widths the layout slips: blocks overlap, text spills out, a button “escapes” off the edge.
And this isn’t only about desktop. Open the same site on a tablet or some other device and you’ll see one of two things. Either the layout breaks, because this width falls in a range nobody worked through. Or the desktop version is “crammed” into the mid-size format — everything’s in place, but the fonts and elements are shrunk to the point of being unreadable, because the desktop frame was scaled down proportionally to tablet size. There can be countless reasons a user’s window size differs: a smaller laptop, increased zoom, and so on. The point is that we don’t know in advance which device and which settings the user will use to view the site.
The problem isn’t that too few frames were drawn. The problem runs deeper — in not fully grasping that on the user’s side, a site doesn’t work at fixed sizes. It adapts across a range.
This is the first and most important shift in thinking — without it, neither design nor development will be any good.
Viewport — the key to understanding
When a designer says “I’m making the screen at 1440,” they mean the size of the frame in Figma. And they probably have “resolution” in mind — a term that gets thrown around in design communities, but not everyone who uses it really understands it.
Resolution is a physical property of the display panel. And here’s what matters: in the operating system (Windows, macOS, Linux), the user sets their own scaling — and that changes the size of the space all applications work in, the browser included.
The viewport is the visible area of the browser window where the layout is rendered. It’s a window the user reshapes however they like: maximizing it or shrinking it, dragging a corner, keeping the browser next to another window at half or two-thirds of the monitor, applying browser zoom. These are the viewing patterns on desktop. Every one of these actions changes the viewport instantly — and the page re-renders in real time.
On mobile devices there’s no such freedom to resize the window — but the sheer variety of devices sets the same requirement: the layout has to work across a fairly wide range. By current web standards, a minimum width of 320 pixels is still supported. And the maximum is effectively unlimited — just look at widescreen and ultrawide monitors.
There’s direct proof of the viewport, sitting in the HTML of literally every site:
<meta name="viewport" content="width=device-width, initial-scale=1">
This tag is there by default in any modern codebase and in every site builder — Tilda and Webflow include it out of the box. It tells the browser: take the real width of the viewing area — that same viewport.1 It’s the viewport — not the display panel’s “resolution” and not the frame’s size — that’s the actual value the layout is rendered against.2
Breakpoints define ranges, not points
Breakpoints are triggered by the width of the viewport, not the width of the screen and not the “resolution.” When you write @media (max-width: 768px) — or work in a builder where the breakpoints are already set for you — the browser compares 768 not against the panel’s diagonal and not against a number from the device’s spec sheet, but against the width of that same viewing area, which the user changes by resizing and zooming in real time.3 Under the hood of a builder, the code has the same @media rules, just written by someone other than you — and they’re still driven by that same viewport.
Ranges. Here’s where the second trap hides. It seems like 768px is a boundary where the frame switches over: below 768 one layout, above it another. As if there are only three stable states (the ones in Figma) and it’s enough to “draw” each one.
In reality there are intervals: 1440 down to 768, 768 to 375, 375 to 320. (In builders or other systems these intervals may differ!) But the general idea is the same: cover the entire range from 320px and up. Three frames from Figma are just three “screenshots” out of the whole range of states. And the user isn’t obliged to open exactly the width you designed and built for.
What matters for users is the entire range the layout has to work within. From 1440 to 768 isn’t one “tablet state” — it’s hundreds of possible intermediate widths in 1px steps. And at every one of them the page has to look right.
A frame in Figma shows what the layout looks like at one specific width. But it says nothing about how it behaves as it moves from one width to another. It’s a static snapshot of a single state. A few such snapshots don’t describe the transitions between them — and problems show up precisely in those in-between spots that nobody “drew” or tested.
Fixed sizes aren’t evil — the evil is when there’s no logic behind them
1440, 768, 375 are perfectly fine numbers on their own. The problem isn’t them — it’s whether the designer understands how the layout will behave at the in-between sizes.
Say the layout is built properly: autolayout applied (flex and grid), min-max sizes set on blocks. If on top of that the designer understands how responsive layout works — or works closely with a developer, iterating with feedback — then three or four frames are enough, sometimes just two. The developer will build out the behavior, because the logic is already baked into the layout.
But if the numbers are pulled out of thin air or “that’s what the course said,” and the design itself is illogical — the result gets worse the further the user’s real browser-window size is from the size of the frame you drew. And “further” is the usual case: OS scaling, browser settings, and so on. So the real viewport size rarely lands on a standard value. (How hardware specs turn into different numbers in software is a big topic — we’ll get into it in future posts.)
Height is telling too. In Figma you draw for one screen — a nominal 900px top to bottom. But in real life the browser’s address bar squeezes from the top, the system bar from the bottom, and on a phone there are also floating panels that hide and reappear as you scroll. The actual space for content can be both less and more than in a static design — and on mobile the viewport height changes right as you scroll. So even if the real viewport width happens to match the frame you drew, the height most likely won’t.
Key takeaways
- On the user’s side, a site works across a range, not at fixed frame sizes. Not 2-3-4 fixed widths - but any width from a continuous range.
- Breakpoints define intervals, and the whole range matters. There are hundreds of possible widths between 768 and 1440, and the page has to work at every one — and likewise across the entire range from 320px and up.
- A frame is a static snapshot of a single state. It shows what the layout looks like at one width but says nothing about the behavior between widths. And things break exactly at the non-standard values that weren’t worked through or tested.
- A breakpoint is driven by the viewport size. The proof is the meta viewport tag in every site’s code. The layout is rendered against the viewport size, and that size is affected by several different parameters the user is fully entitled to change to suit themselves.
- Fixed frame sizes aren’t the problem — the problem is no behavior logic behind them. Good design carries that logic: autolayout (flex and grid) and min-max sizes make blocks reflow and resize to the viewport within a given interval, so nothing breaks at in-between widths. And once the logic is baked in, three or four frames are enough.
- Viewport height varies too — and by a lot. Set a minimum height where nothing breaks: if there’s less room, plain scrolling solves it, and that’s fine. And plan for the opposite — when there’s plenty of height (a tablet in portrait, say): a hard-coded
100vhhere can stretch a block out and leave too much empty space. - A takeaway for two roles. For the designer — you have to engineer it: understand how the layout adapts across sizes and apply that logic at the design stage, rather than drawing static pictures. For the developer — the layout must support the entire range, not a set of frozen states.
Footnotes
-
MDN — “
<meta name="viewport">” — reference for the tag, where the notorious ~980px comes from without it, and whywidth=device-widthis the right value. ↩ -
MDN — “Viewport concepts” — what a viewport is, how the layout viewport differs from the visual one, and why it’s not the same as the screen. ↩
-
MDN — “Using media queries” — what
@mediaactually measures: the viewport width, not the device’s (the difference betweenwidthanddevice-width). ↩