📐 Phase 2 🟡 Intermediate ⏱️ 45 min 📱 5 sections

Responsive Design

📖 Mobile-first · Media queries · clamp() · Viewport units
🏆 Lesson 9 of 12
Lesson progress0%
Responsive design means your website looks great on every screen size — from a 320px phone to a 4K monitor. In this lesson you'll learn the mobile-first approach, how to write media queries, use fluid typography with clamp(), handle responsive images, and work with viewport units. After this, none of your layouts will ever break on mobile again.

Mobile-First Thinking

The mobile-first approach means you write your base CSS for small screens first, then use media queries to add styles for larger screens. This is the opposite of the old "desktop-first" approach — and it's far better because mobile is now the majority of web traffic.

📊
Why mobile-first?
Over 60% of web traffic comes from mobile devices. Starting with mobile forces you to prioritize content and performance. Adding complexity for larger screens is easier than trying to compress a desktop layout down to mobile.

The mental model is simple: write styles for the smallest screen first. Everything you put in your base CSS applies to phones. Then use min-width media queries to override or add styles as the screen grows.

Mobile-first vs Desktop-first
CSS
/* ✅ Mobile-first (recommended) */
.card {
  display: block;         /* single column on mobile */
  padding: 1rem;
}

@media (min-width: 768px) {
  .card { display: flex; } /* side-by-side on tablet+ */
}

/* ❌ Desktop-first (avoid) */
.card {
  display: flex;          /* starts with flex */
}
@media (max-width: 767px) {
  .card { display: block; } /* hack it back for mobile */
}
Same HTML, different layouts at different screen widths
📱 <768px (mobile)
320px wide
🖼️ Image
📝 Title
Body text stacked below
Button
💻 768px+ (tablet)
768px wide
🖼️
📝 Title
Body text beside image
Button
🖥️ 1200px+ (desktop)
1200px wide — 3 columns
🖼️
Card 1
🖼️
Card 2
🖼️
Card 3

Media Queries

A media query applies CSS only when certain conditions are true — like screen width, orientation, or display type. The syntax is @media (condition) { ... }. You'll mostly use min-width breakpoints.

Common breakpoints (mobile-first)
CSS
/* Base styles — mobile (no query needed) */
.container { padding: 1rem; }

/* Small tablet — 480px and up */
@media (min-width: 480px) {
  .container { padding: 1.5rem; }
}

/* Tablet — 768px and up */
@media (min-width: 768px) {
  .container { padding: 2rem; }
  .grid { grid-template-columns: repeat(2, 1fr); }
}

/* Desktop — 1024px and up */
@media (min-width: 1024px) {
  .grid { grid-template-columns: repeat(3, 1fr); }
}

/* Wide desktop — 1280px and up */
@media (min-width: 1280px) {
  .container { max-width: 1200px; margin: 0 auto; }
}
📱 Mobile
0–479px
1 column
📱+ Sm Tablet
480–767px
1–2 cols
📟 Tablet
768–1023px
2 columns
💻 Desktop
1024–1279px
3 columns
🖥️ Wide
1280px+
max-width
Other useful media features
CSS
/* Orientation */
@media (orientation: landscape) { ... }
@media (orientation: portrait)  { ... }

/* Combining with AND */
@media (min-width: 768px) and (max-width: 1023px) {
  /* tablet ONLY */
}

/* Hover capability (touch vs mouse) */
@media (hover: hover) {
  .btn:hover { background: blue; } /* only on pointer devices */
}

/* Dark mode preference */
@media (prefers-color-scheme: dark) {
  body { background: #000; }
}

/* Reduced motion (accessibility!) */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; }
}
💡
Real-world breakpoints
You don't need to match every device. Choose breakpoints where your content breaks — not based on specific devices. Open DevTools, resize the viewport, and add a breakpoint wherever things start looking awkward. Tailwind's breakpoints (sm:640, md:768, lg:1024, xl:1280) are a great starting reference.

Fluid Typography with clamp()

Instead of setting a font size at every breakpoint, clamp() lets you define a minimum, preferred, and maximum value in one declaration. The browser smoothly scales the value between those bounds based on viewport width.

clamp() syntax and examples
CSS
/* clamp(minimum, preferred, maximum) */
h1 {
  font-size: clamp(1.8rem, 5vw, 4rem);
  /* never smaller than 1.8rem          */
  /* grows with 5% of viewport width    */
  /* never larger than 4rem             */
}

h2 { font-size: clamp(1.4rem, 3.5vw, 2.5rem); }
p  { font-size: clamp(0.9rem, 2vw, 1.1rem);  }

