Skip to content

Detecta el soporte AVIF para las imágenes de tu CSS

Posted on:6 de diciembre de 2020

En el artículo AVIF, ¿el formato de imagen definitivo? conocimos este nuevo formato de imágenes, qué nos ofrece un equilibrio de calidad/peso muy interesante. Vamos a ver cómo implementarlo en las imágenes de nuestro CSS.

Imágenes en CSS

Ya hace mucho tiempo que tenemos la posibilidad de añadir imágenes de fondo en los elementos HTML. Lo hacemos con CSS, concretamente con la propiedad background-image.

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

Y a parte de controlar el tamaño, posición o repeticiones, desde CSS no podemos controlar nada más. Es por eso que no es de extrañar que la nueva versión del módulo de imagen CSS venga con novedades que nos solucionarán problemas actuales.

El W3C (el World Wide Web Consortium (W3C) es una comunidad internacional que desarrolla estándares que aseguran el crecimiento de la Web a largo plazo) está trabajando en el borrador CSS Images Module Level 4, en él se detalla todo lo relacionado con las imágenes, hay un montón de propiedades nuevas muy interesantes, pero hoy nos vamos a centrar en image-set, la cual nos permite definir diferentes imágenes según la resolución del dispositivo.

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

Es muy similar a lo que ya podemos hacer en el elemento HTML <picture>.

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

En el tag <picture> también podemos definir el tipo de imagen, como vimos en el artículo sobre AVIF.

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

Pues en la especificación de la versión 4 del CSS Image Module se ha tenido en cuenta, y atención, podremos definir el type de las imágenes de background 🤩.

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

Será fantástico poder optimizar la carga de las imágenes según la resolución o densidad, y el tipo de imagen soportado por el navegador.

¿Lo puedo utilizar ya?

Lamentablemente la respuesta es no, a día de hoy no hay soporte por parte de los navegadores. Sí que tenemos disponible un plugin de PostCSS que nos permite empezar a utilizar la funcionalidad de imágenes según la densisad del dispositivo.

Se trata de postcss-image-set-function, el cual nos permite escribir la nueva funcionalidad en nuestro CSS y PostCSS se encarga de transpilarlo al CSS soportado por todos los navegadores. Según podemos ver en la documentación del plugin.

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

/* Se convierte en */

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

Si lo miramos en detalle, no tiene mucho misterio, simplemente los está definiendo dentro de media queries, así si el navegador soporta image-set por cascada, utilizará la nueva funcionalidad, sino, hará uso de las media queries. Sencillo, pero eficaz.

¿Y qué hay del tipo o formato de imágenes?

El type no lo podemos emular u optener desde CSS, así que tendremos que ayudarnos de JavaScript para “parchear” el problema de forma temporal.

JavaScript al rescate

Con JavaScript podemos aplicar un parche, hasta que lo tengamos de forma nativa en CSS. Lo podemos conseguir haciendo una comprovación del soporte al formato AVIF.

En nuestro CSS vamos a añadir una imagen de fondo en el body del documento.

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

Sé que se puede poner la imagen en formato JPEG directamente en el body y sobreescribir el background-image en el caso de que el navegador tenga soporte, pero para el ejemplo creo que queda más claro.

Ahora necesitamos añadir el JavaScript que añada una clase u otra según si el cliente es capaz de representar imágenes AVIF o no.

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

Lo primero que tenemos es la supportsAvif() que es la encargada de detectar el soporte. La manera de hacerlo es cargar una imagen AVIF, en base64 en este caso, y posteriormente crear el elemento imagen con createImageBitmap. Esto nos devolverá un true o false. Y con la ternaria asignada a la constante classAvif sabremos si el navegador soporta AVIF o no.

Ejemplo

Conclusiones

En cuanto tengamos disponible CSS Image Module Level 4 en nuestros navegadores, será muy simple optimizar la carga de las imágenes según el dispositivo y soporte de formatos. Pero de momento, podemos apoyarnos en JavaScript para conseguir una mejor experiencia a nuestras/os usuarias/os.

Agradecimientos

Quiero dar las gracias a Jon Sneyers y Kornel, unos auténticos magos de los coders/decoders de imágenes, por su ayuda con la optención de un base64 de una imagen de formato AVIF lo más optimizado posible.