Explain mobile-first vs desktop-first media query strategy
Advertisement
🧩 Scenario
Architecture Walkthrough
Mobile-First Uses min-width
In a mobile-first strategy, the styles outside any media query describe the smallest layout, and min-width queries progressively add complexity as more space becomes available.
.layout {
display: grid;
gap: 16px; /* single column base */
}
@media (min-width: 768px) {
.layout {
grid-template-columns: 240px 1fr;
}
}
The base is the simplest possible layout, and each query is additive. Nothing is being undone; capability is being layered on.
Desktop-First Uses max-width
In a desktop-first strategy, the base styles describe the largest layout, and max-width queries strip complexity away as space is lost.
.layout {
display: grid;
grid-template-columns: 240px 1fr;
gap: 16px;
}
@media (max-width: 767px) {
.layout {
grid-template-columns: 1fr;
}
}
Here each query is subtractive: it exists to undo something the base already declared. That is the essential difference, and every other tradeoff follows from it.
Why Mobile-First Is the Default
Four reasons, in rough order of importance.
Less overriding. Small screens need the simplest layout, so making that the base means the fewest declarations to reset. Desktop-first inverts this: the smallest devices parse the most overrides, and every wide-screen property has to be explicitly undone below the breakpoint. Multi-column, absolute positioning, and fixed widths all need a counterpart max-width rule.
Content-out thinking. Unstyled HTML already stacks in a single column, so mobile-first works with the grain of the document rather than against it. Starting from the constrained case also forces prioritisation decisions early, when they are cheap.
Additive change cost. Adding a wide-screen refinement in a mobile-first sheet is a new min-width block that touches nothing existing. Adding the same refinement in a desktop-first sheet means locating and updating the max-width rules that already override those properties, and the more breakpoints exist the more places have to be checked.
Modern CSS reduces the need for queries at all. clamp(), min(), max(), fr units, and repeat(auto-fit, minmax()) handle continuous scaling without breakpoints. Those techniques are all naturally expressed as a fluid base that a min-width query can refine, which fits mobile-first and sits awkwardly in a desktop-first sheet.
The performance argument sometimes made, that mobile devices download less CSS, is not accurate: the whole stylesheet is downloaded either way and non-matching media queries still get parsed. The real saving is in the number of declarations that must be applied and then overridden, which is smaller but real.
Ordering Matters and Overlaps Cause Bugs
Media queries do not change specificity. A rule inside @media (min-width: 768px) has exactly the specificity of its selector, so when two queries both match, source order decides.
That means min-width queries must appear in ascending order. If a min-width: 1024px block precedes a min-width: 768px block, both match on a 1200px viewport and the later, smaller-breakpoint rule wins, which is the opposite of the intent. Desktop-first max-width queries must be ordered descending for the same reason.
The other ordering trap is overlapping boundaries. Writing max-width: 768px and min-width: 768px as adjacent breakpoints means both match at exactly 768px, so the layout at that one width is decided by source order rather than by design. Use max-width: 767px with min-width: 768px, or better, use the modern range syntax @media (width < 768px) and @media (width >= 768px), which makes the boundary explicit and avoids off-by-one arithmetic entirely. Note that fractional viewport widths from zoom or scaling make the older 767.98px convention fragile, another reason to prefer range syntax.
Choosing Breakpoints
Breakpoints should come from where the layout breaks, not from device names. Device dimensions change every year and there are far too many of them to enumerate; the question worth asking is at what width a given component stops looking right. That produces a small number of breakpoints per component rather than a fixed global set applied everywhere.
Where a component's layout depends on its own width rather than the viewport's, container queries are the correct tool and remove the need for a viewport breakpoint at all.
Key Code Explained
/* MOBILE-FIRST: base is the simplest layout, min-width ADDS complexity */
.layout {
display: grid;
gap: 16px; /* single column, no template needed */
}
@media (min-width: 768px) {
.layout {
grid-template-columns: 240px 1fr;
}
}
@media (min-width: 1200px) {
.layout {
grid-template-columns: 280px 1fr 320px;
}
}
/* Ascending order is REQUIRED: both match at 1400px, the later one wins */
/* DESKTOP-FIRST: base is the widest layout, max-width UNDOES it */
.layout-df {
display: grid;
grid-template-columns: 280px 1fr 320px;
gap: 16px;
}
@media (max-width: 1199px) {
.layout-df {
grid-template-columns: 240px 1fr; /* undo the third column */
}
}
@media (max-width: 767px) {
.layout-df {
grid-template-columns: 1fr; /* undo the second column */
}
}
/* Descending order required, and every step is subtractive */
/* OVERLAPPING BOUNDARY BUG: both match at exactly 768px */
@media (max-width: 768px) { .a { color: red; } }
@media (min-width: 768px) { .a { color: blue; } }
/* Old fix: off-by-one, fragile with fractional widths from zoom */
@media (max-width: 767px) { .a { color: red; } }
@media (min-width: 768px) { .a { color: blue; } }
/* Modern fix: range syntax, explicit and exact */
@media (width < 768px) { .a { color: red; } }
@media (width >= 768px) { .a { color: blue; } }
/* Bounded range in one query instead of two conditions */
@media (768px <= width < 1200px) {
.tablet-only { display: block; }
}
/* Often no breakpoint is needed at all */
.fluid {
grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
font-size: clamp(1rem, 2.5vw, 1.25rem);
padding: clamp(1rem, 5vw, 3rem);
}
/* When the COMPONENT width matters, not the viewport */
.card-host { container-type: inline-size; }
@container (min-width: 400px) {
.card { display: grid; grid-template-columns: 120px 1fr; }
}
The two .layout blocks are the comparison to be able to write side by side. They produce identical results at every width, and the difference is entirely in direction: the mobile-first version states three layouts additively, while the desktop-first version states one layout and then removes parts of it twice. Adding a fourth breakpoint to the first is a new block; adding one to the second means auditing the existing overrides.
The overlapping-boundary example is worth memorising because it is a real bug that only manifests at one exact viewport width, which means it survives testing. Range syntax removes the arithmetic and states the intent directly, which is why it is the form to prefer in new code.
Tradeoffs
| Aspect | Mobile-first (min-width) | Desktop-first (max-width) |
|---|---|---|
| Base styles describe | Smallest layout | Largest layout |
| Query direction | Additive | Subtractive |
| Required order | Ascending | Descending |
| Overrides on small screens | Fewest | Most |
| Cost of adding a breakpoint | New block only | Audit existing overrides |
Fits fluid CSS (clamp, auto-fit) | Naturally | Awkwardly |
| Suits an existing desktop-only site | Requires restructuring | Lower initial effort |
What Interviewers Actually Check
- Whether you can write both strategies correctly for the same layout
- Whether you frame the difference as additive versus subtractive rather than just min versus max
- Whether you know media queries do not affect specificity, so ordering decides
- Whether you know about overlapping boundaries and the range syntax fix
- Whether you mention that modern fluid CSS and container queries reduce the need for breakpoints at all
Follow-Up Questions
- Do media queries affect specificity? What decides the winner when two matching queries set the same property?
- What does the modern range syntax add beyond avoiding off-by-one values?
- How do container queries change the mobile-first argument for component-level styling?
- What are
prefers-reduced-motion,prefers-color-scheme, andhovermedia features, and why are they not about screen size at all? - How would you choose breakpoints for a design system used by teams you do not control?
Common Candidate Mistakes
- Writing
max-width: 768pxandmin-width: 768pxas adjacent breakpoints, so both match at exactly 768px and the layout at that width is decided by source order - Placing mobile styles inside a
max-widthquery while the rest of the sheet is mobile-first, producing two competing strategies in one file - Ordering
min-widthqueries from largest to smallest, so on a wide viewport the smaller breakpoint's rules win because media queries do not raise specificity - Choosing breakpoints from device names rather than from the width at which the layout actually stops working, which produces a fixed global set that fits no component well
- Treating media queries as the whole answer to responsive design, when
clamp(),minmax(),auto-fit, and container queries handle much of it continuously and without breakpoints
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you write the same layout in both mobile-first and desktop-first form?
- Can you explain which direction each strategy overrides in, and why that matters for maintenance?
- Can you explain why
min-widthqueries must appear in ascending order? - Can you explain the off-by-one problem with overlapping breakpoints and the modern fix?
- Can you write the range syntax equivalent of a bounded breakpoint?
Summary
Mobile-first puts the smallest layout in the base styles and uses min-width queries to add complexity as space becomes available. Desktop-first puts the widest layout in the base and uses max-width queries to remove complexity as space is lost. The strategies produce the same result; the difference is that one is additive and the other subtractive.
Mobile-first is the default for concrete reasons. Small screens need the simplest layout, so making it the base minimises the declarations that must be overridden, and the smallest devices no longer parse the most overrides. It works with the grain of unstyled HTML, which already stacks in one column. Adding a wide-screen refinement costs a new block rather than an audit of existing overrides. And it composes naturally with clamp(), fr, and repeat(auto-fit, minmax()), which express continuous scaling as a fluid base that queries then refine.
Two mechanical points matter regardless of strategy. Media queries do not change specificity, so when two matching queries set the same property the later one wins, which means min-width queries must be ordered ascending and max-width descending. And overlapping boundaries such as max-width: 768px beside min-width: 768px leave one exact viewport width decided by source order; the modern range syntax (width < 768px) and (width >= 768px) states the boundary exactly and removes the off-by-one arithmetic. Finally, breakpoints should come from where the layout breaks rather than from device names, and where a component depends on its own width, container queries replace the viewport breakpoint entirely.
Why is mobile-first considered the default?
Because the base styles are the simplest layout, progressive enhancement means less CSS to override, and mobile devices parse fewer overridden declarations. It also matches how content naturally stacks with no layout applied.
Can you mix min-width and max-width?
You can, and range syntax makes it clean for genuinely bounded rules. But mixing them arbitrarily makes the cascade hard to follow, so pick one direction as the default and use the other only where a rule truly applies to a bounded range.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement