Topic #05Foundational18 min read

Responsive Design (Media Queries)

Build one UI that fits every screen: the viewport meta tag, mobile-first breakpoints, relative units, fluid type with clamp(), responsive images, container queries, and modern preference queries.

#css#responsive#media-queries#mobile-first#container-queries#clamp#images#fundamentals

What responsive design is. One codebase, one URL, that adapts its layout to any screen — phone, tablet, laptop, ultrawide — instead of shipping a separate 'm.' mobile site. It rests on three pillars: fluid layouts (things sized in relative units and flexible layout modes), flexible media (images/video that scale), and media queries (rules that switch at size thresholds). Modern CSS adds a fourth: container queries (adapt to a parent's size, not the screen's).

Step 0 — the viewport meta tag. Responsive CSS does nothing without <meta name="viewport" content="width=device-width, initial-scale=1"> in the <head>. Without it, mobile browsers pretend to be ~980px wide and zoom out, so your media queries never match. This one line tells the browser to use the device's real width. It's the single most forgotten prerequisite.

Media queries — the syntax. A media query wraps rules that apply only when a condition holds: @media (min-width: 768px) { ... }. Conditions can test min-width/max-width (the workhorses), orientation: landscape, prefers-color-scheme: dark, prefers-reduced-motion, and more. Combine with and, list alternatives with commas (OR). Modern range syntax also allows @media (width >= 768px).

Mobile-first (min-width) vs desktop-first (max-width). Mobile-first: write base styles for the smallest screen, then add complexity with min-width queries as the viewport grows — progressive enhancement. Desktop-first: write for large screens, then override down with max-width queries. Mobile-first is preferred: the default path is the lightest (best for the weakest devices), it forces you to prioritise core content, and stacking min-width layers reads more predictably than a pile of overrides.

Breakpoints — choose by content, not devices. Don't hardcode 'iPhone' widths — devices change. Add a breakpoint wherever the layout starts to look bad. That said, common anchors are ~640px (large phone), ~768px (tablet), ~1024px (laptop), ~1280px (desktop). Keep the number of breakpoints small; every one is layout you must maintain.

Relative units are the foundation. Fixed px can't adapt. Use rem for type and spacing (relative to root font-size → respects the user's accessibility font setting), % for widths relative to the parent, vw/vh for viewport-relative sizing, and ch for text-measure widths. A layout built in relative units is already half-responsive before you write a single media query.

Fluid typography with clamp(). Instead of stepping font-size at each breakpoint, scale it continuously: font-size: clamp(1rem, 0.5rem + 2vw, 1.5rem). clamp(MIN, PREFERRED, MAX) picks the preferred value but never goes below MIN or above MAX — smooth scaling with guardrails, no media queries. The same trick works for fluid spacing and gaps.

Responsive images. A raw <img> should at least have max-width: 100%; height: auto; so it never overflows its container. For real optimisation, srcset + sizes lets the browser pick the right resolution for the device (saving mobile bandwidth), and <picture> swaps entirely different images or formats (e.g. a cropped portrait on mobile, WebP where supported). Add loading="lazy" to defer offscreen images.

Let layout do the work — fewer queries. Much responsiveness needs zero media queries if you lean on intrinsic sizing: grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)) reflows cards on its own; flex-wrap: wrap wraps a toolbar; max-width + margin-inline: auto centres a readable column. Media queries are for the structural jumps layout can't express (e.g. sidebar → top bar).

Container queries (modern). Media queries ask 'how big is the screen?' — but a component doesn't know where it'll be placed. Container queries ask 'how big is my parent?': mark a container with container-type: inline-size, then @container (min-width: 400px) { ... }. Now a card component restyles based on the column it sits in, making it truly reusable across layouts. This is the biggest shift in responsive CSS since Grid.

Preference & capability queries. Responsiveness isn't only about size. @media (prefers-color-scheme: dark) respects the OS theme; @media (prefers-reduced-motion: reduce) lets you disable animations for users who get motion-sick; @media (hover: hover) applies hover effects only on devices that actually have a pointer. Designing for these is part of modern responsive/accessible work.

