Scaling background images with content

A few days ago I found myself with a problem to do with background images used for page headings that had content over them. The issue was that we had images with 100% widths and we wanted to avoid, where possible, the image being cropped as the parent element changed width.

In order to do this we required that the height of the element with the background image to scale with width changes keeping the width to height ratio the same. This problem its self has already been solved on the internet by adding padding-top representing the height to width ratio to the element with the background image.

So if you have a 16:9 ratio image then the padding top should be 9/16 * 100 = 56.25:

HTML:

<div>  
  <div class="adaptiveHeight hasBackgroundImage"></div>
</div>  

CSS:

.adaptiveHeight {
  padding-top: 56.25%;
}

Example codepen here.

How does this work? It works as percentage padding is based on the width of the parent element and the only "content" of the element is the padding so the height becomes the same size as that padding.

However, this solution is no good if you have any content within your background image element as, because you are using padding to set the height, any content will add to the height making the height larger than intended.

A way to handle being able to have content without interfering with the height is to take the content out of the flow completely using absolute positioning, as follows:

HTML:

<div>  
  <div class="adaptiveHeight hasBackgroundImage">
    <div class="adaptiveHeightContent content">
      Content<br />
    </div>
  </div>
</div>  

CSS:

.adaptiveHeight {
  padding-top: 56.25%;
  position: relative;
}

.adaptiveHeightContent {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

Example codepen here.

Another way to deal with this, if you have fixed height content, is to use the calc function on your padding-top to take away the fixed height leading to the overall height being the same. This is done as follows:

HTML:

<div>  
  <div class="adaptiveHeight hasBackgroundImage">
    <div class="adaptiveHeightContent content">
      Content<br />
    </div>
  </div>
</div>  

CSS:

.adaptiveHeight {
  padding-top: calc(56.25% - 60px);
  position: relative;
}

.adaptiveHeightContent {
  height: 60px;
}

Example codepen here.

If you want the text to appear at the top rather than the bottom then use padding-bottom instead.

Finally, what if you have variable length content that you want to be in the flow of the document, so that the height can't become smaller than the content?

Well to deal with that scenario, rather than setting the height via padding on the element its self, I looked at setting the height using hidden child elements. The concept is best shown as an image:

Adaptive height element hidden child display

In the image the dotted border represents the visible area of the element. The red box represents an element sitting out of the visible area of the element, but forcing the element's height to be the height we want it to be.

If at any point the width became so narrow as the make the height of the red box smaller than the element's content height then the content would keep the element from shrinking below its height. Its worth noting that if this happens then the image ratio will be lost and the image will still be cropped. This is unavoidable.

The code for this is as follows:

HTML:

<div>  
  <div class="adaptiveHeight hasBackgroundImage">
    <div class="adaptiveHeightContent content">
      Content<br />
    </div>
  </div>
</div>  

CSS:

.adaptiveHeight {
  white-space: nowrap;
  overflow-x: hidden;
}

.adaptiveHeight::after {
  content: '';
  display: inline-block;
  padding-top: 56.25%;
  vertical-align: top;
}

.adaptiveHeightContent {  
  display: inline-block;
  vertical-align: top;  
}

Example codepen here.

And a final nice quirk of this is that you can vertical align your content within the element using the vertical align property.

Hope this helps you out!