Episode 1 — Fundamentals / 1.9 — CSS Responsive Design
1.9.d — Responsive Images
In one sentence: Responsive images use
object-fit,srcset,<picture>, and modern formats to deliver the right image at the right size — saving bandwidth, preventing layout shifts, and keeping visuals sharp on every screen.
Navigation: ← 1.9.c — Breakpoint Planning · 1.9.e — Fluid Typography →
1. The problem
A single 2400px-wide hero image:
- Wastes bandwidth on a 375px phone (downloads 6× more pixels than needed).
- Looks blurry on a 4K monitor if you only ship a small version.
- Causes layout shifts if dimensions aren't reserved.
Responsive images solve all three.
2. object-fit
When an <img> or <video> is placed inside a container with fixed dimensions, object-fit controls how the content fills the box — like background-size but for replaced elements.
| Value | Behavior |
|---|---|
fill | Stretches to fill the box exactly (default — can distort) |
contain | Scales to fit inside the box, preserving aspect ratio (may show gaps) |
cover | Scales to cover the entire box, preserving aspect ratio (may crop) |
none | No resizing — shows at natural size, may overflow |
scale-down | Behaves like contain or none, whichever is smaller |
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
}
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│ │ ▓▓▓▓▓▓▓▓▓▓▓▓▓ │ │▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│
│▓▓▓▓ fill ▓▓▓▓▓▓▓▓▓│ │ ▓ contain ▓▓▓ │ │▓▓▓▓▓ cover ▓▓▓▓▓▓▓│
│▓▓▓ (distorts) ▓▓▓▓│ │ ▓▓▓▓▓▓▓▓▓▓▓▓▓ │ │▓▓▓▓ (crops) ▓▓▓▓▓▓│
│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│ │ │ │▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│
└───────────────────┘ └───────────────────┘ └───────────────────┘
3. object-position
Works with object-fit to control where the image anchors inside its box. Same syntax as background-position.
.avatar {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
object-position: top center; /* keep face visible */
}
| Value | Effect |
|---|---|
center (default) | Centered in both axes |
top | Anchored to top edge |
50% 20% | 50% across, 20% down |
right bottom | Anchored to bottom-right |
4. srcset and sizes (recap)
Covered in depth in 1.5.h — Responsive Images. Quick refresher:
Resolution switching
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w,
photo-800.jpg 800w,
photo-1200.jpg 1200w"
sizes="(min-width: 1024px) 50vw,
100vw"
alt="Mountain landscape"
/>
srcset— list of image files with their intrinsic widths (wdescriptor).sizes— tells the browser how wide the image will be displayed at each breakpoint.- The browser picks the best file based on viewport width and device pixel ratio.
Pixel-density switching
<img
src="logo.png"
srcset="logo.png 1x,
logo@2x.png 2x,
logo@3x.png 3x"
alt="Company logo"
/>
5. <picture> for art direction
When you need different crops or images at different sizes (not just different resolutions), use <picture>:
<picture>
<!-- Wide: panoramic crop -->
<source
media="(min-width: 1024px)"
srcset="hero-wide.webp"
type="image/webp"
/>
<!-- Tablet: square crop -->
<source
media="(min-width: 640px)"
srcset="hero-square.webp"
type="image/webp"
/>
<!-- Mobile: tall crop + fallback format -->
<source srcset="hero-tall.webp" type="image/webp" />
<img src="hero-tall.jpg" alt="Product showcase" />
</picture>
Key difference: srcset lets the browser choose the best resolution. <picture> lets the author control which image shows at which breakpoint.
6. Modern image formats
| Format | Compression | Transparency | Animation | Browser support |
|---|---|---|---|---|
| JPEG | Good (lossy) | No | No | Universal |
| PNG | Fair (lossless) | Yes | No | Universal |
| WebP | Great (lossy + lossless) | Yes | Yes | 97%+ |
| AVIF | Excellent (lossy + lossless) | Yes | Yes | 92%+ |
Strategy: Serve AVIF with WebP fallback, JPEG as final fallback:
<picture>
<source srcset="photo.avif" type="image/avif" />
<source srcset="photo.webp" type="image/webp" />
<img src="photo.jpg" alt="Description" />
</picture>
7. Lazy loading
Images below the fold should load only when the user scrolls near them.
<!-- Native lazy loading -->
<img src="photo.jpg" alt="…" loading="lazy" width="800" height="600" />
loading value | Behavior |
|---|---|
eager (default) | Load immediately |
lazy | Defer until near viewport |
Rules of thumb:
- Hero images / above-the-fold: Use
loading="eager"(or omit — it's the default) and addfetchpriority="high". - Everything else: Use
loading="lazy". - Always include
widthandheightto prevent CLS while the image loads.
8. The aspect-ratio property
Reserve space for an image before it loads to prevent Cumulative Layout Shift (CLS):
.thumbnail {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
.square-avatar {
width: 80px;
aspect-ratio: 1; /* shorthand for 1 / 1 */
object-fit: cover;
border-radius: 50%;
}
The browser calculates the height automatically from the width and ratio, reserving exact space in the layout.
/* Responsive video container */
.video-wrapper {
width: 100%;
aspect-ratio: 16 / 9;
background: #000;
}
.video-wrapper iframe {
width: 100%;
height: 100%;
}
9. Image performance checklist
| Step | Action |
|---|---|
| 1 | Right format — AVIF > WebP > JPEG/PNG |
| 2 | Right size — never serve wider than the display size × DPR |
| 3 | Compress — 80% quality for lossy is usually indistinguishable |
| 4 | srcset + sizes — let the browser pick the best resolution |
| 5 | loading="lazy" — defer below-fold images |
| 6 | width + height or aspect-ratio — prevent CLS |
| 7 | fetchpriority="high" — on hero/LCP images |
| 8 | CDN with auto-format — Cloudinary, imgix, Vercel Image Optimization auto-serve best format |
10. Key takeaways
object-fit: coveris your go-to for fixed-dimension image containers.srcset+sizes= resolution switching (browser chooses).<picture>= art direction (you choose).- Ship AVIF → WebP → JPEG for maximum compression with graceful fallback.
loading="lazy"on below-fold images, always withwidth/heightto prevent CLS.aspect-ratioreserves space and eliminates the old padding-bottom hack.
Explain-It Challenge
Explain without notes:
- When would you use
object-fit: containinstead ofcover, and what visual difference does the user see? - Why does
<picture>exist whensrcsetalready handles multiple image sources? - A page has a CLS score of 0.35. Images are the primary cause. Walk through the fixes you'd apply.
Navigation: ← 1.9.c — Breakpoint Planning · 1.9.e — Fluid Typography →