📱 Lesson 15: Responsive Design
Your site looks great on your laptop. But what about on a phone? A tablet? A 4K monitor? Responsive design means building pages that adapt to any screen size — rearranging, resizing, and reflowing so the content is always readable and usable. It's not optional; over half of all web traffic comes from mobile devices.
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Explain why responsive design matters and what "mobile-first" means
- Use the viewport meta tag correctly
- Write media queries to apply styles at different screen widths
- Choose sensible breakpoints for your layouts
- Use fluid units (
%,vw,vh,rem,em) instead of fixed pixels - Make images responsive with
max-width: 100% - Build a mobile-first navigation that adapts to larger screens
- Use
clamp()for fluid typography - Test responsive layouts using browser DevTools
Estimated Time: 60 minutes
Hands-on: You'll make your project site fully responsive — from phone to desktop.
📑 In This Lesson
Why Responsive Design Matters
People visit websites on phones, tablets, laptops, desktops, TVs, and even smartwatches. A fixed-width layout that looks perfect at 1200px will force horizontal scrolling on a phone and waste acres of space on a 4K monitor.
Responsive design solves this by making your layout adapt to the available space. The same HTML renders differently depending on the screen width — content reflows, sidebars stack, font sizes adjust, and images scale.
~375px"] --> B["Tablet
~768px"] --> C["Laptop
~1024px"] --> D["Desktop
~1440px+"] style A fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#1e293b style B fill:#fffbeb,stroke:#f59e0b,stroke-width:2px,color:#1e293b style C fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#1e293b style D fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b
Three core principles make a site responsive:
- Fluid layouts — use percentages,
fr, andauto-fitinstead of fixed pixel widths - Flexible media — images and videos scale to fit their containers
- Media queries — apply different CSS rules at different screen sizes
You've actually been using the first two already. The auto-fit + minmax() grid from the last lesson is inherently responsive. Now you'll learn how to handle cases that need explicit breakpoints.
The Viewport Meta Tag
This line in your <head> is essential for responsive design:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Without it, mobile browsers pretend the screen is ~980px wide and zoom out to fit, making everything tiny and unreadable. This meta tag tells the browser: "use the actual device width, don't zoom out."
If you've been using Emmet's ! shortcut, this tag is already in your HTML. If not, add it now — nothing else in this lesson works without it.
💡 What Each Part Means
width=device-width— set the viewport width to the actual device screen widthinitial-scale=1.0— start at 100% zoom (no zooming out)
Never add maximum-scale=1 or user-scalable=no — these prevent users from zooming in, which is an accessibility problem.
Media Queries
A media query is a CSS block that only applies when certain conditions are true — most commonly, when the screen is at least a certain width.
/* Base styles — apply to all screens */
.container {
padding: 16px;
}
/* Only applies when the screen is 768px or wider */
@media (min-width: 768px) {
.container {
padding: 24px 32px;
}
}
The styles inside @media (min-width: 768px) only kick in when the browser window is 768 pixels wide or more. On smaller screens, only the base styles apply.
Syntax
@media (condition) {
/* CSS rules that only apply when the condition is true */
}
/* Width-based */
@media (min-width: 768px) { } /* 768px and wider */
@media (max-width: 767px) { } /* 767px and narrower */
/* Combining conditions */
@media (min-width: 768px) and (max-width: 1023px) { } /* tablet only */
/* Orientation */
@media (orientation: portrait) { }
@media (orientation: landscape) { }
/* Prefer reduced motion (accessibility) */
@media (prefers-reduced-motion: reduce) {
* { animation: none !important; transition: none !important; }
}
/* Dark mode preference */
@media (prefers-color-scheme: dark) {
:root { --color-bg: #0f172a; --color-text: #e2e8f0; }
}
Where to Place Media Queries
Put media queries at the bottom of your stylesheet (or at least after the base styles they modify). The cascade means later rules override earlier ones at the same specificity — so your media query overrides need to come after the defaults.
/* ===========================
Base styles (mobile)
=========================== */
.grid { display: grid; grid-template-columns: 1fr; gap: 16px; }
/* ===========================
Tablet and up
=========================== */
@media (min-width: 768px) {
.grid { grid-template-columns: 1fr 1fr; gap: 20px; }
}
/* ===========================
Desktop and up
=========================== */
@media (min-width: 1024px) {
.grid { grid-template-columns: 1fr 1fr 1fr; gap: 24px; }
}
Choosing Breakpoints
Breakpoints are the screen widths where your layout changes. Don't chase specific devices — devices change every year. Instead, choose breakpoints where your design breaks.
That said, here are commonly used breakpoints that work well as starting points:
| Name | min-width | Typical Devices |
|---|---|---|
| Small (sm) | 640px |
Large phones (landscape) |
| Medium (md) | 768px |
Tablets (portrait) |
| Large (lg) | 1024px |
Tablets (landscape), small laptops |
| Extra large (xl) | 1280px |
Laptops, desktops |
| 2XL | 1536px |
Large desktops, external monitors |
For most projects, two or three breakpoints are enough: one around 768px (tablet) and one around 1024px (desktop). Don't add breakpoints you don't need — every one adds complexity.
💡 The Right Way to Find Breakpoints
Open your site and slowly drag the browser window narrower. When something looks awkward — text too cramped, a sidebar too narrow to be useful, cards squished to unreadable widths — that's where you need a breakpoint. Let the content dictate the breakpoints, not the devices.
Mobile-First Design
"Mobile-first" means writing your base CSS for the smallest screen, then using min-width media queries to add complexity for larger screens. The alternative — writing desktop styles first and overriding with max-width queries — is called "desktop-first" and tends to produce more overrides and messier code.
(single column, stacked, simple)"] B --> C["@media min-width: 768px
(two columns, sidebar appears)"] C --> D["@media min-width: 1024px
(three columns, larger fonts)"] style A fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#1e293b style B fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style C fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style D fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b
Why Mobile-First?
- Less code — mobile layouts are simpler (single column, stacked elements), so fewer overrides are needed
- Progressive enhancement — you start with the essentials and add features for bigger screens, rather than stripping things away
- Performance — mobile devices download only the base CSS; desktop enhancements load but don't apply until the screen is wide enough
- Forces prioritization — when you design for a small screen first, you decide what actually matters
Mobile-First in Practice
/* Mobile (base): single column */
.page-grid {
display: grid;
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
gap: 0;
}
/* Tablet: sidebar appears beside main content */
@media (min-width: 768px) {
.page-grid {
grid-template-columns: 220px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
}
/* Desktop: wider sidebar, max-width container */
@media (min-width: 1024px) {
.page-grid {
grid-template-columns: 280px 1fr;
max-width: 1200px;
margin: 0 auto;
}
}
Notice the pattern: the base styles are the simplest layout. Each min-width query adds more structure. You never need to "undo" anything.
Fluid Units — %, vw, vh, rem, em
Fixed pixel values create rigid layouts. Fluid units create layouts that scale naturally with the screen or the user's font settings.
| Unit | Relative To | Best For | Example |
|---|---|---|---|
px |
Nothing (absolute) | Borders, shadows, small fixed details | border: 1px solid |
% |
Parent element's size | Widths, max-widths | width: 80% |
vw |
Viewport width (1vw = 1%) | Full-width sections, hero images | width: 100vw |
vh |
Viewport height (1vh = 1%) | Full-height sections, hero banners | min-height: 100vh |
rem |
Root font size (usually 16px) | Font sizes, spacing, padding, margins | font-size: 1.25rem |
em |
Parent element's font size | Component-relative spacing | padding: 0.5em 1em |
rem — The Go-To Unit
rem stands for "root em" and is relative to the root element's (<html>) font size, which defaults to 16px in every browser. This makes it predictable and consistent:
/* All of these are based on the root 16px */
h1 { font-size: 2rem; } /* 32px */
h2 { font-size: 1.5rem; } /* 24px */
p { font-size: 1rem; } /* 16px */
.small { font-size: 0.875rem; } /* 14px */
/* Spacing with rem scales if the user changes their browser font size */
.card { padding: 1.25rem 1.5rem; margin-bottom: 1.5rem; }
The big advantage: if a user changes their browser's default font size (for accessibility), everything sized in rem scales proportionally. Pixel-based sizes don't.
When to Use Which
rem— your default for font sizes, padding, margins, gaps. Consistent and accessible.%— widths and max-widths (e.g.,max-width: 90%)vw/vh— full-screen sections (e.g.,min-height: 100vhfor a hero)px— borders, box-shadows, and tiny details that shouldn't scaleem— spacing that should scale with the element's own font size (less common thanrem)
Responsive Images
Images with fixed pixel widths break responsive layouts — they overflow their containers on small screens. The fix is simple:
img {
max-width: 100%;
height: auto;
}
max-width: 100% means "never be wider than your container." height: auto preserves the aspect ratio. These two rules should be in every project's CSS reset.
Object-Fit for Cropped Images
When images need to fill a specific space (like a gallery thumbnail or a card header), use object-fit:
.card-image {
width: 100%;
height: 200px;
object-fit: cover; /* fills the area, crops if needed */
}
| Value | Behavior |
|---|---|
cover |
Fills the entire area, cropping if needed (most common) |
contain |
Fits inside the area, preserving aspect ratio (may leave empty space) |
fill |
Stretches to fill (distorts the image — usually avoid) |
none |
Original size, no scaling (may overflow) |
The <picture> Element for Art Direction
Sometimes you want to serve entirely different images at different screen sizes — a wide landscape shot on desktop but a cropped portrait version on mobile. The <picture> element handles this:
<picture>
<source media="(min-width: 768px)" srcset="hero-wide.jpg">
<source media="(min-width: 480px)" srcset="hero-medium.jpg">
<img src="hero-small.jpg" alt="Sunset over the ocean">
</picture>
The browser picks the first <source> that matches. The <img> is the fallback for browsers that don't support <picture> and for the smallest screens.
Fluid Typography with clamp()
Media queries for font sizes work but create jarring jumps. clamp() creates smoothly scaling typography that flows between a minimum and maximum size:
/* clamp(minimum, preferred, maximum) */
h1 {
font-size: clamp(1.75rem, 4vw, 3rem);
}
p {
font-size: clamp(1rem, 1.5vw, 1.125rem);
}
Here's how clamp(1.75rem, 4vw, 3rem) works:
- The font is at least 1.75rem (28px) — it never goes smaller
- The font prefers to be 4vw (4% of the viewport width) — it scales with the screen
- The font is at most 3rem (48px) — it never goes larger
On a 375px phone, 4vw = 15px, which is below the minimum, so the font stays at 1.75rem. On a 1200px desktop, 4vw = 48px, which matches the maximum, so it caps at 3rem. In between, it scales smoothly.
clamp hits minimum
1.75rem (28px)"] --> B["Tablet: 768px
scales with 4vw
~30.7px"] --> C["Desktop: 1200px
clamp hits maximum
3rem (48px)"] style A fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#1e293b style B fill:#fffbeb,stroke:#f59e0b,stroke-width:2px,color:#1e293b style C fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#1e293b
💡 clamp() Works for More Than Fonts
You can use clamp() for padding, margins, gaps — anything with a size value:
.container {
padding: clamp(16px, 4vw, 48px);
gap: clamp(16px, 2vw, 32px);
}
Testing with DevTools
Every modern browser has a responsive design testing mode. In Chrome and Edge:
- Press F12 to open DevTools
- Click the device toggle button (the phone/tablet icon) in the top-left of the DevTools toolbar — or press Ctrl+Shift+M
- The viewport now has drag handles. Resize it to any width, or pick a preset device (iPhone, iPad, Pixel, etc.)
- Watch your media queries activate and deactivate as you resize
What to Test For
- Text readability — can you read body text without zooming? Are headings too large on mobile?
- Horizontal overflow — slowly narrow the viewport. Does anything cause a horizontal scrollbar?
- Touch targets — are buttons and links at least 44×44 pixels for finger tapping?
- Navigation — does the nav collapse to a hamburger menu on mobile? Does it work?
- Images — do they scale down without overflowing?
- Tables — wide tables often break on mobile. Consider wrapping them in a scrollable
<div>
/* Make tables horizontally scrollable on small screens */
.table-wrap {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
💡 The Scrollbar Test
The easiest way to spot responsive problems: set the viewport to 375px wide (iPhone SE size) and check for a horizontal scrollbar. If one appears, something is too wide. Common culprits: images without max-width: 100%, fixed-width elements, <pre> code blocks, and wide tables.
Hands-on Exercise
🏋️ Exercise: Make Your Site Responsive
Objective: Apply mobile-first responsive design to your project site so it works on phones, tablets, and desktops.
Instructions:
- Add the global responsive image reset to your CSS:
img { max-width: 100%; height: auto; } - Convert your page layout to mobile-first:
- Base styles: single-column grid (
grid-template-columns: 1fr) - At
768px: add the sidebar column
- Base styles: single-column grid (
- Make your navigation responsive:
- Mobile: hamburger button visible, nav links hidden (shown on toggle)
- Desktop (768px+): hamburger hidden, nav links displayed as a flex row
- Add fluid typography using
clamp():h1:clamp(1.75rem, 4vw, 2.5rem)h2:clamp(1.375rem, 3vw, 1.75rem)
- Wrap any
<table>in a<div class="table-wrap">withoverflow-x: auto - Use DevTools responsive mode to test at 375px, 768px, and 1200px
- Check for horizontal scrollbars at every width
💡 Hint
Start with the base mobile styles — everything in a single column. Then add a @media (min-width: 768px) block at the bottom of your stylesheet that introduces the sidebar, switches the nav to horizontal, and increases padding. You only need one or two media queries for a site this size.
✅ Example Solution
/* ===========================
Responsive Reset
=========================== */
img {
max-width: 100%;
height: auto;
}
/* ===========================
Fluid Typography
=========================== */
h1 { font-size: clamp(1.75rem, 4vw, 2.5rem); }
h2 { font-size: clamp(1.375rem, 3vw, 1.75rem); }
h3 { font-size: clamp(1.125rem, 2.5vw, 1.375rem); }
/* ===========================
Mobile Base Layout
=========================== */
.page-grid {
display: grid;
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
min-height: 100vh;
}
/* Navigation — mobile */
.site-nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
flex-wrap: wrap;
}
.nav-links {
display: none;
width: 100%;
flex-direction: column;
gap: 0;
}
.nav-links.active {
display: flex;
}
.nav-links a {
padding: 12px 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.mobile-menu-toggle {
display: block;
}
/* Container — mobile */
.container {
padding: clamp(16px, 4vw, 48px);
}
/* Cards — mobile */
.card-row {
display: flex;
flex-direction: column;
gap: 16px;
}
/* Tables — scrollable on mobile */
.table-wrap {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
/* ===========================
Tablet and Up (768px)
=========================== */
@media (min-width: 768px) {
.page-grid {
grid-template-columns: 220px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
/* Navigation — desktop */
.nav-links {
display: flex;
width: auto;
flex-direction: row;
gap: 24px;
}
.nav-links a {
padding: 0;
border-top: none;
}
.mobile-menu-toggle {
display: none;
}
/* Cards — side by side */
.card-row {
flex-direction: row;
flex-wrap: wrap;
gap: 20px;
}
.card-row .card {
flex: 1 1 250px;
}
}
/* ===========================
Desktop (1024px)
=========================== */
@media (min-width: 1024px) {
.page-grid {
grid-template-columns: 280px 1fr;
max-width: 1200px;
margin: 0 auto;
}
}
🎯 Quick Quiz
Question 1: What does "mobile-first" mean in responsive design?
Question 2: What does @media (min-width: 768px) { ... } mean?
Question 3: How do you make images responsive?
Question 4: What does clamp(1rem, 3vw, 2rem) do?
Question 5: What unit is best for font sizes in a responsive design?
Summary
🎉 Key Takeaways
- Responsive design makes your site adapt to any screen size — it's essential, not optional
- The viewport meta tag (
width=device-width, initial-scale=1.0) is required for mobile to work correctly - Media queries apply CSS rules conditionally based on screen width:
@media (min-width: 768px) { } - Mobile-first = base styles for small screens +
min-widthqueries for larger screens. Cleaner code, fewer overrides. - Choose breakpoints where your design breaks, not at specific device widths. Two or three is usually enough.
- Use fluid units:
remfor fonts/spacing,%for widths,vw/vhfor full-screen sections,pxfor borders - Make images responsive:
img { max-width: 100%; height: auto; } clamp(min, preferred, max)creates fluid typography that scales smoothly — no jumps- Use
object-fit: coverfor images that need to fill a specific area - Test with DevTools responsive mode (Ctrl+Shift+M) — check for horizontal scrollbars at every width
📁 Your Project So Far
my-website/
├── css/
│ └── style.css ← now fully responsive with media queries
├── images/
│ └── sunset.jpg
├── index.html ← mobile-first layout
├── about.html
├── recipe.html
└── contact.html
🎓 Module 3 Complete!
You've finished all of CSS Essentials. You now know how to style colors and fonts, control spacing with the box model, build one-dimensional layouts with Flexbox, create two-dimensional layouts with Grid, and make everything responsive. That's a complete CSS toolkit.
🚀 What's Next?
Time to put it all together. In Module 4: Building Your Website, you'll plan and build a multi-page website from scratch — homepage, content pages, and all the polish — using everything you've learned. First up: Lesson 16: Planning Your Site, where you'll sketch a sitemap, wireframe your pages, and prepare your content before writing a single line of code.