Open book with bookmark
Issue № 328 Illustration by

Fluid Images

A note from the editors: We are pleased to present a portion of Chapter 3 of Responsive Web Design by Ethan Marcotte (A Book Apart, 2011). This chapter follows a previous chapter on fluid grids, expanding upon that grid with fluid images.

Things are looking good so far: we’ve got a grid-based layout, one that doesn’t sacrifice complexity for flexibility. I have to admit that the first time I figured out how to build a fluid grid, I was feeling pretty proud of myself.

Article Continues Below

But then, as often happens with web design, despair set in. Currently, our page is awash in words, and little else. Actually, nothing else: our page is nothing but text. Why is this a problem? Well, text reflows effortlessly within a flexible container—and I don’t know if you’ve noticed, but the Internet seems to have one or two of those “image” things lying around. None of which we’ve incorporated into our fluid grid.

So what happens when we introduce fixed-width images into our flexible design?

Fig 3.0: Our flexible grid is finally finished. not a pixel value in sight, and we didn’t have to skimp on the aesthetics.

Going back, back to markup, markup#section2

To find an answer, let’s do a quick experiment: let’s drop an image directly into our blog module, and see how our layout responds. The first thing we’ll need to do is to clear some space for it in our markup.

Remember our little blockquote, comfortably tucked into our blog article? Well, we’ve got way too much text on this darned page, so let’s replace it with an inset image:

<div class="figure">
  <p>
    <img src="robot.jpg" alt="" /> 
    <b class="figcaption">Lo, the robot walks</b>
  </p>
</div>

Nothing fancy: an img element, followed by a brief but descriptive caption wrapped in a b element. I’m actually appropriating the HTML5 figure/figcaption tags as class names in this snippet, which makes for a solidly semantic foundation.

(Sharp-eyed readers will note that I’m using a b element for a non-semantic hook. Now, some designers might use a span element instead. Me, I like the terseness of shorter tags like b or i for non-semantic markup.)

With that HTML finished, let’s drop in some basic CSS:

.figure {
  float: right;
  margin-bottom: 0.5em;
  margin-left: 2.53164557%;  /* 12px / 474px */
  width: 48.7341772%;  /* 231px / 474px */
}

Fig 3.1: An appropriately botty robot pic, courtesy of Jeremy Noble.

We’re creating a nice inset effect for our figure. It’ll be floated to the right, and will span roughly half the width of our article, or four columns of our flexible grid. Markup: check; style: check. Of course, all this HTML and CSS is for naught if there isn’t an actual image available.

Now, because I love you (and robots) dearly, not just any image will do. And after scouring the web for whole minutes, I found a fantastically imposing robo-portrait (fig 3.1). The beautiful thing about this image (aside from the robot, of course) is that it’s huge. I’ve cropped it slightly, but I haven’t scaled it down at all, leaving it at its native resolution of 655—655. This image is much larger than we know its flexible container will be, making it a perfect case to test how robust our flexible layout is.

So let’s drop our oversized image onto the server, reload the page, and—oh. Well. That’s pretty much the worst thing on the internet.

Fig 3.2: Our huge image is huge. Our broken layout is broken.

Actually, the result isn’t that surprising. Our layout isn’t broken per se—our flexible container is working just fine, and the proportions of our grid’s columns remain intact. But because our image is much wider than its containing .figure, the excess content simply overflows its container, and is visible to the user. There simply aren’t any constraints applied to our image that could make it aware of its flexible environment.

Fluid images#section3

But what if we could introduce such a constraint? What if we could write a rule that prevents images from exceeding the width of their container?

Well, here’s the good news: that’s very easy to do:

img {
  max-width: 100%;
}

First discovered by designer Richard Rutter, this one rule immediately provides an incredibly handy constraint for every image in our document. Now, our img element will render at whatever size it wants, as long as it’s narrower than its containing element. But if it happens to be wider than its container, then the max-width: 100% directive forces the image’s width to match the width of its container. And as you can see, our image has snapped into place.

