Water in a Glass: One Page, Every Screen
A page is responsive by default — you have to work to break it. Most of this skill is not adding cleverness but removing the assumptions you made about a screen you happened to own.
The site looks superb on your monitor. Someone opens it on a phone: the text is unreadably small, one card sticks out past the edge, and there's a horizontal scrollbar — the sure sign that something inside is wider than the world it lives in.
Note what happened. You didn't fail to add responsiveness. A plain HTML document with no CSS at all already works at every width: text reflows, nothing overflows, everything fits. You had to write CSS to break that. Somewhere in your stylesheet is a number taken from the screen in front of you.
Responsive isn't a feature you add at the end. It's what a page does unless you stop it.
The line without which nothing works
Mobile browsers have a defensive habit: assuming a page was built for desktop, they render it at about 980 pixels wide and shrink the result, so old sites are at least legible. Your carefully built layout gets the same treatment — zoomed out and tiny.
One line in the head turns that off and tells the browser to use the device's real width, at its real scale. It's a meta tag named viewport with the content width=device-width, initial-scale=1. Without it, every media query you write is measured against a fiction. It's the first thing to check whenever a site looks "mobile-broken" in a way that makes no sense.
Start narrow
The convention is mobile-first: write the base styles for the narrow case, then use min-width media queries to add complexity as space appears.
Not fashion — three concrete reasons. The narrow case is the hard one, and solving the hard case first stops you from discovering at the end that your desktop layout can't be reduced. The narrow layout is usually the simple one — a single column — so it makes a better base with fewer overrides. And enhancements are additive: at 700 pixels you add a second column, rather than starting wide and undoing things.
The practical shape of it: a stylesheet where the top is the phone, and each media query below adds something for more room.
Where breakpoints come from
Not from a list of device widths. That list was already obsolete when it was written, and chasing it means designing for hardware instead of for content.
The honest method takes thirty seconds: build the component, then drag the browser window slowly from narrow to wide and watch. At some width the line lengths get uncomfortable, the cards get absurdly stretched, or something collides. That width is your breakpoint. It came from your content, so it stays true regardless of what phones ship next year.
You'll usually end up with two or three, not eight. A pile of breakpoints is a sign that fixed sizes are being patched at every width instead of made flexible once.
Units are the actual skill
Most responsiveness is decided by which unit you pick, long before any media query:
px— fixed. Correct for hairline borders and small fixed details. Wrong for font sizes: it ignores the user's browser font-size setting, which people with low vision genuinely rely on.rem— relative to the root font size. The default choice for type and spacing: it scales with the user's preference, and it's predictable because the root doesn't change as you nest.em— relative to the current element's font size. Useful inside a component whose padding should scale with its own text; treacherous when nested, since it compounds.%— relative to the parent's corresponding dimension.vw/vh— a hundredth of the viewport's width or height. Great for full-screen sections, andvhis a known trap on mobile, where the browser's own bars appear and disappear. The newerdvhaccounts for that.ch— the width of a "0" character.max-width: 65chis the most useful single line of typography CSS there is: it caps a text column at a comfortable reading length in whatever font is actually used.
And one function that removes most breakpoints outright: clamp(minimum, preferred, maximum). A heading set to clamp(1.75rem, 4vw, 3rem) grows smoothly with the window between two limits, instead of jumping at a breakpoint. Fluid instead of stepped, in one declaration.
Container queries: the real unit of adaptation
Media queries ask about the window. But a card doesn't care about the window — it cares how much room it was given. The same card might sit in a narrow sidebar and in a wide grid on the same page, and a media query cannot tell those apart.
Container queries fix exactly that: you mark an element as a container, and its children can then respond to its width. That makes a component genuinely portable — it adapts wherever you drop it, with no knowledge of the page around it. If you're learning frontend now, learn these alongside media queries rather than after them; they're the tool the older one was standing in for.
Images, the usual culprit
Images cause more responsive failures than layout does.
max-width: 100% with height: auto on every image is the baseline that prevents most overflow. Beyond that: srcset with sizes lets the browser choose an appropriately sized file instead of sending a 3000-pixel photo to a phone — a bandwidth decision as much as a layout one. aspect-ratio reserves the right space before the file arrives, which stops the page from jumping. And loading="lazy" on images below the fold defers what nobody has scrolled to yet.
Wider than width
Responsive means adapting to the person's conditions, and the window is only one of them. Modern CSS lets you respond to several others, each in one media query: prefers-color-scheme for dark mode, prefers-reduced-motion for people who get motion sickness from animations — for whom a decorative parallax is a physical symptom, not a preference — and prefers-contrast.
Two more conditions are decided by your units rather than queries. Someone who has set a larger default font size gets it only if you sized in rem. And someone zooming to 200%, which is extremely common, gets a usable page only if nothing is pinned to fixed pixel heights.
Then there's input. A finger is not a mouse: touch targets want to be about 44 pixels, and anything that only appears on hover is invisible on a phone — the hover menu that has no tap equivalent is one of the most common broken patterns on the web.
In practice
Build with the window narrow. Not at the end — from the first minute. Keep the browser at phone width while you work and widen it to check. This inverts the default failure, where the desktop version is finished and the phone version is an argument with yourself.
Make widening a habit with a cue. The How to Learn sphere's rule for habits is that the cue matters more than the intention: attach it to something that already happens. Finish a component, drag the window — every time, until you stop deciding to do it.
Hunt the overflow. When a horizontal scrollbar appears, something is wider than the viewport. Devtools will find it; a fixed width, a long unbroken string, or a negative margin is almost always the culprit.
Test on a real phone once a week. Device emulation in devtools is a fast first check, not proof: it doesn't reproduce real touch, real fonts, real network or the browser's own chrome. Load your work on an actual phone on actual mobile data and the list of things to fix writes itself.
Set max-width in ch on every text column. One line, and your typography stops being unreadable on wide screens.
Zoom the browser to 200% and use the page. Two minutes, and it finds the fixed heights you didn't know you'd written.
Check yourself
Close the article and answer in your own words:
- Why is a page with no CSS already responsive, and what breaks it?
- What does the viewport meta tag do, and what happens without it?
- Give three reasons to write mobile-first rather than desktop-first.
- Where does a breakpoint actually come from?
- Why should font sizes be in
remrather thanpx? - What can a container query do that a media query can't?
- Name three conditions besides window width that a responsive page should adapt to.
In short
- A document is responsive by default; responsive work is mostly removing the fixed assumptions you added.
- The viewport meta tag is the precondition — without it, mobile browsers render a fiction and your media queries measure it.
- Mobile-first because the narrow case is the hard case, the simple base, and the one that composes additively.
- Breakpoints come from watching your own content break as you drag the window, not from a list of device sizes.
- Units decide most of it:
remfor type and spacing,chfor reading width,dvhovervhon mobile, andclamp()instead of stepped jumps. - Container queries let a component adapt to the space it was given, which is what makes it portable.
- Images need
max-width: 100%,srcset,aspect-ratioand lazy loading, or they break both layout and bandwidth. - Adapt to more than width: colour scheme, reduced motion, larger default fonts, 200% zoom, and fingers instead of mice.