Better background images for responsive web design
Since publishing this yesterday, I’ve revised the post in response to many people saying that this is simply a stop-gap for browsers without background-size
support. That’s true to a point, but the method proposed here offers several advantages to the CSS-only approach.
Responsive web design, we have a problem.
We apply percentage-based widths to our img
elements to get fluid images and this is a very good thing. But what about background images? Support for background-size
is pretty abysmal right now and still isn’t bulletproof, even when it is supported (I’ll get to that in a second), so we’ve been hacking our way around it, usually by using inline img
elements and absolutely positioning them behind the content. This is all well and good, but at certain browser widths, it breaks.
And it breaks horribly.
The problem with inline images
I found this out the hard way while designing and building the Belong site (which we launched yesterday, by the way). Consider this:

img
is doing fine as a faux background image, especially with the containing div’s overflow set to ‘hidden’.And then this:

It’s true that you could attempt to avoid this situation by creating really tall background images, but that not only means massive files; it means you’d always have to ensure your content never gets too long. Impossible for dynamic sites.
There’s another problem, too: our faux background image has no idea where its own horizontal centre is. On the original JPG, I’ve placed a horizontal line at 300px from the top and 300px from the bottom to indicate its centre, but that line will almost never be in the centre of the element because the img
displays from its top. Consider our example again:

At this point you might be wondering if this really matters, and for some images, it doesn’t. However, for Belong, this was a major problem: it meant that — at some widths — there was no way you’d actually see the t-shirt design:

You can play with the problem demo page in the browser. Be sure to resize the window.
The problem with background-size
So they’re the problems with using inline img
elements, but what about going for a CSS-only approach and using the experimental background-size
property, as suggested by commenters below? It offers a key advantage in being able to detect its own centre (using background-position:centre centre
) and does quite well in most scenarios. However, it still breaks when a containing element become very narrow.
The reason for this is that width:100%
is given priority. You could change it to height:100%
, but then it would break at wider sizes instead. You could give both width and height a value of 100%, but then you’d lose proportion as the image fails to maintain its correct ratio. And the really key thing here is that you’d need to know your preference in advance, which just won’t work for dynamic content: add more text and suddenly your container’s height could be way more important than its width.
To see this action, play with this additional problem demo page in the browser. Again, be sure to resize the window to see the points at which this approach fails.
Correction: as pointed out by Chris Mousdale in the comments below, background-size
does indeed work. Rather than using 100%
or auto
size values, be sure to use cover
instead; e.g.:
div.div01, div.div02, div.div03 {
-webkit-background-size:cover;
-moz-background-size:cover;
background-size:cover;
}
A cross-platform solution
So what we need is a way for the image to fill both its width and its height at all times, while retaining proportion; like background-size:cover
, but for all browsers. It shouldn’t matter if the container is wider than it is tall — or taller than it is wide — at any given point; it should be able to give priority to the appropriate dimension no matter how they’re changed by either the amount of content or the actual browser window size.
I was aware of a jQuery plugin called jQuery Backstretch, written by Scott Robbin, that does exactly this. It’s a fantastic script and I’d already used it on a client project, but the problem was that it only works on the body
. I asked the Decode boys (the chaps behind Belong) if they might be able to modify the code so that it could works its magic on any element.
And they did. Say hello to jQuery Anystretch.
As well as being able to apply Anystretch to any block-level element, an additional refinement is that you can use multiple instances of it:
$('.div01').anystretch("img01.jpg");
$('.div02').anystretch("img02.jpg");
$('.div03').anystretch("img03.jpg");
Further advantages of using Anystretch include being able to change the background image on click, set a fade-in speed, and turn the whole thing into a gallery with just a couple of lines of code. I won’t go into all that here; you can find jQuery Anystretch on GitHub.
To fully appreciate the power of the script — and everything I’ve bleated on about in this post — be sure to check out the third demo that illustrates the Anystretch solution, or, for the best working example, check out wearyoubelong.com, which includes the gallery functionality.
All credit for this fork goes to Dan Millar of Decode and of course to Scott Robbin for the original jQuery Backstretch.
To the inevitable nay-sayers complaining about the use of Javascript for presentational elements: there are more important things in life.