Elliot Jay Stocks is a designer, speaker, and author. He is the Creative Director of Adobe Typekit, the founder of typography magazine 8 Faces, one half of Viewport Industries, and an electronic musician.

Better background images for responsive web design

Posted on 15 February 2012 39 comments

Article illustration for 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:

Screenshot
Our absolutely-positioned inline img is doing fine as a faux background image, especially with the containing div’s overflow set to ‘hidden’.

And then this:

Screenshot
But look what happens when the browser window gets smaller: the image remains at 100% width and, in doing so, reveals the containing div’s background colour.

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:

Screenshot
The horizontal centre of the actual image — indicated by a white line on the original JPG — bears no relation to the horizontal centre of the containing element.

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:

Screenshot
Poor ol’ Trent: completely cut off at wider browser widths

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.

39 comments

  1. Prisca

    Prisca

    15 February 2012 @ 05:46PM #

    Thanks so much for this, Elliot! And of course, Dan Millar of Decode :)
    This has been an ongoing headache, and here’s a possible solution to rid me of those, yay! – will try this out ASAP, thanks again.

  2. Tuhin Kumar

    Tuhin Kumar

    15 February 2012 @ 05:51PM #

    This looks amazing. I recently needed to do this for my site as a “cover” image and simply chose to use background-size:cover and go for a really wide background image otherwise too.

    This however makes it possible to make the design “compatible” even in older browsers.

    Also I am assuming, that for the image to be aware of it’s horizontal (across width) centre is easy too.

  3. val

    val

    15 February 2012 @ 05:50PM #

    Thanks for sharing this, guys! And yes, there are way more important things in life :) When it comes to designing responsively you don’t really have much choice sometimes.

  4. Pavel Ciorici

    Pavel Ciorici

    15 February 2012 @ 06:16PM #

    What a great solution! Thanks a lot for sharing it with us. I’m going to implement it in a slider from a WordPress theme I released recently – PhotoNote (see the problem in 3rd slide)

  5. Elke Hinze

    Elke Hinze

    15 February 2012 @ 06:30PM #

    Thank you for this post! I am going to be implementing a design soon that uses a large background. This should work perfectly.

  6. wooorm

    wooorm

    15 February 2012 @ 06:46PM #

    Why not use pure CSS? (background-size: cover;background-position: 50% 50%;) — http://jsfiddle.net/r3UeK/1/

  7. Daniel Smith

    Daniel Smith

    15 February 2012 @ 06:48PM #

    Thank you for posting this, I have been struggling with background fill on my page on all browsers for ages and this is just the thing I need! Hope to use it correctly asap!
    Thank you once again.

  8. Simon Carr

    Simon Carr

    15 February 2012 @ 06:52PM #

    I’ve been dealing with this exact same issue when I was building my website. I used absolute positioning and z-index to layer images. However, this didn’t cut it for full-screen background images (since not all browser support percentage on backgrounds). So – thanks for sharing your technique!

  9. Tim Wright

    Tim Wright

    15 February 2012 @ 06:53PM #

    This stuff is good if you’re trying to make media queries function in IE8 & below. Other than that, it looks like support for media queries and background size are the same. (MQ: http://caniuse.com/#feat=css-mediaqueries) (BACKSIZE: http://caniuse.com/#feat=background-img-opts)

  10. Ian Oliver

    Ian Oliver

    15 February 2012 @ 07:08PM #

    Gotta say, I’m with some of the other commenters here: background-size seems the much better way to go.Though of course, the JavaScript solution brings the feature to older browsers.

    I’d recommend serving background-size in your normal CSS to all browsers, then use Modernizr to detect “.no-backgrondsize”, then offer this ‘polyfill’ method to those less capable browsers.

  11. Daniel Mui

    Daniel Mui

    15 February 2012 @ 07:15PM #

    I could kiss you right now! Been working on a project of mine where that has been a massive problem. Voila! Problem fixed! Thanks Elliott and Dan

  12. Windo

    Windo

    15 February 2012 @ 07:42PM #

    Thank U, going to try this in current project., love the last paragraph! Lolz

  13. Elliot Jay Stocks

    Elliot Jay Stocks

    15 February 2012 @ 10:20PM #

    @ Ian Oliver: Yep, background-size is definitely preferable, but as browser support is limited, this is a stop-gap fix. Good call on the Modernizr front — that would definitely be the way to go.

  14. Ned Nikolic

    Ned Nikolic

    16 February 2012 @ 02:24AM #

    Hey Elliot,

    I just launched my first version of my portfolio website (http://nednikolic.com/) and I was experiencing the same problem with the front Apple Display slideshow. Unfortunately I just found out about this article which mean’s I’ll have to go back into the code and implement this solution, even though my ‘hackish’ slideshow still works perfectly on all devices.

    Thanks again for the article mate!

  15. Safe As Milk

    Safe As Milk

    16 February 2012 @ 08:44AM #

    Great stuff. Does this only work on background images? Could one, for example, combine it with something like Cycle to make a true 100% wide slider? Rather than use hugely wide images like this http://www.nikebetterworld.com/

  16. Kimberley

    Kimberley

    16 February 2012 @ 12:22PM #

    I know this web site offers quality depending articles or reviews and additional stuff, is there any other web page which presents these information in quality?

  17. Dan Millar

    Dan Millar

    16 February 2012 @ 12:41PM #

    @ Safe As Milk: It could definitely easily be turned into a slideshow. When I get a moment I’ll add a demo into GitHub of using it as a slideshow.

  18. Chris Mousdale

    Chris Mousdale

    16 February 2012 @ 12:47PM #

    I’m not sure if i’m missing something but changing your background-size from ‘100% auto’ to ‘cover’ does what you are trying to achieve – try it on problem2.html and it works. This plug in is a great fix for IE though?

  19. Elliot Jay Stocks

    Elliot Jay Stocks

    16 February 2012 @ 01:01PM #

    @ Chris Mousdale: You are indeed correct! I’ve updated the post to reflect your point!

  20. Andrew Wolfe

    Andrew Wolfe

    16 February 2012 @ 01:58PM #

    Nice!
    I recently did that same tweak to backstretch. Worked like a charm and I haven’t looked back since.

  21. Bilal Ina

    Bilal Ina

    16 February 2012 @ 04:55PM #

    Hai Elliot, can you teach us about web design process? not only tools.

    I need to get knowledge from you about building responsive web design from scratch, with the right way from the master :D haha.
    For case study, maybe how you to redesign Smashingmag.
    Thank you..

  22. Ivo Silva

    Ivo Silva

    16 February 2012 @ 09:13PM #

    I’ve come across this horizontal centering issue recently and it’s really a pain. Next CSS4 update should come out with a crosshair atribute like photoshop for an element which would act as the focal point for images (mainly vertically) or other atributes, such as the transform: rotate. this could be given with a defined value (in pixels) or depending on the window size (percentage).

    It could look something like this:

    img{
    background-image: url(‘something.png’);
    crosshair-bottom: 30%;
    crosshair-right: 20%;
    }

    This would also let you position backgrounds from the right and bottom of it’s parent’s width.

  23. Ivo Silva

    Ivo Silva

    16 February 2012 @ 09:16PM #

    Edit:
    This would also let you position backgrounds from the right and bottom relative to it’s parent’s SIZE, making it act like a mask and keeping the responsive characteristics.

    Ps: Sorry for the double post.

  24. Russell Bishop

    Russell Bishop

    17 February 2012 @ 02:42PM #

    In your original ‘problem’ example, would it not be possible to use an inline image with a top: 0; bottom: 0; – the image is landscape, so you would never have the problem of a background showing. You could then use a max-height to ensure better results when the image needs to be wider.

  25. Winifred

    Winifred

    17 February 2012 @ 08:42PM #

    Highly descriptive article, I enjoyed that bit. Will there be a part 2?

  26. Erik Kraft

    Erik Kraft

    18 February 2012 @ 12:59AM #

    Thanks for this fantastic adaptation of Backstretch—I can think of many places where it will be handy. I am running into a couple of implementation issues and was wondering if anyone might have any ideas where I’m going wrong. First, this page:

    http://bee4.restlessbeedev.com/index2.html

    Anystretch is working at very wide screen widths (like, > 1150), but as the screen gets narrower, the background image isn’t resizing like it should. If I take the width and margins off the large paragraph, the problem goes away.

    Second, this page:

    http://bee4.restlessbeedev.com/about.html

    I’m attempting to use Anystretch on a div with no content. I’m doing so in kind of an ugly way—with a transparent gif. Ugly though it may be, it’s working a treat on my desktop. When I look at it on my iPad, though, it’s broken.

    Any suggestions for either of these issues would be massively appreciated. Thanks again for coming up with this plugin, and sorry if this isn’t the right place to report problems—I didn’t notice an issues section on github.

  27. Erik Kraft

    Erik Kraft

    18 February 2012 @ 02:44PM #

    Ah-ha—I figured out what was going wrong with the homepage. I had a global

    img {max-width:100%;}

    rule, which was screwing up the Anystretch img div. Once I overrode this with a max-width: none rule for the Anystretch container, things started working properly.

    Still curious about the possibility of applying Anystrech to a div with no content…

  28. Gerry

    Gerry

    19 February 2012 @ 09:07PM #

    I prefer a javascript method over css because I can direct the placement of the images much better. In one of my projects, I wanted an image to be noted as left or right side, so when panning across two ‘pages’ they would be seamless. I could also lock it to the top or bottom by including a specific class that the script uses to keep it there.

    Thanks for updating some of the logistics behind Backstretch. Anystretch (and your article that describes the concept behind it) are great.

  29. safe as milk

    safe as milk

    20 February 2012 @ 11:02AM #

    @ Dan Millar

    did you get round to having a look at making this a slider?

    be fascinated to see if it works

    next step would be to make it ‘swipeable’ for tablets etc – I don’t want much do I?

  30. Web Designer

    Web Designer

    20 February 2012 @ 01:08PM #

    Its nice description about problem with Inline Images and problem with Background-Size and Cross-Plateform solution its help me to get right information
    Thank you

  31. Jorge Guerrero

    Jorge Guerrero

    21 February 2012 @ 12:30PM #

    Wow, interesting solution to old browsers. Thanks Elliot!

  32. Anthony Grace

    Anthony Grace

    21 February 2012 @ 10:47PM #

    At what stage of the process does this article apply? What if, like most sane people, we are creating mobile first and tablet/desktop second, as a separate project? In that case do you recommend some limited form of image “stretching”?

    From an RWD perspective, could you somehow highlight where this applies in a real-life development cycle? :)

  33. Russell

    Russell

    22 February 2012 @ 09:48AM #

    Appreciate the recommendation. Let me try it out.

  34. Web Design Harrisburg

    Web Design Harrisburg

    20 February 2012 @ 09:05PM #

    Great tips here. I think this is really going to take off this year.

  35. Dominik

    Dominik

    24 February 2012 @ 12:08PM #

    Works pretty well. Aside from that, if you have JavaScript turned off, there are no images at all. Personally I don’t like that, but I think it’s still applicable.

  36. Joe

    Joe

    24 February 2012 @ 05:20PM #

    Responsive web design is still just a hack job like browser sniffing in the old days

  37. Kurt

    Kurt

    26 February 2012 @ 03:37PM #

    I like this. I’ll probably end up using modernizr.js to detect the presence of the background-size support and then use yepnope.js(?) to determine if anystretch gets called. That way, the best solution is getting delivered to each browser.

    IMG is content and background-image is layout, so you just need to be sure the intention of the image matches the semantic you’re going for.

© 2005 – 2014 Elliot Jay Stocks. All rights reserved. Powered by Harmony and tracked by Gaug.es. To keep updated with new content, you might like to subscribe to my RSS feed.