17

How do you detect in CSS if webp images are supported?

.home-banner-background {
     background-image: url('img/banner.jpg');
 }
 /*Here is an if statement that will check if webp is supported*/ {
        .home-banner-background {
            background-image: url('img/banner.webp');
        }
}

Is there an "if" statement which can do that?

Erez Shlomo
  • 2,124
  • 2
  • 14
  • 27
  • Pretty sure that is impossible to “detect” using CSS alone. – misorude Aug 05 '19 at 13:12
  • There are no if statments in css. If something is not supported it simply ignores that rule. A missing image or not supported image will not be ignored since the css is valid. – Persijn Aug 05 '19 at 13:21
  • 1
    Only "if statements in CSS" can test property support only -- `@supports (property: value){/*rules*/}` -- not image file format support. It would be nice to be able to do `@supports (background-image: url('existing-image.webp')){/*...*/}` but for quite obvious reasons this doesn't work. – myf Aug 05 '19 at 13:37
  • Here working solution example https://stackoverflow.com/a/64048033/1266559 – Alex Oct 15 '21 at 21:31
  • Re: _"There are no if statments in css."_ Both `@media` and `@supports` represent `if` structures. – Rounin Feb 11 '22 at 10:50

8 Answers8

18

The modern webkit browser will use the WebP background image with this CSS:

.map {background-image: url('../img/map.jpg');}
  @supports (background-image: -webkit-image-set(url('../img/map.webp') 1x)) {
    .map {background-image: -webkit-image-set(url('../img/map.webp') 1x) }
  }
Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77
glocsw
  • 462
  • 4
  • 4
  • Didn't work for me. Safari 13.1.3 seems to support `-webkit-image-set` but does not support webp and thus it still tries to load it, but can't. – Dollique Jun 21 '22 at 12:03
11

You can use Modernizr. It is a tool that detect available features on the user's browser. It is just a script to add in your website, and with it, you can write something like that :

.no-webp .home-banner-background {
     background-image: url('img/banner.jpg');
 }

.webp .home-banner-background {
     background-image: url('img/banner.webp');
}
Fifi
  • 467
  • 6
  • 17
  • 4
    For clarification, the answer to the question is "CSS cannot currently directly check if the browser supports webp." This solution uses JavaScript with CSS. – Caleb Aug 03 '22 at 00:00
9

Use the type() function together with image-set() to provide alternative image formats with CSS. In the example the type() function is used to serve the image in WEBP and JPEG formats. If the browser supports webp, it will choose that version. Otherwise it will use the jpeg version.

.box {
  background-image: image-set(
    url("large-balloons.webp") type("image/webp"),
    url("large-balloons.jpg") type("image/jpeg"));
}

Doku

Take
  • 324
  • 3
  • 10
  • This solution has very poor browser support at the moment. – MarcGuay Dec 21 '21 at 16:42
  • 1
    Thanks for adding this answer! It can be helpful for people who only need to target specific browsers. I will bookmark and keep an eye on the browser compatibility over the next year - it looks promising... https://caniuse.com/mdn-css_properties_background-image_image-set – Dagmar Jan 31 '22 at 10:35
  • Don't forget the fallback: https://developer.mozilla.org/en-US/docs/Web/CSS/image/image-set#providing_a_fallback – Mihail H. May 24 '22 at 19:45
6

This is currently not supported with CSS.

In the CSS Images Module Level 4 Draft, a fallback solution is suggested, but this is currently not supported anywhere. (2.4. Image Fallbacks and Annotations: the image() notation)

But if it will become part of a standard in some years, you then might be able to write:

.home-banner-background {
    image:image('img/banner.webp', 'img/banner.jpg')
}

Until then you need to use tools like Modernizer, as suggested in the answer of Fifi

Alternatively, the picture HTML tag might something you could think of, but if that is usable in your case depends on how the image should resize on your site:

<picture>
  <source srcset="img/banner.webp" type="image/webp">
  <source srcset="img/banner.jpg" type="image/jpeg"> 
  <img src="img/banner.jpg">
</picture>


t.niese
  • 39,256
  • 9
  • 74
  • 101
3

You could actually just add it to the original declaration without needing multiple classes, @supports, or bringing in another library similar to this:

.home-banner-background {
    background-image: url('img/banner.jpg');
    background-image: -webkit-image-set(url('img/banner.webp') 1x,
                                        url('img/banner-2x.webp') 2x),
                                        /* etc */;
}

Webkit will use the webp images and all others will fallback to the regular image.

Poelinca Dorin
  • 9,577
  • 2
  • 39
  • 43
stinkysGTI
  • 573
  • 6
  • 21
  • 7
    Safari also supports `-webkit-image-set` but not webp. This will fail on iOS – fregante Apr 23 '20 at 22:06
  • 1
    Ah, good call. In some situations recently, I've been checking for user agent, and using the appropriate image format based on that. Can't wait till there's a better way. – stinkysGTI Apr 24 '20 at 23:38
  • 1
    Checking the "Accept" header for "image/webp" is much better than guessing based on the user agent – Luke Cousins May 12 '20 at 19:57
  • 3
    Pretty sure that both images will be downloaded then and then it totally defeats the purpose of decreasing the size with webp... – OZZIE Oct 21 '21 at 12:12
1

This solution works well:

.home-banner-background {
  background-image: url('img/banner.jpg');
}

@supports (gap: 1px) {
  .home-banner-background {
    background-image: url('img/banner.webp');
  }
}

Targeting just browsers which support the gap property ensures a very similar browser support to WebP images. REF: https://caniuse.com/flexbox-gap

coliff
  • 812
  • 8
  • 12
0

In 2022 I'm using this:

backround: url('image.jpg');
backround: url('image.webp');

And the browser is downloading only the webp.

Karl Zillner
  • 581
  • 7
  • 16
0

For now (Q1 2023) using image-set is not a sufficient solution because lots of devices that actually support WebP won't get be able to use it. There is a large difference in support coverage of both feature.

I'd opt for Modernizr but it is not tree-shakeable. Code below might be useful to set a class on <body> or use directly in the image wrapper or directive.

// inspired by Modernizr implementation
function supportsWebP() {
  return new Promise((resolve) => {
    const image = new Image();
    image.onerror = () => resolve(false);
    image.onload = event => resolve(event.type === 'load' && image.width === 1);
    image.src = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=';
  });
}
// example usage
const useWebP = await supportsWebP();
const backgroundImage = `/img/picture.${ useWebP ? 'webp' : 'png' }`;