Skip to main content

Responsive Images and Content: Complete Guide

Images are often the biggest bandwidth consumers on a page. Responsive image techniques ensure users get the right image at the right size — improving performance, Core Web Vitals, and user experience.

1. The Fluid Image Baseline

The single most important CSS for images — every project should have this in its reset:

img,
video,
svg {
max-width: 100%; /* Never overflow container */
height: auto; /* Maintain aspect ratio */
display: block; /* Remove inline-block bottom gap */
}

2. object-fit — Control How Images Fill Containers

When an image's container has explicit dimensions, object-fit controls how the image fills the space:

.card__image {
width: 100%;
height: 240px; /* Fixed height */
object-fit: cover; /* Crop to fill — like background-size: cover */
}
ValueBehavior
fillStretches to fill. Distorts aspect ratio. (Default)
containFits entirely inside. May leave empty space.
coverFills entirely. Crops if necessary. Maintains ratio. ✅ Most used
noneOriginal size, no scaling
scale-downnone or contain, whichever is smaller
.avatar { width: 60px; height: 60px; border-radius: 50%; object-fit: cover; }
.hero-image { width: 100%; height: 60vh; object-fit: cover; object-position: center top; }
.logo { width: 180px; height: 60px; object-fit: contain; } /* Don't crop logos */

3. object-position — Control the Focus Point

Works like background-position — shifts what part of the image is visible when cropped:

.portrait { object-fit: cover; object-position: top center; } /* Don't crop the face */
.landscape { object-fit: cover; object-position: center; } /* Default center */
.custom { object-fit: cover; object-position: 30% 20%; } /* Custom focal point */

4. Aspect Ratio Control

Maintain consistent proportions without defining both width and height:

.thumbnail { aspect-ratio: 16 / 9; width: 100%; object-fit: cover; }
.avatar { aspect-ratio: 1; width: 60px; object-fit: cover; border-radius: 50%; }
.square { aspect-ratio: 1 / 1; }
.portrait { aspect-ratio: 3 / 4; }

The legacy method (still works for backgrounds):

/* The padding-top trick (pre-aspect-ratio CSS) */
.ratio-16-9 {
position: relative;
padding-top: 56.25%; /* 9/16 = 0.5625 */
}
.ratio-16-9 > img {
position: absolute;
inset: 0;
width: 100%; height: 100%;
object-fit: cover;
}

5. HTML Responsive Images: srcset and sizes

Let the browser choose the right image for the screen's resolution and size:

<!-- Resolution descriptors: 1x (standard) and 2x (retina) -->
<img src="logo.png"
srcset="logo.png 1x, logo@2x.png 2x"
alt="Logo">

<!-- Width descriptors: browser picks best based on viewport + sizes -->
<img src="hero-800.jpg"
srcset="hero-400.jpg 400w,
hero-800.jpg 800w,
hero-1200.jpg 1200w,
hero-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw,
(max-width: 1024px) 80vw,
1200px"
alt="Hero image">

6. The <picture> Element — Art Direction

Serve completely different images for different viewports (not just different sizes of the same image):

<picture>
<!-- Mobile: vertical/portrait crop -->
<source media="(max-width: 640px)"
srcset="hero-portrait.webp"
type="image/webp">

<!-- Tablet: square crop -->
<source media="(max-width: 1024px)"
srcset="hero-square.webp"
type="image/webp">

<!-- Desktop: wide landscape -->
<source srcset="hero-landscape.webp" type="image/webp">

<!-- Fallback: always required last -->
<img src="hero-landscape.jpg" alt="Hero image" loading="lazy">
</picture>

7. Modern Image Formats via <picture>

Use next-gen formats with fallback for older browsers:

<picture>
<source srcset="photo.avif" type="image/avif"> <!-- Best compression -->
<source srcset="photo.webp" type="image/webp"> <!-- Wide support -->
<img src="photo.jpg" alt="Photo"> <!-- Fallback -->
</picture>

8. Lazy Loading

Defer off-screen images until the user scrolls near them:

<!-- Native lazy loading — supported by all modern browsers -->
<img src="photo.jpg" alt="..." loading="lazy">

<!-- Always eager-load above-the-fold images -->
<img src="hero.jpg" alt="..." loading="eager" fetchpriority="high">
/* CSS-only image lazy load skeleton */
img[loading="lazy"] {
background: linear-gradient(90deg, var(--color-surface) 25%, rgba(255,255,255,0.05) 50%, var(--color-surface) 75%);
background-size: 200% 100%;
animation: skeleton-shimmer 1.5s infinite;
}
@keyframes skeleton-shimmer {
from { background-position: 200% 0; }
to { background-position: -200% 0; }
}

9. Fluid Typography Recap

Typography is also "content" that must be responsive:

/* Fluid scale — no media queries needed */
:root {
--text-sm: clamp(0.875rem, 0.8rem + 0.25vw, 1rem);
--text-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
--text-xl: clamp(1.25rem, 1rem + 1vw, 1.75rem);
--text-hero: clamp(2rem, 1rem + 4vw, 5rem);
}

h1 { font-size: var(--text-hero); }
p { font-size: var(--text-base); }

10. WordPress Responsive Image Best Practices

WordPress automatically generates multiple image sizes. To ensure they load correctly:

// functions.php — add a custom image size
add_image_size('hero-xl', 1600, 600, true); // width, height, crop
add_image_size('card-thumb', 400, 300, true);

In Additional CSS — ensure WP images are fluid:

.wp-block-image img,
.attachment-full,
.entry-content img {
max-width: 100%;
height: auto;
}
/* GeneratePress featured image */
.featured-image img { width: 100%; object-fit: cover; }