📱 Responsive Design Principles
A modern website has to look right on a phone in someone's hand, a tablet on the couch, and a widescreen monitor at a desk — from the same code. Responsive design is the craft of building one flexible layout that adapts to any screen, like water taking the shape of its container. This lesson pulls together everything from the week and makes it bend.
Week 1 · Thursday: CSS Layout · Lecture 3
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Explain what responsive design is and why the viewport meta tag is mandatory
- Write media queries and pick sensible breakpoints
- Apply a mobile-first workflow using
min-widthqueries - Build fluid layouts with percentages, viewport units, and
clamp() - Serve appropriately-sized images with
max-width,srcset, and<picture> - Create fluid typography that scales smoothly between screen sizes
Estimated Time: 65 minutes
Practice: Make a fixed layout responsive and add fluid, mobile-first type.
In This Lesson
What Is Responsive Design?
Responsive design is an approach where a single codebase adapts its layout, images, and typography to fit whatever device is viewing it. Rather than building a separate "mobile site," you write flexible CSS that responds to the available space. Coined by Ethan Marcotte in 2010, it rests on three pillars: fluid grids, flexible images, and media queries.
Why it matters, concretely:
- Traffic is mobile-majority. More than half of all web visits come from phones — a broken mobile layout loses real users.
- SEO rewards it. Google uses mobile-first indexing, ranking the mobile experience of your page.
- One codebase. You maintain a single site instead of duplicating effort across "desktop" and "mobile" versions.
- Future-proofing. New device sizes appear constantly; a fluid layout handles them without a rewrite.
The Viewport Meta Tag
This one line is the on-switch for responsive design. Without it, mobile browsers pretend to be ~980px wide and shrink your whole page to fit — so your carefully written media queries never trigger.
<!-- Put this in every page's <head> -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
width=device-width— set the layout width to the device's actual screen width (e.g. 390px on a phone) instead of a faked 980px.initial-scale=1.0— start at a 1:1 zoom level, no shrinking.
⚠️ Don't disable zoom
You'll sometimes see maximum-scale=1.0, user-scalable=no added to prevent pinch-zoom. Avoid it: users with low vision rely on zooming, and disabling it is a serious accessibility failure. Let people zoom.
Media Queries
A media query is an if statement for CSS: "apply these rules only when the screen matches this condition." The most common condition is width.
/* Applies only when the viewport is 768px wide or narrower */
@media (max-width: 768px) {
.container {
padding: 10px;
}
}
/* Applies only when the viewport is 1024px wide or wider */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
margin: 0 auto;
}
}
/* A range — tablets between 768px and 1023px */
@media (min-width: 768px) and (max-width: 1023px) {
.container { width: 90%; }
}
Common breakpoints
There's no universal set — choose breakpoints where your content starts to look cramped, not to match specific phones. A widely used starting point:
| Range | Typical device | Query |
|---|---|---|
| < 768px | Phones | base styles (no query) |
| ≥ 768px | Tablets | @media (min-width: 768px) |
| ≥ 1024px | Laptops | @media (min-width: 1024px) |
| ≥ 1280px | Desktops | @media (min-width: 1280px) |
Beyond width
/* Devices that support true hover (mouse, not touch) */
@media (hover: hover) {
.button:hover { background-color: #2563eb; }
}
/* Respect users who ask for less motion */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
/* Adapt to the user's system dark/light preference */
@media (prefers-color-scheme: dark) {
body { background: #0f172a; color: #e2e8f0; }
}
💡 prefers-reduced-motion is an accessibility must
Some people get motion sickness or vestibular symptoms from parallax and large animations. Honoring prefers-reduced-motion is a small change that makes your site usable for them — always include it when you animate.
Mobile-First Workflow
You can write breakpoints in two directions. Mobile-first — writing base styles for small screens and layering enhancements with min-width — is the modern standard, and here's why: the mobile layout is usually the simplest, so you start from the smallest, most constrained case and progressively add complexity as space allows.
Mobile-first (recommended)
/* Base: phone — single column, small padding */
.card {
padding: 10px;
font-size: 14px;
}
/* Enhance for tablet and up */
@media (min-width: 768px) {
.card { padding: 20px; font-size: 16px; }
}
/* Enhance further for desktop */
@media (min-width: 1024px) {
.card { padding: 30px; font-size: 18px; }
}
Desktop-first (the older way)
/* Base: desktop, then walk styles DOWN with max-width */
.card { padding: 30px; font-size: 18px; }
@media (max-width: 1023px) {
.card { padding: 20px; font-size: 16px; }
}
@media (max-width: 767px) {
.card { padding: 10px; font-size: 14px; }
}
Both work, but mobile-first tends to produce simpler, less-overridden CSS and forces you to prioritize content — a healthy discipline.
Fluid Layouts & Units
Media queries handle the big jumps; fluid units handle everything between them so the layout flexes continuously rather than snapping only at breakpoints.
Percentages and max-width
/* Fluid but capped: fills 90% of small screens,
never exceeds 1200px on large ones, and stays centered */
.container {
width: 90%;
max-width: 1200px;
margin: 0 auto;
}
Viewport units
vw and vh are 1% of the viewport's width and height — perfect for full-screen sections.
.hero {
min-height: 100vh; /* at least one full screen tall */
padding: 5vw; /* padding scales with the window width */
}
clamp() — a fluid value with guardrails
The modern favorite. clamp(MIN, PREFERRED, MAX) picks the preferred value but never drops below the minimum or above the maximum — a whole responsive size in one line, no media query needed.
.container {
/* at least 1rem of side padding, ideally 5vw, at most 3rem */
padding-inline: clamp(1rem, 5vw, 3rem);
width: clamp(320px, 90%, 1200px);
}
✅ Combine the tools
Real responsive layouts blend all of these: a Grid or Flexbox container (last two lessons) sized with clamp(), reflowed at a couple of media-query breakpoints, holding flexible images. No single technique does it alone — they layer.
Responsive Images
Images are usually the heaviest thing on a page. Responsive images do two jobs: never overflow their container, and never download more pixels than the device can use.
The one rule every image needs
img {
max-width: 100%; /* never wider than its container */
height: auto; /* keep the aspect ratio */
}
That alone stops images from breaking layouts. But it still ships a huge desktop image to a phone. For that, use srcset.
srcset — let the browser pick the right file size
<img src="photo-600.jpg"
srcset="photo-300.jpg 300w,
photo-600.jpg 600w,
photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="A hiker on a mountain ridge at sunrise">
You provide several versions with their real pixel widths (300w, etc.); sizes tells the browser how wide the image will display; the browser downloads the smallest file that still looks sharp. Phones save bandwidth, retina screens get detail.
<picture> — art direction
When you need a different crop (not just size) per screen — say a tall portrait on mobile and a wide banner on desktop — use <picture>:
<picture>
<source media="(min-width: 1024px)" srcset="banner-wide.jpg">
<source media="(min-width: 768px)" srcset="banner-medium.jpg">
<img src="banner-mobile.jpg" alt="Product of the month">
</picture>
💡 Free performance: loading="lazy"
Add loading="lazy" to below-the-fold images and the browser defers loading them until the user scrolls near — faster first paint, less wasted data. Always keep meaningful alt text for accessibility and SEO.
<img src="gallery-4.jpg" loading="lazy" alt="Studio workspace">
Fluid Typography
Text should scale with the screen too — big enough to read on a phone, not cartoonishly large on a monitor. The old way was a media-query staircase:
h1 { font-size: 2rem; } /* mobile */
@media (min-width: 768px) { h1 { font-size: 2.5rem; } } /* tablet */
@media (min-width: 1024px) { h1 { font-size: 3rem; } } /* desktop */
That jumps at each breakpoint. clamp() makes it flow smoothly across every width in one line:
/* Never smaller than 1.5rem, never larger than 3rem,
scaling with the viewport in between */
h1 {
font-size: clamp(1.5rem, 4vw, 3rem);
}
p {
font-size: clamp(1rem, 2vw, 1.25rem);
line-height: 1.6;
}
clamp() follows the preferred value in the middle but flattens out at the min and max — smooth scaling with guardrails.Testing Your Work
Responsive layouts must be verified across sizes — a design that looks perfect at your monitor's width can be broken on a phone.
- Browser device mode. Chrome DevTools (Ctrl/Cmd+Shift+M), Firefox Responsive Design Mode, and Edge all let you resize the viewport and emulate specific devices.
- Drag to resize. Just narrowing the browser window slowly reveals where the layout breaks between breakpoints.
- Test both orientations — portrait and landscape behave differently.
- Check real devices when you can; touch targets and font rendering differ from emulation.
- Verify accessibility — zoom to 200%, tab through with the keyboard, and confirm images have
alttext.
Bonus: responsive tables
Wide tables are a classic mobile headache. The simplest fix is a horizontal scroll wrapper (this course's template uses div.table-wrap):
.table-wrap {
overflow-x: auto; /* the table scrolls sideways instead of breaking the page */
}
Practice & Quiz
🏋️ Exercise 1: Make it responsive, mobile-first
Goal: A .card should be a single column with 1rem padding on phones, two columns of content at 768px+, and cap its width at 1100px on large screens. Write it mobile-first.
💡 Hint
Start with the phone styles unconditionally, then add one @media (min-width: 768px) block. Cap width with max-width and center with margin: 0 auto.
✅ Solution
/* Base: phone */
.card {
padding: 1rem;
width: 90%;
max-width: 1100px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr; /* single column */
gap: 1rem;
}
/* Tablet and up: two columns */
@media (min-width: 768px) {
.card {
grid-template-columns: 1fr 1fr;
}
}
🏋️ Exercise 2: One-line fluid heading
Goal: Give an h1 a font size that is never below 1.75rem, never above 3.5rem, and scales with the viewport in between — with no media queries.
✅ Solution
h1 {
font-size: clamp(1.75rem, 5vw, 3.5rem);
line-height: 1.2;
}
🎯 Quick Quiz
Question 1: What happens if you omit the viewport meta tag?
Question 2: Which is the mobile-first way to add tablet styles?
Question 3: What does clamp(1rem, 4vw, 2rem) produce?
Best Practices & Pitfalls
✅ Do
- Always include the viewport meta tag — nothing responsive works without it
- Design mobile-first with
min-widthqueries - Choose breakpoints where your content breaks, not to chase specific phone models
- Prefer fluid tools —
clamp(),fr,%,vw— so the layout flexes between breakpoints - Ship right-sized images with
srcsetand lazy-load off-screen ones - Honor
prefers-reduced-motionand keep zoom enabled
❌ Don't
- Disable zoom with
user-scalable=no— it locks out low-vision users - Rely on media queries alone; without fluid units the layout snaps awkwardly between them
- Set fixed pixel widths on containers that should adapt
- Serve one giant image to every device
- Test only at your own screen size
⚠️ Content-out, not device-in
Don't pick breakpoints by memorizing device widths — new sizes appear every year. Resize your browser and add a breakpoint at the exact width where the design starts to feel cramped or too stretched. Let the content decide.
Summary
🎉 Key Takeaways
- Responsive design = one flexible codebase built on fluid grids, flexible images, and media queries
- The viewport meta tag is mandatory — it's the switch that makes responsiveness work at all
- Prefer a mobile-first workflow with
min-widthbreakpoints chosen by your content - Fluid units —
%,vw, and especiallyclamp()— keep layouts and type smooth between breakpoints - Serve right-sized images with
srcset/<picture>, and always keep zoom and reduced-motion support
📚 Additional Resources
🚀 What's Next?
That wraps up the HTML and CSS half of Week 1 — you can now structure, style, lay out, and make pages responsive. Next we turn on the brain: JavaScript basics — variables, data types, and operators — the first step toward pages that think and react.
🎉 Excellent work this week!
You've built the full front-end foundation: structure, style, layout, and responsiveness. Tomorrow, JavaScript brings it all to life.