Fig 3.3: Just by including max-width: 100%, we’ve prevented our image from escaping its flexible container. On a related note, I love max-width: 100%.

What’s more, modern browsers have evolved to the point where they resize the images proportionally: as our flexible container resizes itself, shrinking or enlarging our image, the image’s aspect ratio remains intact.

Fig 3.4: Regardless of how wide or small its flexible container becomes, the image resizes proportionally. Magic? Who can say.

I hope you’re not tired of all this good news because as it happens, the max-width: 100% rule can also apply to most fixed-width elements, like video and other rich media. In fact, we can beef up our selector to cover other media-ready elements, like so:

img,
embed,
object,
video {
  max-width: 100%;
}

Whether it’s a cute little Flash video, some other embedded media, or a humble img, browsers do a fair job of resizing the content proportionally in a flexible layout. All thanks to our lightweight max-width constraint.

Fig 3.5: Other media play nicely with max-width: 100%, becoming flexible themselves. Did I mention I love max-width: 100%?

So we’ve cracked the problem of flexible images and media—right? One CSS rule and we’re done?

Because this job is never easy#section4

Time to let the healing begin: we need to work through the pain, the tears, the rending of garments, and talk about a few browser-specific issues around flexible images.

max-width in Internet Explorer#section5

The cold, hard truth is that Internet Explorer 6 and below don’t support the max-width property. IE7 version and above? Oh, it is positively brimming with support for max-width. But if you’re stuck supporting the (cough) venerable IE6 or lower, our approach needs refinement.

Now, there are several documented ways to get max-width support working in IE6. Most are JavaScript-driven, usually relying on Microsoft’s proprietary expression filter to dynamically evaluate the width of an element, and to manually resize it if it exceeds a certain threshold. For an example of these decidedly non-standard workarounds, I’d recommend Svend Tofte’s classic blog entry on the subject.

Me? I tend to favor a more lo-fi, CSS-driven approach. Namely, all modern browsers get our max-width constraint:

img,
embed,
object,
video {
  max-width: 100%;
}

But in a separate IE6-specific stylesheet, I’ll include the following:

img,
embed,
object,
video {
 width: 100%;
}

See the difference? IE6 and lower get width: 100%, rather than the max-width: 100% rule.

A word of warning: tread carefully here, for these are drastically different rules. Whereas max-width: 100% instructs our images to never exceed the width of their containers, width: 100% forces our images to always match the width of their containing elements.

Most of the time, this approach will work just fine. For example, it’s safe to assume that our oversized robot.jpg image will always be larger than its containing element, so the width: 100% rule works beautifully.

But for smaller images like thumbnails, or most embedded movies, it might not be appropriate to blindly up-scale them with CSS. If that’s the case, then a bit more specificity might be warranted for IE:

img.full,
object.full,
.main img,
.main object {
  width: 100%;
}

If you don’t want the width: 100% rule to apply to every piece of fixed-width media in your page, we can simply write a list of selectors that target certain kinds of images or video (img.full), or certain areas of your document where you know you’ll be dealing with oversized media (.main img, .main object). Think of this like a whitelist: if images or other media appear on this list, then they’ll be flexible; otherwise, they’ll be fixed in their stodgy old pixel-y ways.

So if you’re still supporting legacy versions of Internet Explorer, a carefully applied width: 100% rule can get those flexible images working beautifully. But with that bug sorted, we’ve still got one to go.

And boy, it’s a doozy.

In which it becomes clear that Windows hates us#section6

Fig 3.6: Seen here in IE6, our robot image has developed some unsightly artifacts. Guess Windows doesn’t much care for our flexible images.

If you look at our blog module with certain Windows-based browsers, our robot.jpg has gone from looking imposing to looking, well, broken). But this isn’t a browser-specific issue as much as a platform-specific one: Windows doesn’t scale images that well. In fact, when they’re resized via CSS, images quickly develop artifacts on Windows, dramatically impacting their quality. And not in a good way.

