
> In the article [AVIF, the definitive image format?](https://joanleon.dev/avif) 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.

<div style="margin-bottom: 4em" />

## 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.

```css
.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](https://drafts.csswg.org/css-images-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.

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

<div style="margin-bottom: 4em" />

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

```html
<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>
```

<div style="margin-bottom: 4em" />

In the `<picture>` tag we can also define the image type, as we saw in the article about [AVIF](https://joanleon.dev/avif).

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

<div style="margin-bottom: 4em" />

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 🤩.

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

<div style="margin-bottom: 4em" />

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](https://github.com/csstools/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.

```css
.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
  );
}
```

<div style="margin-bottom: 4em" />

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.

<div style="margin-bottom: 4em" />

## 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.

```css
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);
}
```

<div style="margin-bottom: 4em" />

> 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.

```js
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);
})();
```

<div style="margin-bottom: 4em" />

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.

<div style="margin-bottom: 4em" />

### Example

<div style="margin-bottom: 2em" />

<iframe
  src="https://codesandbox.io/embed/condescending-colden-o1gse?fontsize=14&hidenavigation=1&module=%2Fjs%2Findex.js&theme=dark"
  style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
  title="condescending-colden-o1gse"
  allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
  sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
></iframe>

<div style="margin-bottom: 4em" />

## 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.

<div style="margin-bottom: 4em" />

## Acknowledgments

I want to thank [Jon Sneyers](https://twitter.com/jonsneyers) and [Kornel](https://twitter.com/kornelski), true magicians of image encoders/decoders, for their help in obtaining a `base64` of an **AVIF** format image as optimized as possible.
