Responsive design.
One layout. Every screen.
Over 60% of web traffic comes from mobile devices. If your site doesn't work on a phone, it doesn't work. Responsive design is how you make one HTML document adapt to any screen size — from a 320px phone to a 4K monitor. Media queries, mobile-first thinking, and the right units are the entire toolkit. This lesson teaches all three, with previews you can resize to see it work.
Responsive design needs three things: (1) the viewport meta tag in HTML so phones render at their actual width, (2) media queries in CSS to apply different styles at different viewport widths, (3) mobile-first CSS — write the small-screen styles first, add desktop styles with @media (min-width: 768px). Add max-width: 100% on images and width: 100%; max-width: Xpx on containers, and you have a responsive site. Three concepts, one pattern.
Before this lesson: complete Lesson 5. You need flexbox, grid, and the box model — responsive design combines all of them.
📱 The viewport meta tag
One line in the HTML head that makes everything else possible.
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Without the viewport meta tag, mobile browsers assume a 980px viewport and zoom out. With it, they render at the actual device width.
Without viewport meta
With viewport meta
Required for every page
Without this tag, no responsive design works. Phone browsers ignore your media queries because they assume a 980px desktop viewport. Add it once per page, in the head.
width=device-width
Tells the browser "use the device's actual screen width." On an iPhone, that's 390px. On an Android, maybe 412px. This makes <code>min-width</code> media queries trigger correctly.
initial-scale=1.0
Start at 100% zoom, not zoomed out. Combined with <code>width=device-width</code>, the page renders 1:1 with the device's pixels.
Never disable zoom
Setting <code>user-scalable=no</code> or <code>maximum-scale=1.0</code> fails WCAG accessibility. Users with low vision need to zoom. Leave zoom enabled.
Test without the viewport tag once. Open any page you've built on your phone, but temporarily remove the viewport meta tag. You'll see exactly why it's required — text becomes microscopic, buttons can't be tapped, the whole page is a shrunk-down desktop. Add it back and the same page becomes usable. This single tag is the difference between "looks like a website" and "looks like a website on a phone."
🎛️ Media queries — the core mechanism
Wrap CSS rules in a condition. Apply them only when the viewport matches.
/* min-width — at least this wide (mobile-first) */
@media (min-width: 768px) {
.box {
background: #1572B6;
}
}
/* Applies when viewport is 768px or wider */
/* max-width — at most this wide (desktop-first) */
@media (max-width: 767px) {
.box {
background: #ef4444;
}
}
/* Applies when viewport is 767px or narrower */
/* Range with and — both conditions must be true */
@media (min-width: 768px) and (max-width: 1023px) {
.box {
background: #10b981;
}
}
/* Applies only between 768-1023px (tablet range) */
/* Modern range syntax (widely supported) */
@media (768px <= width <= 1023px) {
.box {
background: #8b5cf6;
}
}
/* Multiple media queries — stack responsive changes */
.box {
padding: 16px;
font-size: 14px;
}
@media (min-width: 640px) {
.box {
padding: 24px;
font-size: 16px;
}
}
@media (min-width: 1024px) {
.box {
padding: 32px;
font-size: 18px;
}
}
Resize your browser window to see these breakpoints in action. The box changes color, padding, and font size at different widths.
Responsive box
Current viewport: -
@media (min-width: X)
Applies styles when viewport is <em>at least</em> X wide. Used in mobile-first CSS — you start small and layer on styles for wider screens.
@media (max-width: X)
Applies when viewport is <em>at most</em> X wide. Used in desktop-first CSS — you start large and layer on styles for narrower screens.
Combining with and
<code>@media (min-width: 768px) and (max-width: 1023px)</code> applies only in that range. Or modern syntax: <code>@media (768px <= width <= 1023px)</code>.
Other media features
<code>prefers-color-scheme</code> (dark mode), <code>prefers-reduced-motion</code> (accessibility), <code>orientation</code> (portrait/landscape), <code>hover</code> (touch vs mouse). Same syntax.
The most common media query in the world: <code>@media (min-width: 768px) { ... }</code>. It's the tablet breakpoint used by Tailwind, Bootstrap, and thousands of sites. When you don't know what breakpoint to use, start with 768px — it covers phone-vs-tablet. Add 1024px for tablet-vs-desktop. Two breakpoints handle 90% of responsive needs.
📱 Mobile-first — the modern approach
Write the small-screen styles first. Add desktop styles with min-width.
/* MOBILE-FIRST — base styles are for small screens */
/* Base: single column, smaller text (mobile default) */
.card {
display: block;
padding: 16px;
font-size: 15px;
}
.grid {
display: grid;
grid-template-columns: 1fr; /* 1 column on mobile */
gap: 16px;
}
/* Tablet and up: 2 columns, more padding */
@media (min-width: 768px) {
.card {
padding: 24px;
font-size: 16px;
}
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop and up: 3 columns, larger padding */
@media (min-width: 1024px) {
.card {
padding: 32px;
font-size: 17px;
}
.grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* Large desktop: 4 columns */
@media (min-width: 1280px) {
.grid {
grid-template-columns: repeat(4, 1fr);
}
}
/* ================================================================
DESKTOP-FIRST (the older approach — avoid unless you have to)
================================================================ */
/* Base: 4 columns (desktop default) */
.old-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
/* Tablet: drop to 2 columns */
@media (max-width: 1023px) {
.old-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Mobile: drop to 1 column */
@media (max-width: 767px) {
.old-grid {
grid-template-columns: 1fr;
}
}
Mobile-first responsive grid (resize to see it change)
The CSS starts with 1 column (mobile). Two <code style="color:#fbbf24;">min-width</code> queries add 2 and 4 columns for wider screens. Base styles handle the smallest screen.
Why mobile-first?
Mobile usage exceeds desktop (~60%). Mobile CSS is simpler — fewer columns, less decoration. Starting simple and adding complexity is easier than starting complex and stripping it down.
Base styles = smallest
Write CSS as if you're only targeting a phone. Single column, base font sizes, minimal layout. Then add <code>@media (min-width: ...)</code> blocks for wider screens.
min-width, never max-width
In mobile-first CSS, use <code>min-width</code> exclusively. <code>max-width</code> breaks the cascade — small-screen styles need to win by default, then get overridden upward.
Performance
Mobile devices are often on slower networks and weaker CPUs. Mobile-first CSS loads the simplest styles first — faster initial render, less work for slow devices.
Never mix min-width and max-width. If your file has both, the CSS becomes a puzzle to debug — you have to trace which rules apply at every width. Pick one direction. Modern sites use <code>min-width</code> exclusively. Legacy sites use <code>max-width</code>. Never both. If you're editing an old codebase that uses <code>max-width</code>, keep it consistent — don't add <code>min-width</code> queries mixed in.
📏 Breakpoints — where layouts change
There are no universal values. But there are common starting points.
| Breakpoint | Target | What changes |
|---|---|---|
| <code>0 - 639px</code> | Mobile | Single column, large tappable buttons, base font sizes |
| <code>640px</code> (sm) | Large phone / small tablet | Sometimes 2 columns, slightly larger text |
| <code>768px</code> (md) | Tablet | 2 columns, more padding, horizontal nav |
| <code>1024px</code> (lg) | Small laptop | 3 columns, sidebar layouts, larger headings |
| <code>1280px</code> (xl) | Desktop | 4 columns, wider containers, more whitespace |
| <code>1536px</code> (2xl) | Large desktop | Max-width containers so content doesn't stretch too wide |
Start with 2 breakpoints
768px (tablet) and 1024px (desktop) cover 90% of sites. Add 640px and 1280px if needed. Fewer breakpoints = simpler CSS, easier debugging.
Breakpoints where content breaks
Real responsive design breaks where the <em>content</em> breaks, not at device widths. Shrink your browser slowly — when the layout looks wrong, that's your breakpoint.
The device myth
Devices don't have fixed widths. There are hundreds of screen sizes. Don't optimize for "iPhone 14" — optimize for content fitting its container. Breakpoints are a range, not a device.
Tailwind's breakpoints
640, 768, 1024, 1280, 1536. These are the most-tested breakpoints in the world. You don't have to use Tailwind — just copy its breakpoint values and you're inheriting billions of dollars of design testing.
Don't design for specific devices. "Make it look good on iPhone 14 Pro Max" leads to wasted effort when the next iPhone is 3px wider. Design for ranges: <em>mobile range</em> (under 640px), <em>tablet range</em> (640–1024px), <em>desktop range</em> (over 1024px). Test at a few representative widths in each range. This is the approach every professional design system uses — Bootstrap, Tailwind, Material, Ant. None of them target specific devices.
📐 Responsive units
The units that make layouts fluid. The ones that keep them fixed.
/* rem — relative to root font size (16px by default) */
html { font-size: 16px; }
h1 { font-size: 2rem; } /* 32px */
h2 { font-size: 1.5rem; } /* 24px */
body { font-size: 1rem; } /* 16px */
.small { font-size: 0.875rem; } /* 14px */
/* em — relative to the PARENT font size (compounds — usually avoid) */
.card { font-size: 18px; }
.card p { font-size: 1em; } /* 18px (same as parent) */
/* % — relative to the parent */
.container { width: 100%; } /* full width of parent */
.half { width: 50%; } /* half of parent */
/* vw / vh — relative to viewport */
.hero {
width: 100vw; /* full viewport width */
height: 100vh; /* full viewport height */
}
.hero-half {
height: 50vh; /* half viewport height */
}
/* The responsive container pattern */
.container {
width: 100%;
max-width: 1200px; /* cap on very wide screens */
margin: 0 auto; /* center */
padding: 0 16px; /* mobile gutters */
}
@media (min-width: 768px) {
.container { padding: 0 24px; } /* more gutter on larger screens */
}
/* Responsive images */
img {
max-width: 100%; /* never wider than container */
height: auto; /* maintain aspect ratio */
display: block; /* remove inline-image gap */
}
/* ================================================================
UNITS CHEAT SHEET
================================================================ */
/* rem — font sizes, spacing (scales with user accessibility) */
/* % — widths, fluid layouts */
/* vh/vw — full-viewport elements, hero sections */
/* px — borders, small fixed spacing, minimum sizes */
/* em — rarely (compounds — use rem instead) */
/* NEVER use fixed px width on containers */
/* NEVER use vh for content that might overflow — use min-height: 100vh */
Fluid container (width: 100%, max-width: 100%)
Constrained container (max-width: 400px)
rem for text
<code>1rem = 16px</code> (browser default). Users can change their browser font size — rem respects it, px doesn't. Use rem for all font sizes and spacing.
% for widths
<code>width: 100%</code> fills the parent. Combined with <code>max-width</code>, it creates fluid containers. Use % for widths, not fixed px.
max-width: 100% on images
The single most important image rule. Without it, large images overflow their container and cause horizontal scroll. With it, images shrink to fit. Pair with <code>height: auto</code>.
min-height, not height
<code>height: 100vh</code> clips content that exceeds the viewport. Fix: <code>min-height: 100vh</code> — at least full height, grows if needed. This is the correct way to make a full-height section.
Don't use <code>100vh</code> on mobile. Mobile browsers have a dynamic address bar that shows and hides as you scroll — this means "100vh" is often taller than the visible viewport, causing content to be cut off. Fix: use <code>min-height: 100vh</code> plus a mobile-specific fallback. Modern CSS has <code>100dvh</code> (dynamic viewport height) which solves this properly. Or just use <code>min-height: 100vh</code> and add enough padding to prevent clipping.
🎯 Real-world patterns
Five responsive patterns that cover most of what you'll build.
/* 1. RESPONSIVE CONTAINER — the most-used pattern */
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 16px;
}
@media (min-width: 768px) {
.container { padding: 0 24px; }
}
/* 2. RESPONSIVE NAV — stack on mobile, row on desktop */
.nav {
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px;
}
@media (min-width: 768px) {
.nav {
flex-direction: row;
justify-content: space-between;
align-items: center;
}
}
/* 3. RESPONSIVE GRID — 1 → 2 → 3 → 4 columns */
.grid {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
@media (min-width: 640px) {
.grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
.grid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1280px) {
.grid { grid-template-columns: repeat(4, 1fr); }
}
/* 4. RESPONSIVE TYPOGRAPHY — bigger headings on wider screens */
h1 {
font-size: 1.75rem; /* mobile */
line-height: 1.2;
}
@media (min-width: 768px) {
h1 { font-size: 2.25rem; }
}
@media (min-width: 1024px) {
h1 { font-size: 2.75rem; }
}
/* Or better — fluid typography with clamp() */
h1 {
font-size: clamp(1.75rem, 4vw + 1rem, 2.75rem);
}
/* min 1.75rem, max 2.75rem, scales with viewport between those */
/* 5. RESPONSIVE IMAGES */
img {
max-width: 100%;
height: auto;
display: block;
}
/* Hero image — cover full width, aspect ratio preserved */
.hero-image {
width: 100%;
height: 300px;
object-fit: cover;
object-position: center;
}
@media (min-width: 768px) {
.hero-image { height: 500px; }
}
Pattern 3 — responsive grid (resize to see)
The grid reflows at 500px (2 cols) and 800px (4 cols). On mobile: 1 column.
Fluid typography with clamp() is the modern alternative to media queries for text. <code>font-size: clamp(1.75rem, 4vw + 1rem, 2.75rem);</code> means "minimum 1.75rem, maximum 2.75rem, scales in between based on viewport width." This replaces 3 media queries with one line. Every modern site uses clamp() for headings now — it's smoother and less code. The three parameters are (min, preferred, max) — the middle is what makes it fluid.
🏆 Challenge
Three tasks. Each exercises a different responsive skill.
Task 1 — Responsive card
Build a single card that changes at 3 breakpoints:
1. Mobile (<640px): single column, image on top, 16px padding, 15px font
2. Tablet (640–1023px): image and content side by side (flex), 24px padding, 16px font
3. Desktop (1024px+): image takes 1/3 width, content 2/3, 32px padding, 17px font
Use only two media queries (640px and 1024px). Base styles handle mobile. Test by resizing the browser at every step.
Task 2 — Responsive nav bar
Build a navigation bar that:
1. Mobile: hamburger menu (or stacked links) — items stack vertically
2. Desktop (768px+): horizontal — logo left, links center or right
Use flexbox with <code>flex-direction: column</code> in mobile and <code>flex-direction: row</code> at 768px. Add <code>align-items: center</code> and <code>justify-content: space-between</code> at desktop. This is the exact pattern every real nav bar uses.
Task 3 — Full responsive page
Build a small responsive page with:
1. Hero section (full-width, centered text, <code>min-height: 60vh</code>)
2. Feature grid (1 col on mobile, 2 cols at 640px, 3 cols at 1024px)
3. Testimonials (1 col on mobile, 2 cols at 768px)
4. Footer (stacked on mobile, 3 columns at 768px)
5. Responsive typography (headings scale using <code>clamp()</code>)
6. Responsive images (<code>max-width: 100%</code>)
7. All at 3 breakpoints: 640px, 768px, 1024px
Test at 4 widths: 375px (iPhone SE), 768px (iPad), 1024px (iPad landscape), 1440px (desktop). The layout should look intentional at every width.
⚠️ Common mistakes
The responsive bugs that catch every beginner.
Without <code><meta name="viewport" content="width=device-width, initial-scale=1.0"></code>, no responsive CSS works. Phones render at 980px and zoom out. Add it to every page.
Mixing both makes CSS unmaintainable. Pick one: <code>min-width</code> for mobile-first (recommended), or <code>max-width</code> for desktop-first. Never both in the same file.
<code>width: 1200px</code> on a container causes horizontal scroll on every screen under 1200px. Fix: <code>width: 100%; max-width: 1200px; margin: 0 auto;</code>.
A large image without <code>max-width: 100%</code> overflows its container and forces horizontal scroll. Fix: <code>img { max-width: 100%; height: auto; }</code> — one rule, applies to every image.
<code>font-size: 16px</code> doesn't scale when users zoom or change browser font size. Fix: use <code>rem</code>. <code>1rem = 16px</code> by default but scales with user preference.
Mobile address bars show/hide as you scroll — <code>100vh</code> is often taller than the visible viewport. Content gets cut off. Fix: use <code>min-height: 100vh</code> or <code>100dvh</code> (modern).
"Make it work on iPhone 14" leaves you with a broken site on iPhone 15. Fix: design for ranges — mobile (under 640px), tablet (640–1024px), desktop (over 1024px). Test at 3-4 representative widths.
Clickable elements need at least 44×44px on touch screens. Small links and buttons are unusable on mobile. Fix: increase padding on mobile, or use <code>min-height: 44px</code> on buttons.
➡️ What's next
You can lay out anything. Next: making it feel polished with animation.
❓ Frequently asked questions
What is responsive web design?
Responsive design means a single website adapts its layout to any screen size. Instead of building separate sites for mobile and desktop, one HTML document is styled differently at different viewport widths using media queries. It's now a baseline expectation, not a feature.
What is a media query in CSS?
A media query wraps CSS rules in a condition based on the viewport. @media (min-width: 768px) applies rules only when the viewport is 768px or wider. The most common conditions are min-width (at least this wide) and max-width (at most this wide).
What is mobile-first design?
Mobile-first means writing CSS for the smallest screens first, then adding complexity for larger screens using min-width media queries. This is the modern standard because mobile usage exceeds desktop, it forces you to prioritize content, mobile CSS is simpler, and it aligns with how browsers load resources.
What are the best responsive breakpoints?
There are no universal breakpoints — breakpoints should be where your content breaks. Common values are 640px, 768px, 1024px, 1280px, 1536px. Start with 768px and 1024px — two breakpoints cover 90% of sites.
What is the viewport meta tag?
The viewport meta tag tells mobile browsers how to scale the page: meta name='viewport' content='width=device-width, initial-scale=1.0'. Without it, phones render at desktop width and zoom out. Every responsive site needs this tag.
What is the difference between min-width and max-width media queries?
min-width applies when viewport is at least that wide — mobile-first. max-width applies when viewport is at most that wide — desktop-first. Modern practice uses min-width almost exclusively. Pick one direction and be consistent.
What units should I use for responsive design?
Use rem for font sizes and spacing. Use % for widths inside containers. Use vh and vw sparingly. Use px for borders and small fixed dimensions. Avoid fixed px widths on containers — use max-width with width: 100% instead.
How do I make images responsive?
Two rules: img { max-width: 100%; height: auto; } makes every image scale down to fit its container without distortion. For more control, use srcset and sizes to serve different image files at different viewport widths.