/* Works for padding and gaps too! */
.section { padding: clamp(2rem, 8vw, 6rem); }
.grid    { gap:     clamp(1rem, 3vw, 2rem);  }
Live clamp() simulator
Viewport: 760pxfont-size: clamp(1.2rem, 4vw, 3rem)
min 1.2rem
max 3rem
1.9rem
The quick brown fox
🎯
The magic formula
A common formula for the preferred value: preferred = (max - min) / (max_vw - min_vw) * 100vw + (min - (max - min) / (max_vw - min_vw) * min_vw). Or just use clamp.font-size.app — plug in your min/max font sizes and viewport widths, get the formula instantly.

Responsive Images & Viewport Units

Images that overflow their container are the most common responsive bug. The fix is simple. Viewport units (vw, vh, vmin, vmax) let you size elements relative to the screen — great for hero sections and full-screen layouts.

Responsive images — the essentials
CSS + HTML
/* The golden rule — always add this */
img {
  max-width: 100%;   /* never overflow container */
  height: auto;     /* maintain aspect ratio   */
  display: block;   /* remove inline gap       */
}

/* Responsive hero image with object-fit */
.hero-img {
  width: 100%;
  height: clamp(200px, 40vw, 500px);
  object-fit: cover;    /* fill container, crop if needed */
  object-position: center top; /* keep the focus point */
}

/* srcset for serving different sizes */
HTML srcset — serve the right image size
HTML
<!-- Browser picks the right size automatically -->
<img
  src="hero-800.jpg"
  srcset="hero-400.jpg 400w,
          hero-800.jpg 800w,
          hero-1200.jpg 1200w"
  sizes="(max-width: 768px) 100vw,
         (max-width: 1024px) 50vw,
         800px"
  alt="Hero image"
  loading="lazy"
>
Viewport units
CSS
/* Viewport units */
.hero  { height: 100vh; }    /* 100% of viewport height */
.wide  { width: 80vw; }     /* 80% of viewport width  */
.icon  { font-size: 5vmin; } /* 5% of smaller dimension */

/* Modern: dvh fixes the mobile browser bar bug */
.hero  { height: 100dvh; }  /* dynamic viewport height */

/* Centering a container with max-width */
.container {
  width: min(90%, 1200px); /* 90vw but never over 1200px */
  margin: 0 auto;
}
UnitMeaningBest for
vw1% of viewport widthFluid widths, font sizes
vh1% of viewport heightFull-screen sections
dvhDynamic viewport heightMobile (accounts for address bar)
vmin1% of smaller dimensionSquare elements, icons
vmax1% of larger dimensionFull-bleed backgrounds
min()Smaller of two valuesMax-width containers
max()Larger of two valuesMinimum safe padding
🎮 Responsive Layout Playground
Drag the slider to simulate different viewport widths. Watch the layout respond in real time.
📏 Simulated viewport: 760px 📟 Tablet
🍔 Logo HomeAboutWorkContact
📦 Card 1
item
📦 Card 2
item
📦 Card 3
item
📦 Card 4
item
/* Active CSS */ grid-template-columns: repeat(2, 1fr); /* tablet */
🧠 Knowledge Check
5 questions — test your responsive design understanding.
1. What does "mobile-first" CSS mean in practice?
2. What does font-size: clamp(1rem, 3vw, 2rem) do?
3. Which CSS declaration prevents images from overflowing their container?
4. What is the difference between vh and dvh?
5. What does width: min(90%, 1200px) do?
🏆
Coding Challenge
Estimated time: 20–30 min
Build a Responsive Blog Layout

Create a page with a blog listing. On mobile it should be a single stacked column. On tablet (768px+) show 2 columns. On desktop (1024px+) show a 3-column article grid with a fixed sidebar. Use clamp() for the heading font size and min(90%, 1100px) for the container. Make sure images don't overflow with max-width: 100%. Bonus: add a responsive navigation that collapses to a hamburger on mobile.
💡 Show hints
  • Start with the mobile layout — single column, no grid
  • Add @media (min-width: 768px) for 2-column grid
  • Add @media (min-width: 1024px) for 3-col + sidebar using CSS Grid with named areas
  • Use clamp(1.5rem, 4vw, 2.5rem) for article titles
  • For the hamburger, toggle a class on the nav with JavaScript
  • Use position: sticky; top: 0; for a sticky nav
🎉
Lesson 9 Complete!
Responsive design mastered! Your layouts now work on every screen size. Next up — Transitions & Animations to bring your sites to life with motion.
← Course Home
Phase 2 · CSSLesson 9 of 12