Skip to content

AVIF in CSS con PostCSS

Posted on:20 de diciembre de 2020

En el artículo Detecta el soporte AVIF para las imágenes de tu CSS os presentaba una forma de detectar el soporte del nuevo formato de imágenes AVIF y utilizarlas como imágenes de background de nuestro CSS. Hoy os presento este plugin de PostCSS para que sea transparente para nosotras/os en nuestros desarrollos.

Detrás de una idea (casi siempre) hay un tweet

El pasado 6 de diciembre hice una adaptación del artículo que os comentaba arriba, a una versión en inglés “Detect AVIF image support to use in your CSS” publicada en la plataforma dev.to. Siguiendo con la costumbre de dar visibilidad, hice mención de ello en Twitter, y mi gran sorpresa fue ver que la propia cuenta de PostCSS me animaba ha hacer un plugin con esa idea.

Nice idea for avif-in-css PostCSS plugin like we have for WebP

Proceso de creación del plugin

Como en el propio tweet me sugería, me basé en el plugin webp-in-css del propio Andrey Sitnik. Así que el proceso fue simple.

Me gustó mucho la solución de Andrey para detectar el soporte del formato de imagen, simple y muy optimizada.

Comparando soluciones: Joan vs Andrey

Joan

Esta es la versión que propuse en el anterior artículo.

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

Como “buen” desarrollador decidí utilizar async/await, fetch, constantes y promesas, vamos, todo lo que una persona crafter quiere utilizar en su código.

Andrey

Esta es la solución basándome en el código de Andrey.

let i = new Image();
i.onload = i.onerror = _ => {
  document.body.classList.add(i.height > 0 ? "avif" : "no-avif");
};
i.src =
  "data:image/avif;base64,[AAAAFGZ0eXBhdmlmAAAAAG1pZjEAAACgbWV0YQAAAAAAAAAOcGl0bQAAAAAAAQAAAB5pbG9jAAAAAEQAAAEAAQAAAAEAAAC8AAAAGwAAACNpaW5mAAAAAAABAAAAFWluZmUCAAAAAAEAAGF2MDEAAAAARWlwcnAAAAAoaXBjbwAAABRpc3BlAAAAAAAAAAQAAAAEAAAADGF2MUOBAAAAAAAAFWlwbWEAAAAAAAAAAQABAgECAAAAI21kYXQSAAoIP8R8hAQ0BUAyDWeeUy0JG+QAACANEkA=]";

La mejor opción

Andrey Win

Claramente la propuesta de Andrey es mucho más óptima, y como estamos desarrollando un polyfill en este caso, el código debe ser lo más ligero posible.

Ser crafters no quiere decir utilizar las últimas funcionalidades disponibles en el lenguaje, sino aplicar la mejor solución según el caso de uso.

Uso de avif-in-css

Veamos cómo utilizar el plugin para poder añadir imágenes en formato AVIF en nuestro CSS según el soporte del navegador.

Instalación

Implementar avif-in-css consta de dos partes, la primera es el plugin PostCSS, y lo podemos instalar y configurar de la siguente manera.

npm install --save-dev postcss postcss-cli avif-in-css

Una vez instalado, debemos configurar PostCSS para que utilice los plugins que queremos, lo podemos hacer creando un archivo postcss.config.js en la raíz del proyecto con el siguiente contenido.

module.exports = {
  plugins: [require("avif-in-css")],
};

En el caso de que ya estés utilizando PostCSS en tu proyecto, solo debemos añadirlo, como se puede ver en el código del site de ejemplo.

Después podemos ejecutar, o añadir a nuestros scripts de NPM el siguiente comando:

postcss src/css/index.css -o dist/styles.css

Este plugin lo que hará es añadir en nuestro CSS, dos clases para cargar la imagen de formato AVIF en el caso de que el navegador la soporte o la JPEG o PNG que hayamos definido para el resto de navegadores.

CSS de entrada

.logo {
  width: 80px;
  height: 80px;
  background-image: url(logo.jpg);
}

CSS de salida

.logo {
  width: 80px;
  height: 80px;
}

body.avif .logo {
  background-image: url(logo.avif);
}

body.no-avif .logo {
  background-image: url(logo.jpg);
}

La segunta parte, consiste en cargar el polyfill que se encarga de comprobar si el navegador tiene soporte y añadir la clase avif o no-avif en el <body> de nuestra página.

Tenemos varias maneras de cargar el polyfill:

Cargarlo via CDN
<script src="https://unpkg.com/avif-in-css/polyfill.js"></script>
Cargarlo desde el paquete NPM
// CommonJS
require("avif-in-css/polyfill.js");

// ES6
import "avif-in-css/polyfill.js";

Opciones de configuración

El plugin admite un objeto de opciones para configurar algunos parámetros.

modules (boolean)

Si estamos utilizando CSS Modules podemos definir esta opción con un valor true y la clase será envuelta con :global(). Su valor por defecto es false.

avifClass (string)

Es el nombre de la clase para indicar que el navegador tiene soporte para el formato de imágenes AVIF, y su valor por defecto es avif, con esta opción podemos configurar el nombre que más nos interese o evitar colisiones de nombres.

noAvifClass (string)

Es el nombre de la clase para indicar que el navegador no tiene soporte para el formato de imágenes AVIF, y su valor por defecto es no-avif, con esta opción podemos configurar el nombre que más nos interese o evitar colisiones de nombres.

rename (function)

Esta opción nos permite definir una función para indicar el nombre de la imagen AVIF. Esta es la función por defecto:

rename: oldName => oldName.replace(/\.(jpe?g|png)/gi, ".avif");
// image.png -> image.avif

Esto cambia las extensiones jpeg, jpg o png por avif, sin modificar el nombre de la imagen.

En este ejemplo estaríamos añadiéndola, en lugar de cambiarla:

rename: oldName => `${oldName}.avif`;
// image.png -> image.png.avif

Qué no hace avif-in-css

El plugin no hace ningún tipo de conversión en el formato de las imágenes. Así que, previamente deberemos tenerlas convertidas, igual que lo haríamos con el tag HTML <picture> o el atributo srcset del tag <img>.

Actualmente ya hay varias herramientas para convertir nuestras imágenes a formato AVIF, como Squoosh, Avif.app, Convertio.co o avif.io.

Demo

Podéis echar un vistazo al código de la web de ejemplo AVIF in CSS

ChromeSafari
ChromeSafari
AVIF image format | 79.3kBJPEG image format | 183.96kb