Imágenes receptivas en CSS | Programar Plus

El término “imágenes receptivas” ha llegado a significar “imágenes receptivas en HTML”, en otras palabras, el srcset y sizes atributo para <img> y el <picture> elemento. Pero, ¿cómo se asignan las capacidades que estas cosas proporcionan a CSS?

CSS generalmente no estuvo realmente involucrado en el viaje de imágenes receptivas de los últimos años. Eso es por una buena razón: CSS ya tiene las herramientas. Las imágenes receptivas estaban, en cierto sentido, simplemente poniéndose al día con lo que CSS ya podía hacer. Vamos a ver.

srcset en CSS

En HTML, srcset es así (tomado del sitio de Picturefill):

<img srcset="
  https://css-tricks.com/examples/images/image-384.jpg 1x, 
  https://css-tricks.com/examples/images/image-768.jpg 2x
" alt="…">

Una imagen para pantallas 1x, una imagen más grande para pantallas 2x. Si quisiéramos hacer lo mismo, solo como un background-image en CSS, podríamos hacer:

.img {
  background-image: url(examples/images/image-384.jpg); 
}
@media 
  (-webkit-min-device-pixel-ratio: 2), 
  (min-resolution: 192dpi) { 
  .img {
    background-image: url(examples/images/image-768.jpg);
  }
}

Sin embargo, aquí hay una diferencia. Como yo lo entiendo, el camino srcset se especifica es un poco como una sugerencia. El atributo y el valor proporcionan información sobre lo que está disponible y el navegador decide qué es lo mejor en ese momento. O al menos, podría, si los navegadores optaran por implementarlo de esa manera. Con un @media consulta, lo que se declara será.

Las consultas de medios de resolución están bastante bien soportadas:

Soporte del navegador para resolución de consultas de medios

También hay otra forma, que en realidad está más cerca de cómo srcset funciona, y eso es usar el image-set() función en CSS:

.img {
  background-image: url(examples/images/image-384.jpg);
  background-image: 
    -webkit-image-set(
      url(examples/images/image-384.jpg) 1x,
      url(examples/images/image-768.jpg) 2x,
    );
  background-image: 
    image-set(
      url(examples/images/image-384.jpg) 1x,
      url(examples/images/image-768.jpg) 2x,
    );
}

Tiene un poco menos de soporte que las consultas de resolución:

Soporte del navegador para conjunto de imágenes ()

Esta mucho mas cerca de srcset, no solo porque la sintaxis es similar, sino porque permite que el navegador tenga voz. Según la especificación (aún en borrador):

La función image-set () permite a un autor ignorar la mayoría de estos problemas, simplemente proporcionando múltiples resoluciones de una imagen y dejando que UA decida cuál es la más apropiada en una situación determinada.

No existe un reemplazo perfecto 1: 1 para srcset en CSS, pero esto está bastante cerca.

sizes en CSS

El sizes atributo en HTML está muy directamente relacionado con CSS. De hecho, básicamente dice: “Así es como pretendo ajustar el tamaño de esta imagen en CSS, solo te lo hago saber ahora mismo porque es posible que necesites esta información en este momento y no puedes esperar a que CSS se descargue primero”.

Muestra:

<img
  
  
  alt="…">

Lo que asume algo como esto en el CSS:

img {
  width: 100%;
}
@media (min-width: 40em) {
  /* Probably some parent element that limits the img width */
  main {
    width: 80%;
  }
}

Pero los tamaños por sí solos no hacen nada. Lo emparejas con srcset, que proporciona anchos conocidos, por lo que el navegador puede elegir. Supongamos solo un par de imágenes como:

<img
  
  srcset="https://css-tricks.com/examples/images/small.jpg 375w,
          https://css-tricks.com/examples/images/big.jpg 1500w"
  alt="…">

La información en el marcado anterior le da al navegador lo que necesita para encontrar la mejor imagen para él. El navegador conoce 1) su propio tamaño de ventana gráfica y 2) su propia densidad de píxeles.

Quizás la ventana del navegador tiene 320px de ancho y es una pantalla de 1x. Ahora también sabe que mostrará esta imagen a 100vw. Por lo que tiene que elegir entre las dos imágenes proporcionadas. Hace algunas matemáticas.

375 (tamaño de la imagen n. ° 1) / 320 (píxeles disponibles para mostrar la imagen) = 1,17
1500 (tamaño de la imagen n. ° 2) / 320 (píxeles disponibles para mostrar la imagen) = 4,69

1,17 está más cerca de 1 (es una pantalla de 1x), por lo que gana la imagen de 375w. Intentará no hundirse, por lo que 1.3 superaría a 0.99, según tengo entendido.

Ahora digamos que era una pantalla de 2x. Eso duplica la cantidad de píxeles necesarios para mostrar las imágenes, por lo que la matemática es:

375/640 = 0,59
1500/640 = 2,34

2.34 gana aquí, y mostrará la imagen de 1500w. ¿Qué tal una pantalla 1x con una ventana gráfica de 1200px?

375 / (80% de 1200) = 0,39
1500 / (80% de 1200) = 1,56

La imagen de 1500w gana aquí.

