home / skills / jezweb / claude-skills / responsive-images

responsive-images skill

/skills/responsive-images

This skill helps you deliver performant responsive images with srcset, sizes, modern formats, and art-directed picture elements to improve CLS and LCP.

npx playbooks add skill jezweb/claude-skills --skill responsive-images

Review the files below or copy the command above to add this skill to your agents.

Files (10)
SKILL.md
9.1 KB
---
name: responsive-images
description: |
  Implement performant responsive images with srcset, sizes, lazy loading, and modern formats (WebP, AVIF). Covers aspect-ratio for CLS prevention, picture element for art direction, and fetchpriority for LCP optimization.

  Use when: adding images to pages, optimizing Core Web Vitals, preventing layout shift, implementing art direction, or converting to modern formats.
license: MIT
metadata:
  keywords:
    - responsive images
    - srcset
    - sizes
    - lazy loading
    - webp
    - avif
    - aspect-ratio
    - CLS
    - LCP
    - fetchpriority
  version: 1.0.0
  last_updated: 2026-01-14
---

# Responsive Images

**Status**: Production Ready ✅
**Last Updated**: 2026-01-14
**Standards**: Web Performance Best Practices, Core Web Vitals

---

## Quick Start

### Basic Responsive Image

```html
<img
  src="/images/hero-800.jpg"
  srcset="
    /images/hero-400.jpg 400w,
    /images/hero-800.jpg 800w,
    /images/hero-1200.jpg 1200w,
    /images/hero-1600.jpg 1600w
  "
  sizes="(max-width: 640px) 100vw,
         (max-width: 1024px) 90vw,
         1200px"
  alt="Hero image description"
  width="1200"
  height="675"
  loading="lazy"
/>
```

### Hero Image (LCP)

```html
<img
  src="/images/hero-1200.jpg"
  srcset="
    /images/hero-800.jpg 800w,
    /images/hero-1200.jpg 1200w,
    /images/hero-1600.jpg 1600w
  "
  sizes="100vw"
  alt="Hero image"
  width="1600"
  height="900"
  loading="eager"
  fetchpriority="high"
/>
```

---

## Configuration

### Recommended Image Sizes

| Use Case | Widths to Generate | Sizes Attribute |
|----------|-------------------|-----------------|
| Full-width hero | 800w, 1200w, 1600w, 2400w | `100vw` |
| Content width | 400w, 800w, 1200w | `(max-width: 768px) 100vw, 800px` |
| Grid cards (3-col) | 300w, 600w, 900w | `(max-width: 768px) 100vw, 33vw` |
| Sidebar thumbnail | 150w, 300w | `150px` |

### Lazy Loading Rules

| Image Position | loading | fetchpriority | Why |
|----------------|---------|---------------|-----|
| Hero/LCP | `eager` | `high` | Optimize LCP, prioritize download |
| Above fold (not LCP) | `eager` | omit | Load normally |
| Below fold | `lazy` | omit | Defer until near viewport |
| Off-screen carousel | `lazy` | omit | Defer until interaction |

---

## Common Patterns

### Full-Width Responsive Image

```html
<img
  src="/images/banner-1200.jpg"
  srcset="
    /images/banner-600.jpg 600w,
    /images/banner-1200.jpg 1200w,
    /images/banner-1800.jpg 1800w,
    /images/banner-2400.jpg 2400w
  "
  sizes="100vw"
  alt="Full width banner"
  width="2400"
  height="800"
  loading="lazy"
  class="w-full h-auto"
/>
```

### Grid Card Image (3 columns)

```html
<img
  src="/images/card-600.jpg"
  srcset="
    /images/card-300.jpg 300w,
    /images/card-600.jpg 600w,
    /images/card-900.jpg 900w
  "
  sizes="(max-width: 768px) 100vw,
         (max-width: 1024px) 50vw,
         33vw"
  alt="Card image"
  width="900"
  height="600"
  loading="lazy"
  class="w-full h-auto"
/>
```

### Fixed Aspect Ratio Container

```html
<div class="aspect-[16/9] overflow-hidden">
  <img
    src="/images/video-thumbnail-800.jpg"
    srcset="
      /images/video-thumbnail-400.jpg 400w,
      /images/video-thumbnail-800.jpg 800w,
      /images/video-thumbnail-1200.jpg 1200w
    "
    sizes="(max-width: 768px) 100vw, 800px"
    alt="Video thumbnail"
    width="800"
    height="450"
    loading="lazy"
    class="w-full h-full object-cover"
  />
</div>
```

### Modern Formats (WebP + AVIF)

