CSS Background Performance: LCP, Image Formats, and Paint Cost

Last reviewed on 2026-04-30

Backgrounds are quietly responsible for some of the worst performance regressions in modern web pages. Hero images that double the page weight. Animated gradients that pin a phone's GPU at 100%. Glassmorphism panels that drag scrolling into single-digit frame rates on lower-end laptops. None of these are unavoidable; they're just easy to ship without realizing the cost. This page collects the trade-offs you can reason about before opening DevTools, and the few things that actually need a profiler.

The Single Biggest Decision: Image vs. CSS

Almost every background performance question reduces to one earlier one — does this need to be a raster image at all? Solid colors, gradients, and many decorative patterns can be expressed in CSS. A gradient declared in a stylesheet ships as a few dozen bytes inside the CSS file you were already going to download. The same effect drawn into a 1920×1080 PNG can be a quarter-megabyte over the wire and forces an additional HTTP request.

Default to CSS for:

Reach for an image when the background is genuinely photographic, when it's a logo or a complex illustration, or when the visual depends on detail that would take more CSS than a small image would weigh.

Image Format Trade-offs

For backgrounds that have to be raster images, format choice matters more than dimensions. The current useful options:

FormatStrengthsWeaknessesUse for
AVIFSmallest at acceptable quality; HDR support; alphaSlower to decode on older devices; not universal in old browsersHero photos where bytes saved > small decode hit
WebPWide support; good compression; alphaSlightly larger than AVIF at the same qualityDefault workhorse for photo backgrounds
JPEG (progressive)Universal; good for big photographic gradientsNo alpha; larger files than AVIF/WebPFallback only
PNGLossless; alphaHuge for photographic contentSharp edges, small textures, alpha-required UI
SVGResolution-independent; tiny for vector content; CSS-controllableBad for photos; can be CPU-heavy with thousands of nodesPatterns, icons, geometric backgrounds

Use image-set() or the <picture> equivalent for backgrounds so that browsers that support newer formats get them and older ones still see something:

.hero {
  background-image:
    image-set(
      url('hero.avif') type('image/avif'),
      url('hero.webp') type('image/webp'),
      url('hero.jpg')  type('image/jpeg')
    );
  background-size: cover;
  background-position: center;
}

LCP and Background Images

Largest Contentful Paint penalizes background images more than most developers expect. The core issue: the browser doesn't know about a background image until the CSS for that element parses, which is later than an <img> tag's src would be discovered. On a hero with a background photo, the LCP element is often that photo, and it shows up after a measurable delay.

Three fixes, in order of impact:

  1. Use <img> for hero photos when you can. If the only thing the background does is display a photo behind a text overlay, an absolutely-positioned <img> with object-fit: cover behaves identically and gets discovered earlier. The text overlay sits in a sibling element on top.
  2. Preload the hero background. Add <link rel="preload" as="image" href="hero.webp" imagesrcset="..." imagesizes="100vw"> to the head. Combined with fetchpriority="high", this closes most of the gap.
  3. Inline a small placeholder. Encode a 20×20 blurred version as a data URL and put it in the CSS. The browser paints something immediately while the full photo loads.

Paint Cost of Gradients, Filters, and Animations

Pure-CSS visuals are not free either. A handful of effects are particularly expensive on low-end devices:

What to Animate (and What Not To)

Browsers compose only two CSS properties cheaply: transform and opacity. Anything else triggers layout or paint on every animation frame. The practical rule for animated backgrounds:

/* Cheap: composited */
.bg-drift {
  position: absolute; inset: 0;
  background: radial-gradient(circle at 30% 30%, #1a73e8 0%, transparent 60%);
  animation: drift 18s linear infinite;
  will-change: transform;
}
@keyframes drift {
  to { transform: translate(8%, 5%); }
}

/* Expensive: triggers paint every frame */
.bg-bad {
  background: linear-gradient(var(--angle), #1a73e8, #000);
  animation: rotate-bad 18s linear infinite;
}

Mobile and Reduced-Data Behaviour

Two media queries are worth gating background work behind:

@media (prefers-reduced-motion: reduce) {
  .bg-drift { animation: none; }
}

@media (prefers-reduced-data: reduce) {
  .hero { background-image: linear-gradient(135deg, #0a1530, #000); }
}

Decision Criteria

  1. Can this background be expressed in CSS? If yes, use CSS.
  2. If it must be an image, can it be SVG? If yes, use SVG.
  3. If it must be raster, ship AVIF first, WebP second, JPEG/PNG as fallback via image-set().
  4. If the page's LCP candidate is a background photo, switch to <img> + overlay or preload the asset.
  5. If the design needs animation, animate only transform and opacity.
  6. Test on a mid-range phone before shipping. The desktop will lie to you about how fast the page feels.

What to Measure

For the underlying techniques this page is optimizing, see the background-image reference, animated backgrounds, parallax scrolling, gradient mesh, glassmorphism, and the background-size: cover guide. For accessibility implications of optimizations like dropping motion or reducing contrast on weak signals, see accessible dark backgrounds.