How To Solve Adaptive Images In Responsive Web Design

About The Author

Estelle Weyl is an internationally published author, speaker, trainer, and consultant. Estelle started her professional life running public health programs, but …

More about Estelle ↬

Email Newsletter

Weekly tips on front-end & UX.
Trusted by 200,000+ folks.

Some people waste bandwidth by sending high-resolution images to all devices. Others send regular-resolution images, looking less crisp on high-resolution displays. But what we really want to do is find the one solution that sends the image with the most appropriate size and resolution based on the browser and device making the request that can also be made accessible.

Adaptive images are the current hot topic in conversations about adaptive and responsive Web design. Why? Because no one likes any of the solutions thus far. New elements and attributes are being discussed as a solution for what is, for most of us, a big headache: to provide every user with one image optimized for their display size and resolution, without wasting time, memory or bandwidth with a client-side solution.

We have foreground and background images. We have large and small displays. We have regular and high-resolution displays. We have high-bandwidth and low-bandwidth connections. We have portrait and landscape orientations.

Some people waste bandwidth (and memory) by sending high-resolution images to all devices. Others send regular-resolution images to all devices, with the images looking less crisp on high-resolution displays.

Recommended reading: Responsive Images Done Right: A Guide To And srcset

What we really want to do is find the holy grail: the one solution that sends the image with the most appropriate size and resolution based on the browser and device making the request that can also be made accessible.

Clown Car Technique
Clown Car Technique.

The “clown car” technique is the closest thing we’ve got to a holy grail: leveraging well-supported media queries, the SVG format and the <object> element to serve responsive images with a single request. The solution isn’t perfect yet, but it’s getting close.

Background Images And Media Queries #

We’ve solved adaptive background images. Media queries make it simple to tailor the size and resolution of images to a device’s pixel ratio, viewport size and even screen orientation.

By using media queries with our background image styles, we can ensure that only the images that are needed are downloaded from the server. We can limit downloads to the assets that are most appropriate, saving bandwidth, memory and HTTP requests.

Recommended reading: Simple Responsive Images With CSS Background Images

Unfortunately, there has been no solution for foreground images — until now. The technology has been available for a long time. The clown car technique is just a new technique that leverages existing technology.

Proposed Solutions With New Technology #

New Elements And Attributes #

With inline or “content” images, getting the browser to download and display only the appropriate foreground image is a bit more difficult. Most people believe that there is no mechanism for the <img> tag to cause an image of the right size and resolution to be downloaded. To that end, polyfills have been created and services have been established.

The <picture> element — which leverages the semantics of the HTML5 <video> element, with its support of media queries to swap in different source files — was proposed:

<picture alt="responsive image">
     <source src="large.jpg" media="(min-width:1600px),
     (min-resolution: 136dpi) and (min-width:800px)">
     <source src="medium.jpg" media="(min-width:800px),
     (min-resolution: 136dpi) and (min-width:400px)">
     <source src="small.jpg">
  <!-- fallback -->
  <img src="small.jpg" alt="responsive image">
</picture>

Another method, using a srcset attribute on the <img> element, has also been proposed. The above <picture> element would be written as this:

<img
    alt="responsive image"
    src="small.jpg"
    srcset="large.jpg 1600w,
          large.jpg 800w 1.95x,
          medium.jpg 800w,
          medium.jpg 400w 1.95x">

Both solutions have benefits and drawbacks. Picking one is hard — but we don’t have to anymore. The two solutions have been joined into what’s called “Florian’s Compromise.” However, the traction isn’t quite there yet.

Google has proposed client hints as part of HTTP headers, to enable the right image to be served server-side.

SVG As An Out-of-the-Box Solution #

Many people don’t realize that we already have the technology to create and serve responsive images.

SVG has supported media queries for a long time, and browsers have supported SVG for… well, long enough, too. Most browsers support media queries in SVG (you can test your own browser). When it comes to responsive images, the only browsers in the mobile space that don’t support SVG are old versions of the Android browser (Android support for SVG began with Android 3.0).

We can leverage browser support for SVG and SVG support for both media queries and raster images to create responsive images, using media queries in SVG to serve up the right image.

