Responsive background images with fixed or fluid aspect ratios

What's the easiest way to scale background images in responsive layouts? We use an old technique and enhance it to fluidly change the aspect ratio of background images.

Responsive layouts make it possible to dynamically scale the width of a website to fit on small mobile devices as well as larger desktop computers. An <img> element with a percentual width will have its height automatically adjusted. Its aspect ratio remains the same when it is resized.

If we want to accomplish the same with background images we must figure out how to maintain the aspect ratio of any HTML element.

Fixed aspect ratio

The trick we will use to maintain the aspect ratio is to apply vertical padding. We set the padding as a percentage based on the width of the element. This technique is originally described for embedded videos in an older A List Apart article but it works just as well for any element.

Suppose we want to have an image of 800 by 450 pixels. We want to create an element that maintains the aspect ratio of 16:9 when its width changes. Below is a code example. We use px in this example but it works equally well with em units. We also use the HTML5 figure element. Be sure to use a HTML5 shiv or a similar solution to make it work in older browsers.

<div class="column">
  <figure class="fixedratio"></figure>
</div>
div.column {
  max-width: 800px;
}

figure.fixedratio {
  padding-top: 56.25%;  /* 450px/800px = 0.5625 */
}

Try it with your browser.

Adding a background image

The element scales and maintains its aspect ratio. But if we add a background image it will not automatically scale together with the element. To accomplish this we add background-size: cover.

A drawback of scaling background images to cover the entire element is that it is not supported in Internet Explorer 8 and below. To get an acceptable result in these versions we also center the image with a background-position attribute. We must ensure that the image is at least as wide as the max-width of the element. That way the element will never be larger than the image. If the element is smaller than the image it will be cropped. Because these versions of Internet Explorer are only used on desktops this won't be noticed by many people.

<div class="column">
  <figure class="fixedratio"></figure>
</div>
div.column {
  /* The background image must be 800px wide */
  max-width: 800px;
}

figure.fixedratio {
  padding-top: 56.25%;  /* 450px/800px = 0.5625 */

  background-image: url(http://voormedia.com/examples/north-sea-regatta.jpg);
  background-size: cover;
  -moz-background-size: cover;  /* Firefox 3.6 */
  background-position: center;  /* Internet Explorer 7/8 */
}

Try it with your browser.

Fluid aspect ratio

We can take this a step further. Suppose we have a widescreen image that looks great on a desktop computer. On a mobile device we don't want to use the same aspect ratio or the image will become too small. We also don't want to use the exact same height or the image becomes too tall. Instead the height should decrease gradually when the width is reduced. We call this a fluid aspect ratio.

The effect can be accomplished by decreasing the percentual padding and setting a height on the element. Suppose the large image is 800 by 200 pixels and we decide the image should be only 150 pixels high when the width is decreased to 300 pixels. Now we need to calculate the height and padding-top attributes.

The diagram above shows the relationship between the two dimensions. The slope of the line corresponds to the padding-top attribute. The start height corresponds to the height attribute. It represents the height that the element would have if its width were zero.

We can implement a fluid aspect ratio by adjusting the example with these calculated values.

<div class="column">
  <figure class="fluidratio"></figure>
</div>
div.column {
  /* The background image must be 800px wide */
  max-width: 800px;
}

figure.fluidratio {
  padding-top: 10%;  /* slope */
  height: 120px;  /* start height */

  background-image: url(http://voormedia.com/examples/amsterdam.jpg);
  background-size: cover;
  -moz-background-size: cover;  /* Firefox 3.6 */
  background-position: center;  /* Internet Explorer 7/8 */
}

Try it with your browser.

Use SCSS for calculations

It is possible to calculate the correct padding-top and height attributes manually. Of course you can also just play around until it looks right. But a more future proof option is to use a CSS preprocessor like SCSS to automatically calculate the values for you. Here is an example implementation that generates the CSS shown above.

/* Calculate fluid ratio based on two dimensions (width/height) */
@mixin fluid-ratio($large-size, $small-size) {
  $width-large: nth($large-size, 1);
  $width-small: nth($small-size, 1);
  $height-large: nth($large-size, 2);
  $height-small: nth($small-size, 2);
  $slope: ($height-large - $height-small) / ($width-large - $width-small);
  $height: $height-small - $width-small * $slope;

  padding-top: $slope * 100%;
  height: $height;

  background-size: cover;
  -moz-background-size: cover;  /* Firefox 3.6 */
  background-position: center;  /* Internet Explorer 7/8 */
}

figure.fluidratio {
  /* This element will have fluid ratio from 4:1 at 800px to 2:1 at 300px. */
  @include fluid-ratio(800px 200px, 300px 150px);

  background-image: url(http://voormedia.com/examples/amsterdam.jpg);
}

Summary

It is possible to make any HTML element scale its height proportional to its width. Elements with a percentual padding-top attribute will be scaled based on the their width. To make background images scale in modern browsers you can add background-size: cover.

To make the aspect ratio change gradually when resizing, also set the height attribute. Based on two sizes with a different aspect ratio the correct height and vertical padding can be calculated.