Lab: Make It Responsive
Take a desktop-only layout and make it fully responsive using mobile-first CSS, media queries, fluid typography, and responsive images.
You have the tools. This lab puts them together. You will start from a desktop-only layout with fixed widths, large static font sizes, and a single oversized image, then work through each responsive technique in sequence.
There is no auto-grader for CSS — the browser is your test harness. Open DevTools, drag the viewport, and trust your eyes.
Starting point
Create a file called index.html and paste in this starting layout:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Mountain Trails</title>
<style>
/* ── Reset ─────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; color: #1a1a2e; }
/* ── Layout ─────────────────────────────────────────────── */
.page {
width: 1200px;
margin: 0 auto;
padding: 40px;
}
.hero img {
width: 1200px;
height: 400px;
object-fit: cover;
}
.content {
display: grid;
grid-template-columns: 260px 1fr 280px;
gap: 40px;
margin-top: 40px;
}
/* ── Typography ─────────────────────────────────────────── */
h1 { font-size: 56px; margin-bottom: 16px; }
h2 { font-size: 28px; margin-bottom: 12px; }
p { font-size: 16px; line-height: 1.6; margin-bottom: 1rem; }
/* ── Components ─────────────────────────────────────────── */
.trail-card {
background: #f5f5f5;
padding: 24px;
border-radius: 8px;
margin-bottom: 16px;
}
.sidebar {
background: #e8f0fe;
padding: 24px;
border-radius: 8px;
}
/* ── Decorative animation ────────────────────────────────── */
@keyframes fadeSlideIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.hero {
animation: fadeSlideIn 0.8s ease-out;
}
</style>
</head>
<body>
<div class="page">
<header>
<h1>Mountain Trails</h1>
<p>Guided hikes for every level, from gentle valley walks to alpine summits.</p>
</header>
<section class="hero">
<!-- Replace this src with any wide landscape image you have locally -->
<img src="hero.jpg" alt="Panoramic view of a mountain range at golden hour">
</section>
<div class="content">
<aside class="sidebar">
<h2>Upcoming dates</h2>
<p>June 21 — Valley Loop (easy)</p>
<p>July 5 — Ridge Walk (moderate)</p>
<p>July 19 — Summit Push (hard)</p>
<p>August 2 — Overnight Trek (expert)</p>
</aside>
<main>
<div class="trail-card">
<h2>Valley Loop</h2>
<p>A gentle 6km circuit through old-growth forest. Suitable for families and beginners.
Elevation gain: 80m. Duration: 2–3 hours.</p>
</div>
<div class="trail-card">
<h2>Ridge Walk</h2>
<p>12km along the northern ridge with panoramic views. Moderate fitness required.
Elevation gain: 420m. Duration: 4–5 hours.</p>
</div>
<div class="trail-card">
<h2>Summit Push</h2>
<p>18km to the 2300m summit and back. Good fitness and proper gear required.
Elevation gain: 1100m. Duration: 7–9 hours.</p>
</div>
</main>
<aside class="sidebar">
<h2>What to bring</h2>
<p>Water (2L minimum), snacks, sunscreen, layers, and sturdy footwear.
We provide maps, a guide, and emergency gear.</p>
</aside>
</div>
</div>
</body>
</html>Open the file in a browser. Shrink the viewport to 375px wide. Everything overflows — the fixed widths push content off the right edge. That is the starting point you are going to fix.
Task 1 — Add the viewport meta tag
The <head> is missing the viewport meta tag. Without it, mobile browsers
render the page at a faked desktop width and your media queries will not fire.
Add this as the second line inside <head> (after <meta charset>):
<meta name="viewport" content="width=device-width, initial-scale=1">After adding it, reload the page at 375px wide in DevTools. The content is no longer zoomed out — it is rendering at true mobile width, even if it is still broken. You have just made breakpoints work.
Task 2 — Convert to mobile-first layout
Remove the width: 1200px constraint on .page and the three-column
grid-template-columns on .content. Replace them with a mobile-first
approach.
Goal: on narrow screens, the layout is a single column with the sidebar sections stacked above and below the main content. On wider screens, the three-column grid appears.
A rough guide:
.page {
/* Remove the fixed width — use max-width instead */
max-width: 1200px;
margin: 0 auto;
padding: 1.5rem;
}
.content {
display: grid;
grid-template-columns: 1fr; /* single column: mobile base */
gap: 1.5rem;
margin-top: 1.5rem;
}
@media (min-width: 900px) {
.content {
grid-template-columns: 240px 1fr 260px; /* three columns: wide screens */
}
}Also fix the hero image — width: 1200px makes it overflow. Replace it with
width: 100% so it fills its container.
Resize the viewport between 375px and 1280px and confirm:
- Below 900px: one column, sidebar sections stack.
- At 900px and above: three-column layout appears.
Task 3 — Apply fluid typography
The heading font-size: 56px is too large on a phone and the 28px subheadings
are fine on mobile but could grow. Replace the fixed pixel values with clamp().
h1 { font-size: clamp(1.75rem, 5vw, 3.5rem); }
h2 { font-size: clamp(1.25rem, 3vw, 1.75rem); }
p { font-size: clamp(0.95rem, 1.5vw, 1rem); line-height: 1.6; }Drag the viewport slowly from 320px to 1440px and watch the heading size flow rather than jump. That smooth scaling is the whole point.
While you are here, apply clamp() to the padding too:
.page {
padding: clamp(1rem, 5%, 3rem);
}
.trail-card,
.sidebar {
padding: clamp(1rem, 3%, 1.5rem);
}Task 4 — Add srcset and sizes to the hero image
A single hero.jpg sends the same file to every device. Add srcset and
sizes so the browser can choose. For this exercise you can reference the same
source file at different descriptor widths — in production you would prepare
actual resized files.
<img
src="hero.jpg"
srcset="hero-400.jpg 400w,
hero-800.jpg 800w,
hero-1600.jpg 1600w"
sizes="(min-width: 900px) min(100vw, 1200px),
100vw"
alt="Panoramic view of a mountain range at golden hour"
>Also add aspect-ratio to prevent layout shift while the image loads:
.hero img {
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
object-fit: cover;
}Task 5 — Add lazy loading to below-the-fold images
The trail cards do not have images in this layout, but the two sidebar sections are below the fold on mobile. If they had images, those would be good candidates for lazy loading.
Add a small illustrative image to each trail card and apply loading="lazy":
<img
src="trail-valley.jpg"
loading="lazy"
alt="Forest path through tall conifers"
style="width: 100%; aspect-ratio: 16/9; object-fit: cover; border-radius: 4px;"
>Confirm that loading="lazy" is not on the hero image — that one should
load immediately.
Bonus — prefers-reduced-motion
The starting CSS includes a fadeSlideIn animation on .hero. Some users have
"reduce motion" enabled in their OS settings. Add a media query to disable it:
@media (prefers-reduced-motion: reduce) {
.hero {
animation: none;
}
}To test it on desktop: open System Preferences / Settings on your OS, find the Accessibility or Motion section, and enable "Reduce motion." Reload the page. The animation should not play.
Self-review checklist
Work through these before calling the lab done:
-
<meta name="viewport" ...>is the second tag in<head> -
.pageusesmax-width, not a fixedwidth - Below 900px: single column, no overflow
- Above 900px: three-column grid appears
-
h1font size scales smoothly between narrow and wide viewports - Hero image uses
srcsetandsizeswith multiple width descriptors - Hero image has
aspect-ratioset in CSS - Below-the-fold images have
loading="lazy"; hero image does not -
prefers-reduced-motionquery disables the entrance animation
Real-world responsive work adds one more step: testing on actual devices or in BrowserStack, not just DevTools. DevTools simulates viewport dimensions and pixel density but does not replicate touch targets, font rendering, or network conditions accurately. A $300 Android phone is the most useful testing tool on your desk.
Responsive Images
The browser can choose from multiple image sources based on screen size and pixel density — if you provide the options with srcset and sizes.
What Is Web Accessibility?
Accessibility means building pages that work for all users — including those using screen readers, keyboard-only navigation, or other assistive technologies.