My original experiment should theoretically work, and it does work in Internet Explorer (IE) 10 and Opera. When you mark up the HTML, you add a single call to an SVG file.

<img src="awesomefile.svg" alt="responsive image">

Now, isn’t that code simple?

SVGs support raster images included with the <image> element and with the CSS background-image property. In our responsive SVG, we would include all of the images that we might need to serve and then show only the appropriate image based on media queries.

Download A Single Raster Image #

My first attempt at SVG used <image> with media queries, hiding them with display: none.

While the SVG works perfectly in terms of responsiveness, it has several problems. Unfortunately, setting display: none on an <image> in SVG, similar to <img> in HTML, does not prevent the resource from being downloaded. If you open the <image> SVG in your browser, all four PNGs will be retrieved from the server, making four HTTP requests, wasting bandwidth and memory.

We know from CSS background images that downloading only the images that are needed is indeed possible. Similarly, to prevent the SVG from downloading all of the included images, we would use CSS background images instead of foreground images in our SVG file:

<svg xmlns="https://www.w3.org/2000/svg"
   viewBox="0 0 300 329" preserveAspectRatio="xMidYMid meet">

<title>Clown Car Technique</title>

<style>
svg {
  background-size: 100% 100%;
  background-repeat: no-repeat;
}

@media screen and (max-width: 400px) {
  svg {
    background-image: url(images/small.png");
  }
}

@media screen and (min-width: 401px) and (max-width: 700px) {
  svg {
    background-image: url(images/medium.png);
  }
}

@media screen and (min-width: 701px) and (max-width: 1000px) {
  svg {
    background-image: url(images/big.png);
  }
}

@media screen and (min-width: 1001px) {
  svg {
    background-image: url(images/huge.png);
  }
}
</style>
</svg>

The above can be included directly as an inline, or embedded with the<img> <strong>src</strong> attribute or<object> <strong>data</strong>attribute.

If you’re familiar with media queries and CSS, most of the code above should make sense. The clown car technique uses the same media queries that you would use elsewhere on your adaptive website.

To preserve the aspect ratio of the containing element and ensure that is scales uniformly, we include theviewboxandpreserveAspectRatioattributes.

The value of theviewboxattribute is a list of four space- or comma-separated numbers:min-x,min-y,widthandheight. By defining the width and height of our viewbox, we define the aspect ratio of the SVG image. The values we set for thepreserveAspectRatioattribute — 300 × 329 — preserve the aspect ratio defined inviewbox.

Issues with including the above include 1) Chrome and Safari not maintaining the aspect ratio when<svg>is includedinline: instead, defaulting the<svg>to 100% width and height. A bug has been submitted. 2) Webkit and Firefox not allowing the inclusion of raster images or scripts in SVGs embedded via the<img>element, and 3) No SVG support in IE <=8 and Android <=2.3.3.

When youopen the SVG file with just background imagesdefined, the raster image will take up the entire viewport. While the<image>version might look better as a standalone file because it is maintaining its aspect ratio and thebackground-imageversion is filling up the viewport, when you include the SVG as a separate document pulled into the HTML, the aspect ratio is preserved by default.  The background-size of containcover or 100% all work: choose the one that works best for your requirements.

The CSSbackground-imageproperty solves the HTTP request problem.Open the SVG file with just PNG background images(or theJPEG version ) and look at the “Network” tab in your developer tools, and you’ll see that the SVG has made only two HTTP requests, rather than five. If your monitor is large, then the browser would have downloaded a small SVG file (676 bytes) andhuge.pngorhuge.jpg.

Our first problem — that all of the different sizes of images are downloaded, even those that aren’t needed — has been resolved. Thisbackground-imageversion downloads only the image required, thereby addressing the concerns about multiple HTTP requests and wasted bandwidth.

The magic happens when we include SVG in a flexible layout. You’ll notice that the first time you resize the image, the browser might flicker white as it requests the next required PNG — because it doesn’t automatically download all assets. Rather, it downloads only the asset it needs. Simply declare either the width or the height of the container (<img>,<svg>or<object>) with CSS within your layout media queries, and the SVG will only pull in the single raster image it needs.