```html
<picture>
  <source
    srcset="
      /images/hero-800.avif 800w,
      /images/hero-1200.avif 1200w,
      /images/hero-1600.avif 1600w
    "
    sizes="100vw"
    type="image/avif"
  />
  <source
    srcset="
      /images/hero-800.webp 800w,
      /images/hero-1200.webp 1200w,
      /images/hero-1600.webp 1600w
    "
    sizes="100vw"
    type="image/webp"
  />
  <img
    src="/images/hero-1200.jpg"
    srcset="
      /images/hero-800.jpg 800w,
      /images/hero-1200.jpg 1200w,
      /images/hero-1600.jpg 1600w
    "
    sizes="100vw"
    alt="Hero image"
    width="1600"
    height="900"
    loading="eager"
    fetchpriority="high"
  />
</picture>
```

### Art Direction (Different Crops)

```html
<picture>
  <source
    media="(max-width: 640px)"
    srcset="
      /images/product-portrait-400.jpg 400w,
      /images/product-portrait-800.jpg 800w
    "
    sizes="100vw"
  />
  <source
    media="(min-width: 641px)"
    srcset="
      /images/product-landscape-800.jpg 800w,
      /images/product-landscape-1200.jpg 1200w,
      /images/product-landscape-1600.jpg 1600w
    "
    sizes="(max-width: 1024px) 90vw, 1200px"
  />
  <img
    src="/images/product-landscape-1200.jpg"
    alt="Product image"
    width="1200"
    height="675"
    loading="lazy"
  />
</picture>
```

---

## Error Prevention

### Always Include Width and Height

**Problem**: Layout shift when images load (poor CLS score)

```html
<!-- ❌ WRONG - causes layout shift -->
<img src="/image.jpg" alt="Image" loading="lazy" />

<!-- ✅ CORRECT - browser reserves space -->
<img
  src="/image.jpg"
  alt="Image"
  width="800"
  height="600"
  loading="lazy"
/>
```

