Why a Hero Background Hurts LCP
Drupal's Umami demo profile includes a hero component whose background image is loaded via CSS background-image. That triggers a long dependency chain: download HTML, download and parse CSS, reconcile rules with the DOM, download the image, and finally display it.
Because the hero sits in the initial viewport, this directly impacts Largest Contentful Paint (LCP). In a WebPageTest run, the hero's background image was the ninth request, taking over a second to even start downloading — roughly half of the measured 2.4-second LCP.
Step One: Swap CSS for an <img> Element
The first fix is to stop loading the hero image through CSS. Using a standard <img> tag lets the browser's preload scanner discover the asset as it parses HTML, rather than waiting for an external stylesheet.
To replicate a background, wrap the image in a container (say, .hero) and absolutely position the image so it stacks behind other content:
.hero {
position: relative; /* Anchor the image */
}
.hero img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
Absolutely positioning the image takes it out of normal document flow. Two problems follow:
- Distortion. With
width: 100%andheight: 100%, the image stretches to the container's aspect ratio. The CSS-only equivalent ofbackground-size: coverexists for HTML images asobject-fit: cover.
.hero {
position: relative; /* Anchor the image */
}
.hero img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover; /* Prevents squishing */
}
- Content hidden underneath. Any non-static position creates a stacking context, so the hero's text and button flow beneath the image. Give the content wrapper
position: relativeto produce its own stacking context while keeping it in normal flow.
.hero {
position: relative; /* Anchor the image */
}
.hero img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover; /* Prevents squishing */
}
.hero__content {
position: relative; /* Adds a stacking context */
}
Depending on markup order inside the parent, you may need z-index to resolve stacking.
Step Two: Serve WebP
WebP is supported in all modern browsers and generally much smaller than JPEG. After configuring Drupal to output WebP, the hero image in this example shrank by 10% with no visible quality loss. Your mileage will vary — heavily optimized JPEGs yield less than an unoptimized source.
Step Three: Add Responsive Images
Serving one image file to every viewport wastes bandwidth on smaller screens. Responsive images, via the <picture> element, let the browser pick from multiple sizes:
<picture>
<source media="all and (min-width: 1400px)" type="image/webp" width="3000" height="1285">
<source media="all and (min-width: 800px) and (max-width: 1400px)" type="image/webp" width="1440" height="617">
<source media="all and (min-width: 500px) and (max-width: 800px)" type="image/webp" width="1200" height="514">
<source media="all" type="image/webp" width="500" height="214">
<img src="https://www.smashingmagazine.com/img-oath_medium/veggie-pasta-bake-hero-umami.jpg.webp" width="1200" height="514" alt="Mouth watering vegetarian pasta bake with rich tomato sauce and cheese toppings">
</picture>
Drupal supports responsive images out of the box. Other CMSes may require a third-party service like Cloudinary.
Results and Remaining Work
These three changes cut measured LCP by 58%, from 2.4 seconds down to 1.4 seconds. Further gains are possible:
- AVIF typically reduces file size another 20–30% over WebP.
fetchpriorityon the<img>element can hint at the image's importance. Support is still limited; a Drupal core issue is adding an admin UI for this attribute, where the hero would be markedhigh.
Similar to accessibility, the biggest obstacle to making web performance better is indifference.
The takeaway: tools like Lighthouse and WebPageTest expose these problems, but fixing them requires caring about the metrics in the first place.



