One page, every screen
More than half of all web traffic comes from phones, so a layout that only works at desktop width fails most visitors. There is also no separate mobile version of a stylesheet, so one file must adapt itself, and media queries are the mechanism.
A media query applies CSS only while a condition about the device is true.
.cards {
display: grid;
grid-template-columns: 1fr; /* phones: one column */
}
@media (min-width: 640px) {
.cards { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
.cards { grid-template-columns: repeat(3, 1fr); }
}| Screen width | Columns |
|---|---|
| below 640px | 1 |
| 640px and up | 2 |
| 1024px and up | 3 |
Read @media (min-width: 640px) as at 640px wide and up. The widths where the layout changes, here 640 and 1024, are called breakpoints.
Writing base rules for the smallest screen and layering desktop upgrades on with min-width queries is called mobile-first, and it is the standard approach. Phone layouts are the simple ones, so enhancing upward beats gutting downward.
The tag that makes it all work
One line in the head, part of the lesson 2-1 skeleton family, or phones will pretend to be 980px-wide desktops and shrink everything to fit.
<meta name="viewport" content="width=device-width, initial-scale=1">
| Part | Meaning |
|---|---|
width=device-width | match the device's real width |
initial-scale=1 | start at 100% zoom |
It tells the browser to make the layout viewport match the device's real width at full zoom, and every responsive page needs it.
Skipping it means carefully written media queries never fire on phones, because the browser reports a false width. The failure is confusing precisely because the CSS is correct.
Testing without a drawer full of phones is what DevTools is for, since its device toolbar resizes the viewport freely. Lesson 9-1 covers it properly.
What base rules cover
In mobile-first CSS, the rules written outside any media query target all screens, starting from the smallest.
Base rules always apply, and min-width queries then add overrides as the screen grows.
| Rule location | Applies at |
|---|---|
| outside any query | every width |
inside min-width: 640px | 640px and up |
inside min-width: 1024px | 1024px and up |
That layering is the whole mobile-first pattern, meaning a simple base plus targeted upgrades. Since later rules override earlier ones at equal specificity, the queries must come after the base rules in the file.
The breakpoint logic as a function
pickColumns(width) reproduces the .cards breakpoints, returning 1 below 640px, 2 from 640px, and 3 from 1024px.
function pickColumns(width) { if (width >= 1024) return 3; if (width >= 640) return 2; return 1; } console.log(pickColumns(390)); console.log(pickColumns(640)); console.log(pickColumns(800)); console.log(pickColumns(1024));
Output
1 2 2 3
| Width | Columns |
|---|---|
| 390 | 1 |
| 640 | 2 |
| 800 | 2 |
| 1024 | 3 |
The largest breakpoint is checked first, then the rest fall through, which mirrors how the last matching media query wins in CSS.
min-width: 640px matches a width of exactly 640, so the comparison uses >= rather than >. Getting that boundary wrong produces a layout that is broken at exactly one width, which is the hardest kind of bug to notice.
When queries work in DevTools but not on a phone
Media queries that work in desktop DevTools while a real phone shows a shrunken desktop layout mean the viewport meta tag is missing.
<meta name="viewport" content="width=device-width, initial-scale=1">
| With the tag | Without it |
|---|---|
| the phone reports its real width | it reports a pretend 980px |
min-width queries fire | they see 980px and never fire |
It goes in the head, next to charset and title. Because the phone lays the page out at a fake 980px and then zooms out to fit, the result looks like a working desktop page rendered too small, rather than like broken CSS.