**Source**: [Web.dev - Optimize CLS](https://web.dev/articles/optimize-cls)

### Don't Lazy Load LCP Images

**Problem**: Delayed LCP, poor Core Web Vitals score

```html
<!-- ❌ WRONG - delays LCP -->
<img
  src="/hero.jpg"
  alt="Hero"
  loading="lazy"
/>

<!-- ✅ CORRECT - prioritizes LCP -->
<img
  src="/hero.jpg"
  alt="Hero"
  loading="eager"
  fetchpriority="high"
/>
```

**Source**: [Web.dev - Optimize LCP](https://web.dev/articles/optimize-lcp)

### Use Width Descriptors (w), Not Density (x)

**Problem**: Browser can't choose optimal image for viewport

```html
<!-- ❌ WRONG - only considers DPR -->
<img
  src="/image.jpg"
  srcset="/image.jpg 1x, /[email protected] 2x"
  alt="Image"
/>

<!-- ✅ CORRECT - considers viewport + DPR -->
<img
  src="/image-800.jpg"
  srcset="
    /image-400.jpg 400w,
    /image-800.jpg 800w,
    /image-1200.jpg 1200w
  "
  sizes="(max-width: 768px) 100vw, 800px"
  alt="Image"
  width="800"
  height="600"
/>
```

**Exception**: Density descriptors are appropriate for fixed-size images like logos.

### Always Include Alt Text

**Problem**: Fails accessibility, SEO, and screen readers

```html
<!-- ❌ WRONG -->
<img src="/product.jpg" />

<!-- ✅ CORRECT - descriptive alt text -->
<img src="/product.jpg" alt="Red leather messenger bag with brass buckles" />

<!-- ✅ CORRECT - decorative images use empty alt -->
<img src="/decorative-line.svg" alt="" role="presentation" />
```

### Aspect Ratio with object-fit

**Problem**: Image stretches or squashes when container size differs from image dimensions

```html
<!-- ❌ WRONG - image distorts -->
<div class="w-full h-64">
  <img src="/image.jpg" alt="Image" class="w-full h-full" />
</div>

<!-- ✅ CORRECT - maintains aspect ratio -->
<div class="w-full h-64">
  <img
    src="/image.jpg"
    alt="Image"
    class="w-full h-full object-cover"
  />
</div>
```

---

## Quick Reference

### Sizes Attribute Patterns

```html
<!-- Full width -->
sizes="100vw"

<!-- Content width (max 800px) -->
sizes="(max-width: 768px) 100vw, 800px"

<!-- Sidebar (fixed 300px) -->
sizes="300px"

<!-- 2-column grid -->
sizes="(max-width: 768px) 100vw, 50vw"

<!-- 3-column grid -->
sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw"

<!-- Responsive with max-width -->
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 90vw, 1200px"
```

### Common Aspect Ratios

| Ratio | CSS | Use Case |
|-------|-----|----------|
| 16:9 | `aspect-[16/9]` | Video thumbnails, hero images |
| 4:3 | `aspect-[4/3]` | Standard photos |
| 3:2 | `aspect-[3/2]` | DSLR photos |
| 1:1 | `aspect-square` | Profile pictures, Instagram-style |
| 21:9 | `aspect-[21/9]` | Ultrawide banners |

### object-fit Values

| Value | Behavior | Use Case |
|-------|----------|----------|
| `cover` | Fill container, crop edges | Card images, backgrounds |
| `contain` | Fit inside, preserve all content | Logos, product photos |
| `fill` | Stretch to fill | Avoid unless necessary |
| `scale-down` | Smaller of `contain` or original size | Mixed content sizes |

### Format Comparison

| Format | Quality | File Size | Browser Support | Use Case |
|--------|---------|-----------|-----------------|----------|
| JPEG | Good | Medium | 100% | Photos, complex images |
| PNG | Lossless | Large | 100% | Logos, transparency |
| WebP | Excellent | Small | 97%+ | Modern browsers, photos |
| AVIF | Excellent | Smallest | 90%+ | Cutting-edge, fallback required |

**Recommended Strategy**: AVIF → WebP → JPEG fallback using `<picture>`

---

## Resources

- [Web.dev: Responsive Images](https://web.dev/articles/responsive-images)
- [MDN: Responsive Images](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)
- [Web.dev: Serve Images in Modern Formats](https://web.dev/articles/serve-images-webp)
- [Web.dev: Optimize Cumulative Layout Shift](https://web.dev/articles/optimize-cls)
- [Cloudflare Images Documentation](https://developers.cloudflare.com/images/)

---

**Token Efficiency**: ~70% savings by preventing trial-and-error with srcset/sizes syntax
**Errors Prevented**: 6 common responsive image issues documented above

Overview

This skill implements performant responsive images using srcset, sizes, lazy loading, and modern formats (WebP, AVIF) to improve load speed and Core Web Vitals. It also enforces aspect-ratio patterns to prevent CLS, supports picture-based art direction, and applies fetchpriority for LCP optimization. Production-ready recommendations and code patterns for React/TypeScript and static sites are included.

How this skill works

The skill inspects image markup and prescribes srcset width descriptors, sizes attributes, and appropriate loading/fetchpriority values based on image role (LCP, above-the-fold, below-the-fold). It recommends generating multiple image widths, adding width and height to reserve layout space, wrapping sources in picture for AVIF/WebP fallbacks, and using aspect containers or CSS object-fit to avoid layout shift. Output includes concrete patterns and table-driven rules for generating sizes and lazy-loading behavior.

When to use it

  • Adding images to pages where page speed and Core Web Vitals matter
  • Optimizing Largest Contentful Paint (LCP) and reducing Cumulative Layout Shift (CLS)
  • Implementing art direction or different crops per breakpoint
  • Converting image pipelines to modern formats like AVIF/WebP with fallbacks
  • Building responsive grids, hero banners, or card components in React or static sites

Best practices

  • Always include width and height (or fixed aspect-ratio container) so the browser reserves space
  • Use width descriptors (w) in srcset plus a proper sizes attribute; avoid density descriptors except for fixed-size assets
  • Do not lazy-load LCP images—use loading="eager" and fetchpriority="high" for heroes
  • Serve AVIF → WebP → JPEG fallback using picture/source elements for best compression and compatibility
  • Generate a small set of widths tailored to each use case (hero, content, cards, thumbnails) to limit bytes and cache variants

Example use cases

  • Full-width hero: sizes="100vw" with eager loading and fetchpriority="high" to optimize LCP
  • Grid cards: 300w/600w/900w srcset and responsive sizes for 3-column layouts
  • Product pages: picture element with portrait crop for mobile and landscape crop for desktop (art direction)
  • Video thumbnails: aspect-[16/9] container plus object-cover to avoid CLS and preserve crop
  • Migrating images to AVIF/WebP: provide sources in picture with JPEG fallback

FAQ

Why include width and height attributes?

They reserve layout space so images don't shift when they load, improving CLS.

When should I use fetchpriority?

Use fetchpriority="high" for your LCP image to ensure it downloads early; omit for non-critical images.