Easy Responsive Background Images in Craft and WordPress

A simple way to implement responsive background images in Craft and WordPress.

Using Media Queries to load a specific background images is nothing new, but is still so rarely implemented. The good news is, if you're using a CMS that can generate images dynamically, like Craft or WordPress, it's even easier. 

In Craft

Here's a simple implementation of how we do it on our Craft sites:

{% set image = entry.image.first %}

<style type="text/css">
  .hero-image {
    background-image: url('{{ image.url({ width: 600 }) }}');
  }

  @media (min-width: 600px) {
    .hero-image {
      background-image: url('{{ image.url({ width: 1000 }) }}');
    }
  }

  @media (min-width: 1000px) {
    .hero-image {
      background-image: url('{{ image.url({ width: 1400 }) }}');
    }
  }
</style>

If you wanted to take it a step further, you can install our fork of this plugin that adds an image64 Twig filter that inlines a base64 encoded version of an image. In our case, a 16px wide one. Because the image is inline the user will get a small preview (albeit a stretched version) while their browser downloads the image they actually need. 

{% set image = entry.image.first %}

<style type="text/css">
  .hero-placeholder {
    background-image: url({{ image.url({ width: 16 }) | image64(true) }});
  }

  .hero-image {
    background-image: url({{ image.url({ width: 600 }) }});
  }

  .hero-wrapper {
    position: relative;
  }

  .hero-placeholder,
  .hero-image {
    height: 400px;
    position: absolute;
    width: 100%;
  }
</style>

<div class="hero-wrapper">
  <div class="hero-placeholder"></div>
  <div class="hero-image"></div>
</div>

To see a more in-depth implementation of this method check out this post on how Medium does it.

In WordPress

We'll first define image sizes we want to use in our template along with a custom base64Encoder function to copy that inline image placeholder functionality we use above. All this goes in functions.php:

<?php
// Defines image sizes
add_image_size('preloader' , 16);
add_image_size('small'     , 600);
add_image_size('medium'    , 1000);
add_image_size('large'     , 1400);

// Allows us to get a base64 encoded image
function base64Encoder($url)
{
    $image = file_get_contents($url);
    return base64_encode($image);
}

Then, in your WordPress template, grab that image and reference the size we want:

<style type="text/css">
  .hero-placeholder {
    background-image: url(data:image/jpeg;base64,<?php echo base64Encoder(wp_get_attachment_image_src(get_post_thumbnail_id($post->ID), 'small')); ?>);
  }

  .hero-image {
    background-image: url(<?php echo wp_get_attachment_image_src(get_post_thumbnail_id($post->ID), 'small'); ?>);
  }

  @media (min-width: 600px) {
    .hero-image {
      background-image: url(<?php echo wp_get_attachment_image_src(get_post_thumbnail_id($post->ID), 'medium'); ?>);
    }
  }

  @media (min-width: 1000px) {
    .hero-image {
      background-image: url(<?php echo wp_get_attachment_image_src(get_post_thumbnail_id($post->ID), 'large'); ?>);
    }
  }
</style>

One downside of using this technique in WordPress is if you ever need to add a new image size (say cinema, 1600px); we'll need to install a plugin to regenerate all thumbnail images. 

If you don't want to define all of these image sizes in WordPress, you can use the Timber plugin, which, along with giving you the awesome Twig syntax in your templates, includes a TimberImage class that can dynamically transform images. 

<style type="text/css">
  .hero-placeholder {
    background-image: url(data:image/jpeg;base64,{{ function('base64Encoder', post.thumbnail.src|resize(16)) }});
  }

  .hero-image {
    background-image: url({{ post.thumbnail.src|resize(600) }});
  }

  @media (min-width: 600px) {
    .hero-image {
      background-image: url({{ post.thumbnail.src|resize(1000) }});
    }
  }

  @media (min-width: 1000px) {
    .hero-image {
      background-image: url({{ post.thumbnail.src|resize(1400) }});
    }
  }
</style>

That's all there is to it!

Final Thoughts

Of course, there are packages and gems that can do this image transformation outside of a CMS. We've used them before and with great success, but be wary if you're transforming hundreds or thousands of images. The more images and transformations you have to generate the more your server will get bogged down. One strategy is offloading this work to a service like Imgix and Cloudinary that can generate and host these images on the fly. And, seeing the direction the web is going (utilize services, do less stuff on your own) they're both worth checking out.

I hope you found this helpful! Let me know in the comments below if you've use other techniques for responsive background images, or just want to troll.

Tommy is a front-end developer who builds dynamic, interactive sites for Viget clients such as ESPN, Dick's Sporting Goods, and PUMA. He works in our Falls Church, VA, HQ.

More posts by Tommy