Esto es un poco extraño y complicado de escribir en CSS. Si solo pensamos en pantallas 1x, terminamos con una lógica como …

  • Si la ventana gráfica tiene menos de 375 px, usa la imagen de 375w.
  • Si la ventana gráfica es más grande que 375 px pero menos de 400 px, usa la imagen de 1500w (porque de lo contrario estaríamos ampliando la escala).
  • A 400px, la imagen se mueve a 80vw de ancho, por lo que es seguro usar la imagen de 375w por un poquito ((entre 400px y 468px)
  • Más de 468 px, usa la imagen de 1500w.

Que podríamos escribir como:

img {
  background-image: url(small.jpg);
}
/* Only override this if one of the conditions for the 1500w image is met */
@media 
  (min-width: 375px) and (max-width: 400px),
  (min-width: 468px) {
  main {
    background-image: url(https://css-tricks.com/large.jpg);
  }
}

En este caso exacto, una pantalla de 2x, incluso con un ancho realmente estrecho como 300px, todavía requiere 600px para obtener esa calidad mínima de 1.0, por lo que también agregaríamos eso a la lógica:

.img {
  background-image: url(small.jpg);
}
/* Only override this if one of the conditions for the 1500w image is met */
@media 
  (min-width: 375px) and (max-width: 400px),
  (min-width: 468px),
  (-webkit-min-device-pixel-ratio: 2), 
  (min-resolution: 192dpi) {
  .img {
    background-image: url(https://css-tricks.com/large.jpg);
  }
}

La complejidad de esto se dispara cuanto más puntos de corte (tamaños) y más imágenes proporcionadas. Y todavía no es una combinación perfecta para lo que pueden hacer las imágenes receptivas (en HTML), ya que no permite la discreción del navegador (por ejemplo, la posibilidad de que un navegador considere otros factores [i.e. bandwidth] para elegir una imagen).

picture en CSS

Un ejemplo:

<picture>
  <source  media="(min-width: 1000px)">
  <source  media="(min-width: 800px)">
  <img  alt="…">
</picture>

Este tipo de cosas es una conversión bastante sencilla a consultas de medios. Las consultas de medios exactas están ahí para copiar:

.img {
  background-image: url(https://css-tricks.com/medium.jpg);
}
@media (min-width: 800px) {
  .img {
    background-image: url(https://css-tricks.com/large.jpg);
  }
}
@media (min-width: 1000px) {
  .img {
    background-image: url(https://css-tricks.com/extrahttps://css-tricks.com/large.jpg);
  }
}

No es de extrañar, esto puede complicarse más, porque srcset puede hacer su cosa dentro del picture elemento también:

<picture>
  <source  media="(min-width: 800px)">
  <img  alt="…">
</picture>

Que se traduce en:

.img {
  background-image: url(small.jpg);
}
@media
  (-webkit-min-device-pixel-ratio: 2), 
  (min-resolution: 192dpi) {
  .img {
    background-image: url(https://css-tricks.com/medium.jpg);
  }
}
@media
  (min-width: 800px) {
  .img {
    background-image: url(https://css-tricks.com/large.jpg);
  }
}
@media
  (-webkit-min-device-pixel-ratio: 2) and (min-width: 800px), 
  (min-resolution: 192dpi) and (min-width: 800px) {
  .img {
    background-image: url(https://css-tricks.com/extrahttps://css-tricks.com/large.jpg);
  }
}

Nuevamente, esto solo está esperando a aumentar en complejidad a medida que agrega algunas imágenes y condiciones más. Un poco mejor con image-set():

.img {
  background-image: url(small.jpg);
  background-image: 
    -webkit-image-set(
      "small.jpg" 1x,
      "https://css-tricks.com/medium.jpg" 2x,
    );
  background-image: 
    image-set(
      "small.jpg" 1x,
      "https://css-tricks.com/medium.jpg" 2x,
    );
}
@media
  (min-width: 800px) {
  .img {
    background-image: url(https://css-tricks.com/large.jpg);
    background-image: 
      -webkit-image-set(
        "https://css-tricks.com/large.jpg" 1x,
        "https://css-tricks.com/extrahttps://css-tricks.com/large.jpg" 2x,
      );
    background-image: 
      image-set(
        "https://css-tricks.com/large.jpg" 1x,
        "https://css-tricks.com/extrahttps://css-tricks.com/large.jpg" 2x,
      );
  }
}

¿Pueden ayudar los mixins?

¿Probablemente? Me encantaría ver un descarado @mixin eso tomaría todos estos parámetros, tomaría en cuenta el tamaño de la imagen (que se da cuenta de sí mismo golpeando el disco), y escupe algo de calidad en código CSS de imágenes receptivas. Tal vez haya incluso una forma de combinar consultas de resolución y image-set() sintaxis?

¿Realmente estamos haciendo esto?

Tengo curiosidad por saber cuántas personas están manejando activamente imágenes receptivas en CSS. ¿Quizás incluso en una forma intermedia de simplemente intercambiar imágenes más grandes en grandes puntos de interrupción? Me pregunto si, debido a que picture / srcset a menudo está automatizado, eso en realidad tiene una tasa de adopción más alta que las imágenes receptivas en CSS.