Responsive Background Images with a Minimum Height

A challenge working with background images is getting to scale properly in responsive design. CSS3 introduced the background-size property, and the "cover" and "contain" values for that provide different ways to scale the background image to the size of the HTML element. But, in the case of an element that is the full -width of the page, as the viewport size changes, there isn't an obvious way to set CSS width and height properties that keeps the element fixed to the aspect ratio of the background image. So depending on whether the background-size property is set to "cover" or "contain", the background image either won't fill up the element entirely, or it will be cropped in one dimension.

One CSS quirk comes in handy in this case: Percentage units on the vertical padding properties are measured on the horizontal dimension of the parent. So, in the case of the full-width element, the aspect ratio can be preserved. Divide the height of the image by the width, turn that ratio into a percentage, and set the padding-bottom property on the element to that percentage. For example, if I'm putting a background-image that's 1000px wide by 400px high on a page title H1 tag (class "page-title") that is the full-width of the page, I'd set the CSS like so:

h1.page-title {
  height: 0;
  padding-bottom: 40%;
  width: 100%;
  background-image: url(path/to/my/image.jpg) no-repeat;
  background-size: cover;
}

The height is set to zero, but the padding-bottom property sets the element vertically to the correct size to fit the background image. But now I also want to make sure that the H1 never gets below a minimum height, so that it doesn't get too small on thinner viewports. There are no minimum or maximum properties for padding, but there is a way around this. I can set the height of the element to the minimum I want (say, 200px), and then set the padding-bottom property to account for that 200px by subtracting it:

h1.page-title {
  height: 200px;
  padding-bottom: calc(40% - 200px);
  width: 100%;
  background-image: url(path/to/my/image.jpg) no-repeat;
  background-size: cover;
}

So now, calc makes padding-bottom 40% of the width minus 200 pixels, but those 200 pixels are added back in the height. If 40% of the width is less than 200 pixels, calc produces essentially a negative padding-bottom value. Since padding can't be negative, it effectively becomes 0. So the height will be exactly 200 pixels, which is what I wanted.

This works when I know the size of the image beforehand, but what if I want to be able to upload any image to be the background image of the H1 page title of a node page? If I use an Image field in Drupal attached to a node, or any other entity, I can have Drupal do the math and output the CSS. For this example, I have a Content type Page with an Image Field (machine name: field_image). I've created a custom module called my_module, and in the module I implement hook_node_view():

/**
* Implements hook_node_view()
*/

function my_module_node_view($node, $view_mode, $langcode) {
  // Get the array of field values for the Image field
  if ($image_field_values = field_get_items('node', $node, 'field_image') {
    // Get the image information for the file URI stored in the field, which includes the height and width
    if($image_info = image_get_info($image_field_values[0]['uri'])) {
      // Calculate the aspect ratio by dividing height by width
      $aspect_ratio = $image_info['height'] / $image_info['width'];
     // Output CSS to the page, setting the padding-bottom property based on the calculated aspect ratio
      drupal_add_css('h1.page-title { padding-bottom: calc(' . ($aspect_ratio * 100) . '% - 200px); }', 'type' => 'inline');
    }
  }
}