Testing responsiveness. Use browser DevTools device-emulation (responsive mode), drag the viewport to find where things break (that's your next breakpoint), and always test real text lengths and images — not lorem ipsum in a perfect box. Check both orientations and the largest OS font setting.

The mental model (memorise this). Start mobile-first with fluid, relative units and flexible layout (Grid/Flex) so most of the design adapts for free. Add the viewport meta tag. Insert min-width breakpoints only where the layout genuinely breaks. Use clamp() for fluid type, srcset/picture for images, container queries for reusable components, and preference queries for theme/motion/pointer. The goal: one UI that feels native at every size.

Backend Analogy

Mobile-first is progressive enhancement, like designing an API with sensible defaults and layering optional capabilities on top rather than shipping a maximal payload and stripping it down per client. Media queries are feature flags keyed on environment (screen width) that swap configuration at runtime. Container queries are dependency injection for layout — a component adapts to the context it's dropped into instead of hardcoding assumptions about the global environment, exactly like a service resolving behaviour from its injected scope rather than global state.

Key Insights
  • Nothing responsive works without <meta name=viewport content="width=device-width, initial-scale=1"> in the head.
  • Mobile-first (min-width) progressively enhances; desktop-first (max-width) progressively degrades — mobile-first is usually simpler and lighter.
  • Choose breakpoints where the content breaks, not by device model. Keep the count small.
  • Prefer rem for type/spacing (respects user font-size), % for parent-relative, vw/vh for viewport-relative.
  • clamp(MIN, PREFERRED, MAX) gives fluid typography and spacing that scales smoothly without breakpoints.
  • img { max-width: 100%; height: auto } stops overflow; srcset/sizes and <picture> serve the right image per device.
  • Intrinsic layout (auto-fill + minmax, flex-wrap, max-width + margin auto) removes the need for many media queries.
  • Container queries adapt a component to its PARENT's size (container-type: inline-size + @container), making components reusable anywhere.
  • Preference queries matter too: prefers-color-scheme, prefers-reduced-motion, hover: hover.
  • Test by dragging the viewport to find break points; verify real text and images, both orientations, large font settings.

Worked Code

The viewport meta tag (required in <head>)
HTML
<!-- Without this, mobile browsers fake ~980px and your media queries never fire -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
Mobile-first breakpoints (min-width, progressive enhancement)
CSS
/* BASE: styles for the smallest screen — single column */
.app-shell {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}
.container { padding-inline: 16px; }

/* TABLET and up: add the sidebar */
@media (min-width: 768px) {
  .app-shell { grid-template-columns: 240px 1fr; }
}

/* DESKTOP: cap and center the reading width */
@media (min-width: 1024px) {
  .container { max-width: 1200px; margin-inline: auto; }
}
Fluid type & spacing with clamp() — no breakpoints
CSS
:root {
  /* scales smoothly between 1rem @ small and 1.5rem @ large viewports */
  --step-0: clamp(1rem, 0.85rem + 0.8vw, 1.5rem);
  --gap:    clamp(12px, 2vw, 32px);
}
h1   { font-size: clamp(1.75rem, 1rem + 4vw, 3.5rem); line-height: 1.1; }
body { font-size: var(--step-0); }
.stack { display: grid; gap: var(--gap); }
Responsive images + a container query
CSS
/* Every image scales down to its container, never overflows */
img { max-width: 100%; height: auto; display: block; }

/* Container query: the CARD adapts to its column, not the screen */
.card-wrap { container-type: inline-size; }

.card { display: grid; gap: 8px; }
@container (min-width: 400px) {
  .card { grid-template-columns: 120px 1fr; } /* side-by-side when wide */
}

/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

Try It Live

Edit the code and press Run — it executes safely in a sandboxed iframe. Use the Console tab for log output.

Resize the preview: fluid type + auto-reflowing grid

Interview-Ready Q&A

You write base CSS for the smallest screen, then add complexity with min-width media queries as the viewport grows. It's preferred because the default path is the lightest (best for low-power devices and slow networks), it forces prioritising core content, and stacking min-width layers is easier to reason about than overriding desktop styles back down with max-width.

Things to Remember
  • 1Always include the viewport meta tag — responsive CSS is inert without it.
  • 2Mobile-first: base styles + min-width breakpoints (progressive enhancement).
  • 3Set breakpoints where content breaks, not at device widths; keep them few.
  • 4Relative units: rem (type/spacing), % (parent), vw/vh (viewport), ch (measure).
  • 5clamp(MIN, PREFERRED, MAX) = fluid type/spacing without breakpoints.
  • 6Responsive images: max-width:100%; height:auto, plus srcset/sizes and <picture>.
  • 7Intrinsic layout (auto-fill + minmax, flex-wrap) removes many media queries.
  • 8Container queries adapt components to their parent's width — reusable anywhere.
  • 9Honor prefers-color-scheme, prefers-reduced-motion, and hover: hover.
  • 10Test by resizing to find break points; check real content, both orientations, large fonts.

References & Further Reading