#Responsive design and mobile-first
AddedResponsive design means one page, one URL and one set of HTML that adapts its layout to the space it's given, instead of a separate m. site for phones. The term comes from Ethan Marcotte's 2010 article, which named three ingredients that are still the core of it: fluid layouts (sizes in relative units, not fixed pixels), flexible media (images that never overflow their container), and media queries (CSS that applies only when a condition, such as a minimum width, is true). Modern CSS adds a fourth: layouts that adapt on their own (flex-wrap, auto-fit grids, clamp()) and container queries that respond to a component's own width rather than the whole screen.
Mobile-first is the usual way to write it. The base CSS, outside any media query, is the small-screen layout: usually a single column, which is also what plain block layout gives you for free. Then min-width media queries add layout as space grows. The alternative, desktop-first, writes the wide layout as the base and uses max-width queries to undo it on small screens.
Mobile-first wins for three reasons: the small screen (often the slowest device on the worst network) gets the simplest CSS with nothing to override; each query only adds, so rules don't fight each other; and it forces you to decide what matters most when space is short. A useful mental model: the base styles are the content in its simplest arrangement, and every breakpoint is an enhancement for when there's room. Put breakpoints where the content starts to look wrong (lines too long, cards too wide), not at the widths of particular phones.
/* Base: small screens. Plain block flow, cards in one column. */
.page { display: grid; gap: 12px; }
.cards { display: grid; gap: 8px; }
.side, .card { padding: 8px; }
.side { background: #e0e7ff; color: #312e81; }
.card { height: 40px; background: #dcfce7; color: #14532d; }
/* Enhancement: from 600px there is room for a sidebar and two columns. */
@media (min-width: 600px) {
.page { grid-template-columns: 180px 1fr; }
.cards { grid-template-columns: 1fr 1fr; }
}<div class="page">
<nav class="side">Filters</nav>
<div class="cards">
<div class="card">Card 1</div><div class="card">Card 2</div>
<div class="card">Card 3</div><div class="card">Card 4</div>
</div>
</div>- Read the base CSS as "the phone layout".
.pageand.cardsare grids with no columns, so each is a single column of full-width items: at a 360px viewport the sidebar and every card are336pxwide (360 minus the body's 12px margins), stacked. - Nothing in the base CSS has to be undone later: there are no widths, floats or columns to reset. That's the point of mobile-first.
@media (min-width: 600px)adds two column definitions. At 800px it matches: the page gets a180pxsidebar column plus1fr(776 − 180 − 12 = 584px), and the cards split that into two columns of(584 − 8) / 2 = 288px, measured.- The test runs the same demo at 360px and 800px. On this page the Result frame is as wide as the text column, and media queries inside the frame see the frame's width: narrow your browser window and the live result switches layouts at 600px.
- The desktop-first version of the same page would put the two-column rules in the base and add
@media (max-width: 599px) { .page, .cards { grid-template-columns: none; } }to take them away, which is more CSS and a second place to keep in sync. The next example shows the two side by side.
You'll meet desktop-first in older codebases, and an interviewer may ask you to convert one into the other. It produces the same pixels; the difference is which rules have to override which.
/* Base: the wide layout. */
.page { display: grid; gap: 12px; grid-template-columns: 180px 1fr; }
.cards { display: grid; gap: 8px; grid-template-columns: 1fr 1fr; }
.side { padding: 8px; background: #fee2e2; color: #7f1d1d; }
.card { padding: 8px; height: 40px; background: #fef3c7; color: #78350f; }
/* Small screens: undo what the base did. */
@media (max-width: 599px) {
.page, .cards { grid-template-columns: none; }
}<div class="page">
<nav class="side">Filters</nav>
<div class="cards">
<div class="card">Card 1</div><div class="card">Card 2</div>
<div class="card">Card 3</div><div class="card">Card 4</div>
</div>
</div>- The base CSS is now the wide layout, so a phone downloads and applies it first.
- Below 600px,
@media (max-width: 599px)resets both grids withgrid-template-columns: none, which gives back the single column:336pxat 360px, the same as the mobile-first version. - At 800px the query doesn't match and the base layout shows:
180pxsidebar,288pxcards, again identical. - Every property set in the base and changed on mobile needs an "undo" in the query. On a real stylesheet that's dozens of resets (floats, widths, margins), and forgetting one is a classic phone-only bug. Note
599pxrather than600px: with old-stylemin-/max-queries you must avoid both matching at exactly 600px, which Media queries shows measured, and the range syntax solves.
With the mobile-first CSS above (@media (min-width: 600px)), which layout does a viewport of exactly 600px get? And one of 599px?
Show answer
600px gets the two-column layout; 599px gets one column. min-width is inclusive.
.cards { display: grid; gap: 8px; }
@media (min-width: 600px) { .cards { grid-template-columns: 1fr 1fr; } }
.cards > div { height: 30px; background: #dcfce7; }<div class="cards"><div></div><div></div></div>min-width: 600pxmeans "the viewport is at least 600px wide", so it's true at600pxitself: the two cards share a row.- At
599pxit's false and the cards stack. - In the range syntax this is written
(width >= 600px), which reads the way it behaves.