See the Pen Effective Retina Backgrounds with SVG by Dudley Storey (@dudleystorey) on CodePen.

While the HTML5 spec now has Retina/HiDPI screens well in hand with a combination of media queries and srcset, images loaded via CSS have been somewhat excluded from recent progress: there’s no CSS equivalent to srcset or sizes to easily fit images of the optimal resolution into page backgrounds.

This oversight is ironic, given that massive high-resolution background pictures arguably consume more bandwidth and download time than smaller inline illustrative images. With that in mind, let’s look at some techniques you can use to code and load the optimally sized images for the all screens.

Option 1: Avoid the problem entirely by using SVG

Considering the qualities of an ideal background image - low contrast, broad strokes rather than fine detail, small file size, and viewport flexibility - in many cases an might well be a better choice for a background, even if the original is a photograph. For example, taking the following image: Photograph of birds on a wire

Saving this at its largest resolution (approx. 1000 × 650 pixels) as a 25% quality JPEG would result in a file size of 17K (pre-gzip), which isn’t bad. But if we bring the image into Adobe Illustrator and apply Live Trace and Expand, then export the result as a cleaned-up and minimized SVG, the result is a 1.7K file: 10% the size of the original JPEG, in a format that is immune to resolution changes.

body {
    margin: 0;
    background-image: url(birds-on-wire.svg);
    background-size: cover;
}

The SVG has some further advantages:

  • unlike a bitmap, the SVG can be easily reshaded, providing better contrast control against body copy and foreground images.
  • the SVG can be layered with a repeated background, providing the impression of a “seamless” infinite image (see the associated CodePen for an example).
  • the SVG file is also small enough to be included directly in the CSS as a DataURI string, potentially saving a HTTP request, speeding up page load still further.

Option 2: Go Big and Crush It

If vector images aren’t a possibility and you’re in a rush, it’s possible to load a single JPEG image and crush it down to 0% quality.

Yes, I said 0%. It may sound like heresy - and it doesn’t work for every image - but a sufficiently high resolution bitmap (>1200px wide) can actually look decent at 0 quality: kudos to Dave Rupert for being the first developer brave enough to try this.

The one downside to this “one size fits all” approach is exactly that: the assumption that that everyone needs a highDPI image, and forcing it on them. A better solution would be to load a bitmap appropriate to the user’s screen size and resolution; for that, we need @media queries.

Option 3: Use Media Queries

First, load the background image that regular, non-Retina screens will see:

body {
    background-image: url("eagle-eye-panorama.jpg");
    background-size: cover;
}

Then, add a media query that loads a higher DPI image for Retina screens:

@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
 only screen and (min-moz-device-pixel-ratio: 1.5),
 only screen and (min-resolution: 240dpi) {
 body {
 background-image: url("eagle-eye-panorama-2x.jpg");
 }
}

Option 4: Use image-set()

The recent addition of image-set() to the CSS lexicon provides for a fourth possibiliity in modern browsers, discussed in-depth in the next article.

Conclusion

There are many possibilities for employing large background images that remain sharp on all screens with minimal file sizes: as always, the ideal solution is arrived at by anticipation (setting a performance budget for each page before you start), research, and testing.

Photograph by Tarik Browne, used under a Creative Commons Attribution-NonCommercial 2.0 Generic license

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/xGNOZr