Skip to content

Detect AVIF Support for Your CSS Images

Published:

In the article AVIF, the definitive image format? we learned about this new image format, which offers a very interesting quality/size balance. Let’s see how to implement it in our CSS images.

Images in CSS

We’ve been able to add background images to HTML elements for a long time. We do it with CSS, specifically with the background-image property.

.element {
  background-image: url("https://picsum.photos/seed/picsum/400/200");
}

And apart from controlling size, position or repetitions, we can’t control anything else from CSS. That’s why it’s not surprising that the new version of the CSS Image module comes with new features that will solve current problems.

The W3C (the World Wide Web Consortium (W3C) is an international community that develops standards that ensure the long-term growth of the Web) is working on the draft CSS Images Module Level 4, which details everything related to images, there are a lot of very interesting new properties, but today we’re going to focus on image-set, which allows us to define different images according to device resolution.

.element {
  background-image: image-set(
    "image.png" 1x,
    "image-2x.png" 2x,
    "image-print.png" 600dpi
  );
}

It’s very similar to what we can already do with the HTML <picture> element.

<picture>
  <source srcset="image-desktop.jpg" media="(min-width:960px)" />
  <source srcset="image-tablet.jpg" media="(min-width:600px)" />
  <img src="image.jpg" alt="Image description" />
</picture>

In the <picture> tag we can also define the image type, as we saw in the article about AVIF.

<picture>
  <source srcset="image.avif" type="image/avif" />
  <source srcset="image.webp" type="image/webp" />
  <img src="image.jpg" alt="Image description" />
</picture>

Well, in the specification of version 4 of the CSS Image Module this has been taken into account, and attention, we’ll be able to define the type of background images 🤩.

.element {
  background-image: image-set(
    "image.avif" type("image/avif"),
    "image.webp" type("image/webp"),
    "image.jpg" type("image/jpeg")
  );
}

It will be fantastic to be able to optimize image loading according to resolution or density, and the image type supported by the browser.

Can I use it now?

Unfortunately the answer is no, as of today there’s no support from browsers. We do have a PostCSS plugin available that allows us to start using the image functionality according to device density.

It’s postcss-image-set-function, which allows us to write the new functionality in our CSS and PostCSS takes care of transpiling it to CSS supported by all browsers. As we can see in the plugin documentation.

.example {
  background-image: image-set(
    url(img.png) 1x,
    url(img@2x.png) 2x,
    url(img@print.png) 600dpi
  );
}

/* Becomes */

.example {
  background-image: url(img.png);
}

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
  .example {
    background-image: url(img@2x.png);
  }
}

@media (-webkit-min-device-pixel-ratio: 6.25), (min-resolution: 600dpi) {
  .example {
    background-image: url(my@print.png);
  }
}

.example {
  background-image: image-set(
    url(img.png) 1x,
    url(img@2x.png) 2x,
    url(img@print.png) 600dpi
  );
}

If we look at it in detail, there’s not much mystery, it’s simply defining them within media queries, so if the browser supports image-set by cascade, it will use the new functionality, otherwise, it will use the media queries. Simple, but effective.

What about image type or format?

We can’t emulate or obtain the type from CSS, so we’ll have to use JavaScript to temporarily “patch” the problem.

JavaScript to the Rescue

With JavaScript we can apply a patch, until we have it natively in CSS. We can achieve it by checking for AVIF format support.

In our CSS we’re going to add a background image to the document body.

body {
  background-color: #000;
  background-repeat: no-repeat;
  background-size: cover;
}

body.avif {
  background-image: url(./images/lions.avif);
}

body.no-avif {
  background-image: url(./images/lions.jpg);
}

I know you can put the image in JPEG format directly on the body and overwrite the background-image if the browser has support, but I think it’s clearer for the example.

Now we need to add the JavaScript that adds one class or another depending on whether the client is capable of representing AVIF images or not.

async function supportsAvif() {
  if (!this.createImageBitmap) return false;

  const avifData =
    "data:image/avif;base64,AAAAFGZ0eXBhdmlmAAAAAG1pZjEAAACgbWV0YQAAAAAAAAAOcGl0bQAAAAAAAQAAAB5pbG9jAAAAAEQAAAEAAQAAAAEAAAC8AAAAGwAAACNpaW5mAAAAAAABAAAAFWluZmUCAAAAAAEAAGF2MDEAAAAARWlwcnAAAAAoaXBjbwAAABRpc3BlAAAAAAAAAAQAAAAEAAAADGF2MUOBAAAAAAAAFWlwbWEAAAAAAAAAAQABAgECAAAAI21kYXQSAAoIP8R8hAQ0BUAyDWeeUy0JG+QAACANEkA=";
  const blob = await fetch(avifData).then(r => r.blob());
  return createImageBitmap(blob).then(
    () => true,
    () => false
  );
}

(async () => {
  const classAvif = (await supportsAvif()) ? "avif" : "no-avif";
  document.body.classList.add(classAvif);
})();

The first thing we have is supportsAvif() which is in charge of detecting support. The way to do it is to load an AVIF image, in base64 in this case, and then create the image element with createImageBitmap. This will return a true or false. And with the ternary assigned to the classAvif constant we’ll know if the browser supports AVIF or not.

Example

Conclusions

As soon as we have CSS Image Module Level 4 available in our browsers, it will be very simple to optimize image loading according to device and format support. But for now, we can rely on JavaScript to achieve a better experience for our users.

Acknowledgments

I want to thank Jon Sneyers and Kornel, true magicians of image encoders/decoders, for their help in obtaining a base64 of an AVIF format image as optimized as possible.


Previous Post
It's Curious How Forgotten and Mistreated CSS Is
Next Post
CSS Books