For a quick test case, I’ve tossed a text-heavy graphic into a flexible container, and then resized our image with the max-width: 100% fix, while IE6 and below receive the width: 100% workaround. Now, you’d never actually put this amount of text in an image. But it perfectly illustrates just how badly things can get in IE7 or lower. As you can see, the image looks—if you’ll pardon the technical term—downright nasty.

But before you give up on the promise of scaleable, flexible images, it’s worth noting that this bug doesn’t affect every Windows-based browser. In fact, only Internet Explorer 7 and lower are affected, as is Firefox 2 and lower on Windows. More modern browsers like Safari, Firefox 3+, and IE8+ don’t exhibit a single problem with flexible images. What’s more, the bug seems to have been fixed in Windows 7, so that’s more good news.

Fig 3.7: In certain Windows-based browsers, the image quickly develops too many artifacts to be readable.

So with the scope of the problem defined, surely there’s a patch we can apply? Thankfully, there is—with the exception of Firefox 2.

Now, this grizzled old browser was released in 2006, so I think it’s safe to assume it isn’t exactly clogging up your site’s traffic logs. At any rate, a patch for Firefox 2 would require some fairly involved browser-sniffing to target specific versions on a specific platform—and browser-sniffing is unreliable at best. But even if we did want to perform that kind of detection, these older versions of Firefox don’t have a switch that could fix our busted-looking images.

Internet Explorer, however, does have such a toggle. (Pardon me whilst I swallow my pride for this next section title.)

Hail AlphaImageLoader, the conquering hero#section7

Ever tried to get transparent PNGs working in IE6 and below? Chances are good you’ve encountered AlphaImageLoader, one of Microsoft’s proprietary CSS filters. There have since been more robust patches created for IE’s lack of support for the PNG alpha channel (Drew Diller’s DD_belatedPNG library is a current favorite of mine), but historically, if you had a PNG attached to an element’s background, you could drop the following rule into an IE-specific stylesheet: (Line wraps marked » —Ed.)

.logo {
  background: none;
  filter: progid:DXImageTransform.Microsoft.AlphaImageLoader»
  (src="/path/to/logo.png", sizingMethod="scale");
}

This AlphaImageLoader patch does a few things. First, it removes the background image from the element, then inserts it into an AlphaImageLoader object that sits “between” the proper background layer and the element’s content. But the sizingMethod property is the clever bit, dictating whether the AlphaImageLoader object should crop any parts of the image that overflow its container, treat it like a regular image, or scale it to fit it within its containing element.

I can hear you stifling your yawns by now: after all, what does an IE-specific PNG fix have to do with our broken image rendering?

Quite a bit, as it turns out. At one point I discovered that applying AlphaImageLoader to an image dramatically improves its rendering quality in IE, bringing it up to par with, well, every other browser on the planet. Furthermore, by setting the sizingMethod property to scale, we can use our AlphaImageLoader object to create the illusion of a flexible image.

So I whipped up some JavaScript to automate that process. Simply download the script and include it on any page with flexible images; it will scour your document to create a series of flexible, high-quality AlphaImageLoader objects.

And with that fix applied, the difference in our rendered images is noticeable: in our example we’ve gone from an impossibly distorted image to an immaculately rendered one. And it works wonderfully in a flexible context.

Fig 3.8: Our image is now perfectly legible, and resizing wonderfully. A dab of AlphaImageLoader’ll do ya.

(It’s worth mentioning that many of Microsoft’s proprietary filters, and AlphaImageLoader in particular, have some performance overhead associated with them, Stoyan Stefanov covers the pitfalls in more detail on the YUI blog. What does this mean for you? Just be sure to test the fix thoroughly on your site, gauge its effect on your users, and evaluate whether or not the improved rendering is worth the performance tradeoff.)

