Skip to main content

The CSS padding trick: responsive intrinsic ratios

You may or may not have come across this this little CSS quirk before, but if you have it will most likely have been within the context of creating responsive videos.

This technique makes use of the unique way padding-top and padding-bottom deal with percentage values - they will be interpreted as a percent of the width of the containing element.

WTF? I dunno why, but in this case it actually turns out to be pretty useful - we can create boxes with intrinsic ratios pretty easily using this idea. Let's take a look at the example from Thierry Koblentz's article, mentioned above:

CSS:

.wrapper-with-intrinsic-ratio {
  position: relative;
  padding-bottom: 20%;
  height: 0;
}
.element-to-stretch {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: teal;
}

HTML:

<div class="wrapper-with-intrinsic-ratio">
  <div class="element-to-stretch"></div>
</div>

See the JSfiddle here.

So the wrapper is going to be flexible and as wide as it can, while always being 20% as high as it is wide - an aspect ratio of 5:1. This height is set to 0 because the actual height comes from the padding, so in order to have content in there it needs to be positioned absolutely, relative to the wrapper. This allows the content to be positioned over the "padding area".

The padding percentage itself can be easily calculated with: (height / width) x 100.

So working with that we can now apply this idea wherever we want. Let's say we have a grid of images with a two different of aspect ratios (1:1 and 2:1) like this:

We could just slap the images in there and rely on the browser to use the image dimensions for the layout, but this isn't perfect - the browser would have to wait for the images to load before it has any idea what is going on. 

And what about responsive? The browser would have to scale the images and then use their new dimensions for the layout - this is also unreliable, different browsers will come up with varied results which might be off by a pixel or two (check out John Resig's post on sub-pixels). This would likely break the layout or at least look crap.

So we can use the padding trick to achieve this is a much more reliable way pretty easily:

CSS:

.wrapper-1x1 {
  width: 33.33333%;
  float: left;
}
.wrapper-2x1 {
  width: 66.66666%;
  float: left;
}
.wrapper-1x1 .inner {
  position: relative;
  padding-bottom: 100%;
  height: 0;
}
.wrapper-2x1 .inner {
  position: relative;
  padding-bottom: 50%;
  height: 0;
}
.element-to-stretch {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

HTML:

<div class="wrapper-1x1">
  <div class="inner">
    <img class="element-to-stretch" src="http://lorempixel.com/400/400/cats/" />
  </div>
</div>
<div class="wrapper-2x1">
  <div class="inner">
    <img class="element-to-stretch" src="http://lorempixel.com/800/400/cats/" />
  </div>
</div>
<div class="wrapper-2x1">
  <div class="inner">
    <img class="element-to-stretch" src="http://lorempixel.com/800/400/cats/" />
  </div>
</div>
<div class="wrapper-1x1">
  <div class="inner">
    <img class="element-to-stretch" src="http://lorempixel.com/400/400/cats/" />
  </div>
</div>

Pretty sweet. You can see the JSfiddle here.

If you are using SASS you could even whip up a handy mixin for this, with arguments for the x and y dimensions.

SCSS:

@mixin pad-ratio($x, $y, $selector: img) {
  position: relative;
  padding: 0 0 percentage($y/$x) 0;
  height: 0;
  #{$selector} {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }
}

I also added an optional argument for the selector to be used, which defaults to img. You could use this with the same markup as before - you would still need to set the widths for the wrappers of course.

SCSS:

.wrapper-1x1 {
  width: 33.33333%;
  float: left;
}
.wrapper-2x1 {
  width: 66.66666%;
  float: left;
}
.wrapper-1x1 .inner {
  @include pad-ratio(1, 1);
}
.wrapper-2x1 .inner {
  @include pad-ratio(2, 1);
}

Well there you have it. My point is that it's good to know this trick exists and that it can be useful for more that just videos.

It might not be something you need to use all of the time, but when you do, it can be the perfect solution - and to think it was hiding there in the rules of CSS all along.