Responsive Images Skill
This skill covers modern responsive image techniques: resolution switching with srcset and sizes, art direction with <picture>, and modern format support with graceful fallbacks.
Philosophy
Images should:
- •Serve optimal files - Right size for the display context
- •Use modern formats - AVIF, WebP with JPEG/PNG fallback
- •Prioritize correctly - LCP images load first, others lazy load
- •Work everywhere - Fallbacks for older browsers
The Image Loading Attributes
Every image should have appropriate loading attributes:
<img src="photo.jpg"
alt="Descriptive text"
loading="lazy"
decoding="async"
fetchpriority="auto"/>
Attribute Reference
| Attribute | Values | Purpose |
|---|---|---|
loading | eager, lazy | When to load the image |
decoding | sync, async, auto | How to decode the image |
fetchpriority | high, low, auto | Network priority hint |
Loading Strategy by Context
| Image Type | loading | fetchpriority | decoding |
|---|---|---|---|
| Hero/LCP image | eager | high | async |
| Above fold | eager | auto | async |
| Below fold | lazy | auto | async |
| Thumbnails | lazy | low | async |
| Background decorative | lazy | low | async |
Resolution Switching with srcset and sizes
The Problem
A single image file can't serve all contexts:
- •A 2000px image wastes bandwidth on mobile
- •A 400px image looks blurry on retina displays
- •Screen size and pixel density vary widely
The Solution: srcset and sizes
<img src="photo-800.jpg"
srcset="photo-400.jpg 400w,
photo-800.jpg 800w,
photo-1200.jpg 1200w,
photo-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw,
(max-width: 1200px) 50vw,
800px"
alt="Product photograph"
loading="lazy"
decoding="async"/>
How It Works
- •
srcsetlists available image files with their widths (wdescriptor) - •
sizestells the browser how wide the image will display - •Browser calculates optimal file based on viewport + pixel density
- •
srcprovides fallback for browsers withoutsrcsetsupport
The sizes Attribute
sizes uses media conditions to describe rendered width:
sizes="(max-width: 600px) 100vw,
(max-width: 1200px) 50vw,
800px"
This means:
- •At 600px viewport or less: image displays at 100% viewport width
- •At 601-1200px viewport: image displays at 50% viewport width
- •Above 1200px: image displays at 800px fixed width
Common sizes Patterns
| Context | sizes Value |
|---|---|
| Full-width hero | 100vw |
| Content image (max 800px) | (max-width: 800px) 100vw, 800px |
| Two-column grid | (max-width: 600px) 100vw, 50vw |
| Three-column grid | (max-width: 600px) 100vw, (max-width: 900px) 50vw, 33vw |
| Card thumbnail | (max-width: 600px) 100vw, 300px |
| Gallery thumbnail | (max-width: 600px) 50vw, 220px |
Art Direction with <picture>
When to Use <picture>
Use <picture> when you need different images for different contexts:
- •Different crops (portrait mobile, landscape desktop)
- •Different aspect ratios
- •Showing/hiding details at different sizes
- •Different content entirely
Basic Art Direction
<picture>
<source media="(max-width: 600px)"
srcset="photo-mobile.jpg"/>
<source media="(max-width: 1200px)"
srcset="photo-tablet.jpg"/>
<img src="photo-desktop.jpg"
alt="Product photograph"
loading="lazy"
decoding="async"/>
</picture>
Art Direction with srcset
Combine art direction with resolution switching:
<picture>
<!-- Mobile: portrait crop -->
<source media="(max-width: 600px)"
srcset="photo-mobile-400.jpg 400w,
photo-mobile-600.jpg 600w"
sizes="100vw"/>
<!-- Tablet: square crop -->
<source media="(max-width: 1200px)"
srcset="photo-tablet-600.jpg 600w,
photo-tablet-900.jpg 900w"
sizes="50vw"/>
<!-- Desktop: landscape crop -->
<img src="photo-desktop-800.jpg"
srcset="photo-desktop-800.jpg 800w,
photo-desktop-1200.jpg 1200w,
photo-desktop-1600.jpg 1600w"
sizes="800px"
alt="Product photograph"
loading="lazy"
decoding="async"/>
</picture>
Modern Image Formats
Format Comparison
| Format | Compression | Browser Support | Use Case |
|---|---|---|---|
| AVIF | Best (50% smaller than JPEG) | Chrome, Firefox, Safari 16+ | Primary modern format |
| WebP | Very good (25-35% smaller) | All modern browsers | Fallback for AVIF |
| JPEG | Good | Universal | Final fallback |
| PNG | Lossless | Universal | Transparency, screenshots |
Format Fallback Pattern
<picture>
<source srcset="photo.avif" type="image/avif"/>
<source srcset="photo.webp" type="image/webp"/>
<img src="photo.jpg"
alt="Descriptive text"
loading="lazy"
decoding="async"/>
</picture>
Complete Pattern: Formats + Resolution + Art Direction
<picture>
<!-- Mobile: AVIF -->
<source media="(max-width: 600px)"
type="image/avif"
srcset="photo-mobile-400.avif 400w,
photo-mobile-600.avif 600w"
sizes="100vw"/>
<!-- Mobile: WebP -->
<source media="(max-width: 600px)"
type="image/webp"
srcset="photo-mobile-400.webp 400w,
photo-mobile-600.webp 600w"
sizes="100vw"/>
<!-- Mobile: JPEG -->
<source media="(max-width: 600px)"
srcset="photo-mobile-400.jpg 400w,
photo-mobile-600.jpg 600w"
sizes="100vw"/>
<!-- Desktop: AVIF -->
<source type="image/avif"
srcset="photo-800.avif 800w,
photo-1200.avif 1200w"
sizes="(max-width: 1200px) 50vw, 800px"/>
<!-- Desktop: WebP -->
<source type="image/webp"
srcset="photo-800.webp 800w,
photo-1200.webp 1200w"
sizes="(max-width: 1200px) 50vw, 800px"/>
<!-- Desktop: JPEG fallback -->
<img src="photo-800.jpg"
srcset="photo-800.jpg 800w,
photo-1200.jpg 1200w"
sizes="(max-width: 1200px) 50vw, 800px"
alt="Descriptive text"
loading="lazy"
decoding="async"/>
</picture>
Common Image Patterns
Hero Image (LCP)
<picture>
<source type="image/avif"
srcset="hero-800.avif 800w,
hero-1200.avif 1200w,
hero-1920.avif 1920w"
sizes="100vw"/>
<source type="image/webp"
srcset="hero-800.webp 800w,
hero-1200.webp 1200w,
hero-1920.webp 1920w"
sizes="100vw"/>
<img src="hero-1200.jpg"
srcset="hero-800.jpg 800w,
hero-1200.jpg 1200w,
hero-1920.jpg 1920w"
sizes="100vw"
alt="Hero image description"
loading="eager"
fetchpriority="high"
decoding="async"/>
</picture>
Content Image
<picture>
<source type="image/avif"
srcset="content-400.avif 400w,
content-800.avif 800w,
content-1200.avif 1200w"
sizes="(max-width: 800px) 100vw, 800px"/>
<source type="image/webp"
srcset="content-400.webp 400w,
content-800.webp 800w,
content-1200.webp 1200w"
sizes="(max-width: 800px) 100vw, 800px"/>
<img src="content-800.jpg"
srcset="content-400.jpg 400w,
content-800.jpg 800w,
content-1200.jpg 1200w"
sizes="(max-width: 800px) 100vw, 800px"
alt="Content image description"
loading="lazy"
decoding="async"/>
</picture>
Card Thumbnail
<picture>
<source type="image/avif"
srcset="thumb-300.avif 300w,
thumb-450.avif 450w"
sizes="(max-width: 600px) 100vw, 300px"/>
<source type="image/webp"
srcset="thumb-300.webp 300w,
thumb-450.webp 450w"
sizes="(max-width: 600px) 100vw, 300px"/>
<img src="thumb-300.jpg"
srcset="thumb-300.jpg 300w,
thumb-450.jpg 450w"
sizes="(max-width: 600px) 100vw, 300px"
alt="Card thumbnail description"
loading="lazy"
decoding="async"/>
</picture>
Gallery Image
<picture>
<source type="image/avif"
srcset="gallery-220.avif 220w,
gallery-330.avif 330w,
gallery-440.avif 440w"
sizes="(max-width: 600px) 50vw, 220px"/>
<source type="image/webp"
srcset="gallery-220.webp 220w,
gallery-330.webp 330w,
gallery-440.webp 440w"
sizes="(max-width: 600px) 50vw, 220px"/>
<img src="gallery-220.jpg"
srcset="gallery-220.jpg 220w,
gallery-330.jpg 330w,
gallery-440.jpg 440w"
sizes="(max-width: 600px) 50vw, 220px"
alt="Gallery image description"
loading="lazy"
decoding="async"/>
</picture>
Simplified Patterns
When Full <picture> Is Overkill
For simpler cases, srcset alone may suffice:
<!-- Resolution switching only, no format variants -->
<img src="photo-800.jpg"
srcset="photo-400.jpg 400w,
photo-800.jpg 800w,
photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="Description"
loading="lazy"
decoding="async"/>
Minimum Viable Responsive Image
At minimum, always include:
<img src="photo.jpg"
alt="Descriptive text"
loading="lazy"
decoding="async"
width="800"
height="600"/>
The width and height attributes prevent layout shift.
Image Sizing Strategy
Images in Container-Queried Components
The HTML sizes attribute uses viewport media queries, not container queries. For images inside container-queried components, the sizes may not accurately describe rendered width.
Strategies:
- •Generous
sizesestimation - Provide srcset that covers the full range:
<!-- Inside a container-queried card that could be 300-800px wide -->
<img srcset="photo-300.jpg 300w,
photo-450.jpg 450w,
photo-600.jpg 600w,
photo-800.jpg 800w"
sizes="(max-width: 600px) 100vw, 800px"
alt="Product photo"/>
- •CSS-controlled sizing - Let CSS and container queries control the rendered size:
@layer components {
product-card {
container-type: inline-size;
}
product-card img {
width: 100%;
height: auto;
aspect-ratio: 4/3;
object-fit: cover;
}
@container (min-width: 500px) {
product-card img {
width: 40%;
}
}
}
The browser still selects from srcset based on rendered size after CSS is applied.
- •Use
object-fitwith container units for fluid sizing:
product-card img {
width: min(100%, 20cqi);
aspect-ratio: 1;
object-fit: cover;
}
Note: Future CSS may support container-based image selection, but for now, provide a robust srcset range.
Standard Breakpoints for srcset
Generate images at these widths to cover common scenarios:
| Width | Use Case |
|---|---|
| 400w | Mobile thumbnails |
| 600w | Mobile full-width |
| 800w | Tablet/small desktop |
| 1200w | Desktop content |
| 1600w | Large desktop/retina |
| 1920w | Full-width heroes |
Calculating sizes
- •Measure rendered width at key breakpoints
- •Use viewport units (
vw) for fluid images - •Use fixed pixels for constrained images
- •Order conditions from narrowest to widest
Accessibility
Alt Text Requirements
Every image needs meaningful alt text:
<!-- Informative: describe the content --> <img alt="Team members collaborating around a whiteboard"/> <!-- Decorative: empty alt --> <img alt="" role="presentation"/> <!-- Functional: describe the action --> <img alt="Search"/>
Avoid Redundant Alt Text
<!-- Bad: redundant with adjacent text --> <figure> <img alt="Photo of our office building"/> <figcaption>Our office building in Seattle</figcaption> </figure> <!-- Good: complementary description --> <figure> <img alt="Modern glass-walled building with green roof"/> <figcaption>Our office building in Seattle</figcaption> </figure>
Checklist
When adding images:
- • Include
altattribute (descriptive or empty for decorative) - • Add
loadingattribute (eagerfor LCP,lazyfor others) - • Add
decoding="async"for all images - • Add
fetchpriority="high"for LCP/hero images - • Include
widthandheightto prevent layout shift - • Use
srcsetfor resolution switching when image > 400px - • Calculate appropriate
sizesbased on rendered width - • Use
<picture>for format fallbacks (AVIF > WebP > JPEG) - • Use
<picture>for art direction (different crops) - • Test with network throttling to verify lazy loading
Related Skills
- •images - Umbrella coordinator for image handling with automation
- •css-author - Container queries for component-scoped image sizing
- •performance - Write performance-friendly HTML pages
- •xhtml-author - Write valid XHTML-strict HTML5 markup
- •placeholder-images - Generate SVG placeholder images for prototypes