With the max-width: 100% fix in place (and aided by our width: 100% and AlphaImageLoader patches), our inset image is resizing beautifully across our target browsers. No matter the size of the browser window, our image scales harmoniously along with the proportions of our flexible grid.

But what about images that aren’t actually in our markup?

Flexibly tiled background images#section8

Let’s say our dearly esteemed designer sends over a revised mockup of our blog module. Notice anything different about it?

Fig 3.9: Our blog’s sidebar is now sporting a background graphic. Hot.

Up until now, our blog’s content has been sitting on a rather unassuming near-white background. But now the design has been modified slightly, adding a two-toned background to the blog entry to provide more contrast between the left- and right-hand columns. What’s more, there’s actually a subtle level of noise added to the background, adding an extra level of texture to our design:

Fig 3.10: A detailed look at our new background treatment.

So: how do we actually add this new background image to our template?

Back in 2004, Dan Cederholm wrote a brilliant article showing how a vertically repeating background graphic could be used to create a “faux column” effect. The technique’s genius is in its simplicity: by tiling a colored background graphic vertically behind our content, we can create the illusion of equal height columns.

In Dan’s original technique, the background graphic was simply centered at the top of the content area and then tiled vertically, like so:

.blog {
  background: #F8F5F2 url("blog-bg.png") repeat-y 50% 0;
}

And that technique works beautifully. But Dan’s technique assumes that your design is a fixed width, creating a graphic that matches the width of your design. Then how, pray, are we supposed to work in a background image that tiles over two flexible columns?

Thanks to some early research by designer Doug Bowman, we can still apply the faux column technique. It just requires a little bit of extra planning, as well as a dash of your favorite formula, target ÷ context = result.

First, we’ll begin by taking a look at our mockup, to find the transition point in our background graphic, the exact pixel at which our white column transitions into the gray. And from the look of things, that switch happens at the 568 pixel mark.

Fig 3.11: Our white column switches over to gray at the 568px mark. That’s our transition point.

Armed with that information, we can now adapt the “faux columns” approach to our fluid grid. First, we’ll convert that transition point into a percentage-based value relative to our blog module’s width. And to do so, our target · context = result formula comes into play yet again. We have our target value of 568px, and the width of the design—our context—is 900px. And if we plug those two values into our stalwart formula:

568 · 900 = 0.631111111111111

That’s right: another impossibly long number, which converts to a percentage of 63.1111111111111%.

Keep that percentage in the back of your mind for a moment. Now, let’s open up your favorite image editor, and create a foolishly wide document—say, one that’s 3000 pixels across. And since we’re going to tile this image vertically, its height is only 160px tall.

Fig 3.12: A monstrously large canvas that we’ll (shortly) turn into our background graphic.

In a moment, we’re going to turn this blank document into our background graphic. But why is it so large? Well, this image needs to be larger than we can reasonably assume the browser window will ever be. And unless you’re reading this from the 25th century on your wall-sized display made of, I don’t know, holograms or whatever, I’m assuming your monitor’s not quite that wide.

To create the columns themselves, we’ll need to apply the transition point percentage (63.1111111111111%) to our new, wider canvas. So if we’re working with a graphic that’s 3000px across, we simply need to multiply that width by the percentage, like so:

3000 x 0.631111111111111 = 1893.333333333333

We’re left with 1893.333333333333 as our result. And since Photoshop doesn’t deal in anything less than whole pixels, let’s round that down to 1893 pixels. Armed with that number, we’ll recreate our textures in our blank image, switching from white to gray at the 1893rd pixel.

Fig 3.13: We’ve applied that percentage to our oh-so-wide background graphic, creating our tile-ready columns.

How does that help us? Well, what we’ve just done is to proportionally scale our transition point up to this new, wider canvas. So we can take that new pixel value, and use it to create our columns: the white column will be 1893px wide, with the gray column filling up the remainder.

So now there’s only one thing left to do: drop our newly minted graphic into our stylesheet.