We still have the SVG file itself, which requires an HTTP request when not embedded inline with<svg>. We’ll solve that issue third.

Content Security Issues #

In Opera or in Windows 9 or 10, open theHTML file containing an SVG raster imagethat is linked to with the<img>tag. Note in the “Resources” panel of the developer tools that only one JPEG or PNG is being downloaded. Resize your browser. Note that the<img>is responsive. Additional JPEGs or PNGs (we could also have used GIF or WebP) are downloaded only when needed.

If you opened theHTML file containing an SVG raster imagein Firefox or WebKit, you would likely have seen no image. The SVG works in all modern browsers, but the<img>that calls in an SVG pulling in raster images works only in Opera and IE 9+. We’ll first cover how it works in IE and Opera, then we’ll cover the issues with WebKit and Firefox.

The code is simple:

<img src="awesomefile.svg" alt="responsive image">

When you include the SVG in your HTML<img>with a flexible width, such as 70% of the viewport, then as you grow and shrink the container by changing the window’s size or the CSS, the image will respond accordingly.

Thewidthmedia query in the SVG is based on the parent element in which the SVG is contained — the<img>, in this case — not the viewport’s width.

As the window grows and shrinks, the image displayed by the SVG changes. In the SVG file, the images are defined as being 100% of the height and width of the parent, which, in the case above, when we opened the SVG directly, was the viewport. Now, the container is the<img>element. Because we included theviewboxandpreserveAspectRatioattributes, as long as at least one length is defined, the SVG will grow or shrink to fit that length, maintaining the declared aspect ratio in the SVG, whatever the image’s size.

These foreground images work perfectly in Opera and IE 9+ (the versions found on mobile devices). In Chrome and Safari, if you open the SVG file first, thereby caching it, then the HTML file that contains the foreground SVG image might work as well.

While we saw earlier that the browser can indeed render the SVG, if the SVG is included in our document via the<img>tag, then this particular type of SVG will fail to render.

Why? To prevent cross-domain scripting attacks, some browsers have content security policies in place to keep SVG from importing media or scripts, in case they’re malicious in nature.

Blocking SVGs from importing scripts and images does make sense: To prevent cross-domain scripting attacks, you don’t want a file to pull potentially malicious content. So, SVG is supported, but in the case of WebKit and FireFox, it is just being prevented from pulling in external raster images. I’ve submitted aChrome bug reportto get the ban on importing raster images in SVG lifted.

In Firefox, the responsive SVG also works on its own. Firefox fully supports SVG. However, for security reasons, Firefox blocks the importing of external raster images, even if those images are on the same domain. The rationale is that allowing visitors to upload images and then displaying those images and scripts as part of an SVG constitutes a security risk. I would argue that if a website uses unsecured user-generated content, they’re already doing it wrong.

For right now, this simple line…

<img src="awesomefile.svg" alt="responsive image">

… is blocked in some browsers and, therefore, isn’t a viable solution.

All browsers support SVG media queries. They all support SVG as foreground or content images. They all support SVG as background images. The support just isn’t identical because of browser security policies.

All browsers do support the<object>tag. Without changing browser security policies,<img>alone won’t work yet. We can leverage the<object>tag.

With The<object>Tag #

The<object>element allows an external resource to be treated as an image. It can take care of the browser security drawbacks we see with<img>, disallowing the importing of images or scripts into an<img>file. The<object>element allows both.

The code isn’t that much more complex:

<object data="awesomefile.svg" type="image/svg+xml"></object>

By default, the<object>will be as wide as the parent element. However, as with images, we can declare a width or height with thewidthorheightattribute or with the CSSwidthorheightproperty. For the clown car technique to maintain the declared aspect ratio, simply declare just one length value.

Because of theviewboxandpreserveAspectRatiodeclarations in our SVG file, the<object>will by default maintain the declared aspect ratio. You can overwrite this with HTML or CSS attributes.

As noted earlier, the media queries in the SVG match the SVG’s container, not the viewport. The matched media queries in the SVG file will reflect the parent of the<object>tag, rather than the viewport.

If you look at anSVG being pulled in as the<object>data, you’ll see that it works in all browsers that support SVG.