You’ll probably agree that using the same background image for a desktop screen as well as for a mobile device is not good for performance. And to make matters worse usually full viewport background image is the main above the fold element of most home pages.
Wouldn’t be nice if we had the same responsive image functionality for our background images as we have for img
tags?
Well, not long ago I found a neat trick how to do just that. I’ll show you how to automate creating responsive background images so that all you’re left to do is upload one high-resolution image to WordPress and pass its ID to a Blade component.
Current options for responsive background images
The srcset
and sizes
attributes for img
tags have been for around for a while now. According to Can I Use browser support is about 89%.
For background-image
set with CSS the similar standard would be image-set()
function. But the problem is that there is no support for Firefox and Edge. It could be a good alternative in the future but for now, I’d not recommend using image-set
for responsive background images.
Another option is the object-fit
CSS property for img
tags but again browser support is lacking, and if you need to support IE then it’s off the table.
Alternative approach
WordPress will take care of generating different image sizes, including your custom ones. What we are left to do is to make sure that the browser downloads the right size of an image.
We can use the power of the img
tag with srcset
and sizes
attributes to pick the appropriate image size.
Once the image is loaded then with a little bit of JavaScript we’ll apply it to the background of an element that has our predefined class.
We’ll also use Blade’s component to create a background image container.
JavaScript snippet
In this case, our background images will depend on JavaScript being supported by all browsers.
If you’re fine with that then here is the JavaScript code snippet that does all the magic.
// Search for inner img, wait for load event and grab src to apply as background-image
export class ResponsiveBackgroundImage {
constructor(element) {
this.element = element;
this.img = element.querySelector('img');
this.src = '';
this.img.addEventListener('load', () => {
this.update();
});
if (this.img.complete) {
this.update();
}
}
update() {
let src = typeof this.img.currentSrc !== 'undefined' ? this.img.currentSrc : this.img.src;
if (this.src !== src) {
this.src = src;
this.element.style.backgroundImage = 'url("' + this.src + '")';
}
}
}
// Init to look for containers with .js-bg-image class
const bgImages = document.querySelectorAll('.js-bg-image');
for (let i = 0; i < bgImages.length; i++) {
new ResponsiveBackgroundImage(bgImages[i]);
}
Let’s place it in scripts/respbgimages.js
and include in resources/assets/config.json
:
...
"entry": {
"main": [
"./scripts/main.js",
"./styles/main.scss"
],
"customizer": [
"./scripts/customizer.js"
],
"respbgimages": [
"./scripts/respbgimages.js"
]
},
...
We want this script to start executing as soon as possible once our page is loaded with all img
tags so let’s enqueue it in the footer before sage/main.js
.
wp_enqueue_script('respbgimages', asset_path('scripts/respbgimages.js'), [], null, true);
Blade components
Components in Blade are very similar in behavior to views.
Let’s create our background image component in views/components/bg-image.blade.php
.
<section>
{!! $slot !!}
</section>
When we include a component in Blade template the $slot
variable will be replaced with a markup that’s inside a component. Similarly like @yield('content')
in layouts/app.blade.php
is replaced by the content inside @section('content') @endsection
in Sage templates.
Here is the full markup for a component for a full-width background image.
<section class="c-bg-image {{$class ?? ''}} js-bg-image">
<img src="{{ wp_get_attachment_image_src($image, 'full')[0] }}" style="display:none" sizes="100vw" srcset="{{ wp_get_attachment_image_srcset($image, 'full') }}">
{!! $slot !!}
</section>
I’m setting a container to which background image will be applied and an img
inside it to load the correct image size. If your website has a boxed design then adjust sizes
attribute to something like that:
sizes="(min-width: 1400px) 1400px, 100vw"
Also, you can pass an optional $class
variable in case you need to style a specific background image.
Don’t get alarmed by display: none
in the img
, a browser will still grab the correct src
from a srcset
. As the proof, I made the fiddle demonstration.
Finally, include a component in your template:
@component('components.bg-image', ['image' => $id])
<div class="container">
<h2>Title</h2>
<p>Some text</p>
</div>
@endcomponent
Bonus tip
Also, while a full-size image is loading, as a fallback we can use background-image: linear-gradient()
reflecting your background image colors to improve a perceived load time. Here is a quick guide from Harry Roberts how he does it for his own site.
Join the discussion on Roots Discourse