.blog {
  background: #F8F5F2 url("blog-bg.png") repeat-y 63.1111111111111% 0;  
  /* 568px / 900px */
}

As in Dan’s original technique, we’re still positioning the graphic at the very top of our blog, and then repeating it vertically down the width of the module (repeat-y). But the ­background-position value reuses our transition point percentage (63.1111111111111% 0), keeping the columns firmly in place as our design resizes itself.

And with that, we’ve got faux columns working beautifully in a fluid layout. All thanks to Dan Cederholm’s original approach, augmented with a little proportional thinking.

fig 3.14: Our flexibly faux columns.

Fully flexible background images?#section9

Of course, our flexible faux column isn’t really flexible: we’re simply using percentages to position a background image in such a way that the columns appear to resize with their container. The image’s dimensions haven’t changed at all.

But what about a background image that actually does need to resize with the layout? Perhaps you’ve placed a logo on an h1 element’s background, or used sprites to create rollovers for your site’s navigation. Can we resize images that need to live in the background?

Well, sort of. There is a CSS3 property called background-size, which would allow us to create truly flexible background images, but—you guessed it—browser support is still pretty immature.

In the interim, there are some rather ingenious JavaScript-based solutions out there: for example, Scott Robbin’s jQuery Backstretch plugin simulates resizable background images on the body element. And as you’ll see in the next chapter, CSS3 media queries could also be used to apply different background images tailored to different resolution ranges. So while background-size might not be available yet, the sky is, as the kids say, the limit.

27 Reader Comments

  1. … brilliant!

    It seems I have to really get back learning the trade again. Thanks for this wonderful chapter.

    Must now get hold of the book.

  2. I dealt with a similar issue recently.

    Thought I should mention for IE7 and IE8 you can use the CSS property

    -ms-interpolation-mode:bicubic;

    Gives much nicer scaling of images for IE. Used jQuery to cope with IE6’s lack of max-width.

    Did not consider AlphaImageLoader, will have a look at this for making IE6 look better too.

  3. Luckily, I don’t work on projects that require IE6 compatibility any longer… From the YUI discussion it looks like the page takes a lot more ram to render… like 700% more ram.

    In that case, I would just let them have poo-poo image resizing and put a message to upgrade.

  4. I’m starting to develop a crush on code (blush).
    More awesome articles like this and I might (gulp) love to code.

  5. Firefox and Opera seem to ignore the max-width: 100% if there’s a right margin and a right sidebar with a negative left margin, yet webkit browsers respect the max-width: 100% even with a right sidebar. width: 100% fixes the problem, but increases the actual size of the image. Why doesn’t max-width: 100% work here? Is there a workaround for this?

  6. On the faux column technique, why not just position bg at right?
    background: url(…) top right repeat-y;

  7. If you want to avoid having that super-wide image from loading in modern browsers, you can replace it with a generated version, via the CSS3 ‘linear-gradient’ function. That would look like this (rounding the percentage because it’s easier to read):

    background: #F8F5F2 url(“blog-bg.png”) repeat-y 63.1% 0;
    background: -moz-linear-gradient(left, #ffffff 63.1%, #cccccc 63.1%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, right top, color-stop(63.1%,#ffffff), color-stop(63.1%,#cccccc)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(left, #ffffff 63.1%,#cccccc 63.1%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(left, #ffffff 63.1%,#cccccc 63.1%); /* Opera11.10+ */
    background: -ms-linear-gradient(left, #ffffff 63.1%,#cccccc 63.1%); /* IE10+ */
    background: linear-gradient(left, #ffffff 63.1%,#cccccc 63.1%); /* W3C !!DRAFT!! */

  8. @oliveiraev: Because then you would always have a sidebar color that was 1107px wide. The author’s technique is not actually sizing the background image, it is just positioning it. So the image is still 300px wide, but 631111111111111% across the image will align with 631111111111111% across the background. That’s how percentages work in ‘background-position’.

  9. Sorry, I left out a decimal. That should have read “63.1111111111111% across the image will align with 63.1111111111111% across the background”.

  10. your sample is sure very nice coded, but what about the total page-weight? we all know that a important issue is the size and optimisation of images, and with this (extreme) difference in “displayed” image-size and “downloaded” image-size w’re wasting band-width, wich is allso time and therefor money.
    I realise that in your sample the difference is huge, but millions of images a day is what’s happening in my daily work, and I cannot permit myself to much of a band-width-waste.

  11. This seems like a great book and a very good read. I will have to look into this book. Isn’t it wonderful how a few little tweaks can completely clean up an image and make it look so much better. Images capture an audience and can make a website either good or bad. Little things like this can help do the trick. Thanks Tesmond for the comment I will definitly look into -ms-interpolation-mode:bicubic; so that IE images are smoother scaling.

  12. For some reason, perhaps because my images are inside link tags, the main browsers don’t keep the aspect ratio constant when resizing them: the height stays constant and the images distort.

    Using both width: 100% and height: 100% works to fix the aspect ratio in most browsers nicely, but sadly WebKit-based ones take the 100% to be the height of the browser window. This results in very tall narrow images… 🙁

  13. Why would you apply one background to what should be two divs (I’m talking about the blog example). The technique is interesting but I don’t really see the point. Each div should have it’s own background image.

  14. I really love responsible designs and have worked intensely on it for the last year or two. What is often forgotten is bandwidth and that perceived speed is very important for the overall experience of a website. Browser resizing with CSS is a necessity, but should in my opinion be considered a last resort.

    It’s quite interesting to work with context sensitive javascript queries changing the requested image url to a “thumbnail” version so that less bandwidth is used by the client.

  15. Of course, if you weren’t using an image to do the job of text, you wouldn’t have to worry about it looking like an arty fax (that is what you said, right?) and having to mess around with alphaimageloader. And if it gets resized too much, it will be too small to be legible, no matter what the quality of reproduction.

    Remember, images are pictures, not text. And while pictures might look slightly scrappy if resized in IE6, they will not be so broken that they’re unusable. When that happens, it’s usually a clue that you shouldn’t have been using an image there in the first place.

  16. I’m designing a fluid layout right now and this really helps a lot. The background faux-column trick seems a bit strange though. I wonder if there is a better way around this.

  17. Ok, now I think I have to get the book. It was so inspiring to see the alternatives to the standard ways we use in our everyday work. It’s time to get fluid …

  18. Thought you might find useful:

    The problems that occur in ie. when rendering images at sizes other than their native size can be fixed in by adjusting the image interpolation method to ‘bicubic’ by using the following css:

    img { -ms-interpolation-mode: bicubic; }

    For more info see:
    http://css-tricks.com/2398-ie-fix-bicubic-scaling-for-images/

    Hope this helps

    ….have fun

    p.s. great article

  19. Thanks for the great article! I can’t tell you how many times I’ve had to pair down an image in photoshop to get it to an appropriate dimension. It makes so much more sense for the applications out there today as well as the varying degree of browsers to approach image sizing like this.

    Allowing for changes in the proportions of screens as well as mobile devices’ ability to resize using fluid images is just plain smart. It certainly puts perspective on the way the web is developing not as a static medium but a highly interactive and dynamic tool.

    Sometimes it is confounding how such a simple principle is finally introduced to HTML and CSS and makes our lives so much easier. Not only do fluid images allow for a skip in the step between image editing software and the web, but creates a contextual image that will adapt to the screen. This aspect is so crucial for a time when the screen isn’t limited by certain dimensions. Also, just simply in the act of designing, the ability to easily resize the image in divs based on percentages is so helpful to navigate through the design on a more immediate and visual level.

  20. Fluid images go hand in hand with responsive design, with multiple widths per site depending upon the resolution of the screen and width of the browser window. We have recently had a lot of interest is sites with this responsive design.

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career