El elemento <picture>
no renderiza nada por sí solo, sino que actúa como un motor de decisión para un elemento <img>
interno y le indica qué renderizar. <picture>
sigue un precedente ya establecido por los elementos <audio>
y <video>
: un elemento wrapper que contiene elementos <source>
individuales.
<picture>
<source …>
<source …>
<img …>
</picture …>
Ese <img>
interno también te proporciona un patrón de resguardo confiable para navegadores más antiguos sin compatibilidad con imágenes responsivas: si el navegador del usuario no reconoce el elemento <picture>
, se ignorará. Luego, los elementos <source>
también se descartan, ya que el navegador no los reconocerá o no tendrá un contexto significativo para ellos sin un elemento superior <video>
o <audio>
.
Sin embargo, cualquier navegador reconocerá el elemento <img>
interno y la fuente especificada en su src
se renderizará como se espera.
Imágenes “dirigidas con el arte” con <picture>
Hacer cambios en el contenido o la relación de aspecto de una imagen según el tamaño de la imagen en la página, por lo general, se conoce como imágenes responsivas "dirigidas con el arte". srcset
y sizes
están diseñados para funcionar de forma invisible, intercambiando fuentes sin problemas según lo indique el navegador del usuario.
Sin embargo, en algunas ocasiones, es posible que quieras alterar las fuentes entre los puntos de interrupción para destacar mejor el contenido, de la misma manera que adaptas los diseños de página.
Por ejemplo, una imagen de encabezado de ancho completo con un enfoque central pequeño puede funcionar bien en un viewport grande:
Sin embargo, cuando se reduce la escala para adaptarse a pequeñas viewports, el enfoque central de la imagen puede perderse:
![Una imagen de ancho de encabezado de una flor color bígaro, reducida. La abeja apenas se puede ver.](https://web.dev/static/learn/images/prescriptive/image/a-header-width-image-a-p-a272cd48c084e.png?authuser=6&hl=es)
El sujeto de estas fuentes de imagen es el mismo, pero para enfocarte mejor en ese tema visualmente, querrás que las proporciones de la fuente de la imagen cambien entre los puntos de interrupción. Por ejemplo, un zoom más ajustado en el centro de la imagen y algunos de los detalles en los bordes recortados:
Ese tipo de "recorte" se puede lograr a través de CSS, pero dejaría al usuario solicitando todos los datos que componen esa imagen, aunque nunca termine viéndola.
Cada elemento source
tiene atributos que definen las condiciones para la selección de ese source
: media
, que acepta una consulta de contenido multimedia, y type
, que acepta un tipo de medio (antes conocido como "tipo MIME"). Se seleccionará el primer elemento <source>
en el orden de origen para que coincida con el contexto de navegación actual del usuario, y el contenido del atributo srcset
en ese source
se usará para determinar los candidatos adecuados para ese contexto. En este ejemplo, se seleccionará el primer objeto source
con un atributo media
que coincida con el tamaño del viewport del usuario:
<picture>
<source media="(min-width: 1200px)" srcset="wide-crop.jpg">
<img src="close-crop.jpg" alt="…">
</picture>
Siempre debes especificar el img
interno en el último orden. Si ninguno de los elementos source
coincide con sus criterios media
o type
, la imagen actuará como una fuente "predeterminada". Si usas consultas de medios min-width
, debes tener las fuentes más grandes primero, como se ve en el código anterior. Cuando uses consultas de medios max-width
, debes colocar primero la fuente más pequeña.
<picture>
<source media="(max-width: 400px)" srcset="mid-bp.jpg">
<source media="(max-width: 800px)" srcset="high-bp.jpg">
<img src="highest-bp.jpg" alt="…">
</picture>
Cuando se elige una fuente según los criterios que especificaste, el atributo srcset
de source
se pasa a <img>
como si se hubiera definido en <img>
, lo que significa que también puedes usar sizes
para optimizar las fuentes de imágenes dirigidas por arte.
<picture>
<source media="(min-width: 800px)" srcset="high-bp-1600.jpg 1600w, high-bp-1000.jpg 1000w">
<source srcset="lower-bp-1200.jpg 1200w, lower-bp-800.jpg 800w">
<img src="fallback.jpg" alt="…" sizes="calc(100vw - 2em)">
</picture>
Por supuesto, una imagen con proporciones que pueden variar según el elemento <source>
seleccionado genera un problema de rendimiento: <img>
solo admite un único atributo width
y height
, pero omitir esos atributos puede generar una experiencia del usuario mucho peor.
Para explicarlo, una adición relativamente reciente, pero bien compatible, a la especificación HTML permite el uso de atributos height
y width
en elementos <source>
. Funcionan a fin de reducir los cambios de diseño tan bien como lo hacen en <img>
, con el espacio apropiado reservado en tu diseño para cualquier elemento <source>
seleccionado.
<picture>
<source
media="(min-width: 800px)"
srcset="high-bp-1600.jpg 1600w, high-bp-1000.jpg 1000w"
width="1600"
height="800">
<img src="fallback.jpg"
srcset="lower-bp-1200.jpg 1200w, lower-bp-800.jpg 800w"
sizes="calc(100vw - 2em)"
width="1200"
height="750"
alt="…">
</picture>
Es importante tener en cuenta que la dirección artística no solo se puede usar para tomar decisiones basadas en el tamaño del viewport; dado que la mayoría de estos casos se pueden manejar de manera más eficiente con srcset
o sizes
. Por ejemplo, seleccionar una fuente de imagen que se adapte mejor al esquema de colores que dicta la preferencia del usuario:
<picture>
<source media="(prefers-color-scheme: dark)" srcset="hero-dark.jpg">
<img srcset="hero-light.jpg">
</picture>
El atributo type
El atributo type
te permite usar el motor de decisión de solicitud única del elemento <picture>
para publicar solo formatos de imagen en los navegadores compatibles.
Como aprendiste en Formatos de imagen y compresión, una codificación que el navegador no puede analizar ni siquiera se podrá reconocer como datos de imagen.
Antes de la introducción del elemento <picture>
, las soluciones de frontend más viables para entregar formatos de imagen nuevos requerían que el navegador solicitara y tratara de analizar un archivo de imagen antes de determinar si desecharlo y cargar un resguardo. Un ejemplo común era una secuencia de comandos como estas:
<img src="image.webp"
data-fallback="image.jpg"
onerror="this.src=this.getAttribute('data-fallback'); this.onerror=null;"
alt="...">
Con este patrón, aún se realizaría una solicitud de image.webp
en todos los navegadores, lo que significa que se desperdiciaba una transferencia para los navegadores que no son compatibles con WebP. En los navegadores en los que no se pudo analizar la codificación WebP, se generaba un evento onerror
y se cambiaba el valor data-fallback
por src
. Fue una solución que no generó un desperdicio, pero, de nuevo, los enfoques como este eran la única opción disponible en el frontend. Recuerda que el navegador comienza a realizar solicitudes de imágenes antes de que se pueda ejecutar una secuencia de comandos personalizada (o incluso que se analice), por lo que no podríamos interrumpir este proceso.
El elemento <picture>
está diseñado de forma explícita para evitar esas solicitudes redundantes. Si bien no hay forma de que un navegador reconozca un formato que no admite sin solicitarlo, el atributo type
advierte al navegador sobre las codificaciones de origen de antemano, para que pueda decidir si realizar o no una solicitud.
En el atributo type
, proporcionas el tipo de medio (antes llamado tipo de MIME) de la fuente de la imagen especificada en el atributo srcset
de cada <source>
. De esta manera, el navegador recibe toda la información que necesita para determinar de inmediato si la imagen candidata que proporcionó ese source
se puede decodificar sin realizar solicitudes externas. Si no se reconoce el tipo de medio, se ignoran <source>
y todos sus candidatos, y el navegador continúa.
<picture>
<source type="image/webp" srcset="pic.webp">
<img src="pic.jpg" alt="...">
</picture>
Aquí, cualquier navegador compatible con la codificación WebP reconocerá el tipo de medio image/webp
especificado en el atributo type
del elemento <source>
, seleccionará ese <source>
y, como solo proporcionamos un único candidato en srcset
, indicará al <img>
interno que solicite, transfiera y renderice pic.webp
. Cualquier navegador sin compatibilidad con WebP ignorará el source
y, si no se indica ninguna instrucción que indique lo contrario, <img>
renderizará el contenido de src
como lo hace desde 1992.
No es necesario especificar un segundo elemento <source>
con type="image/jpeg"
aquí, por supuesto, puedes suponer la compatibilidad universal con JPEG.
Sin importar el contexto de navegación del usuario, todo esto se logra con una sola transferencia de archivos y no se desperdicia ancho de banda en fuentes de imágenes que no se pueden renderizar. Esto también es visionario, dado que los formatos de archivo más nuevos y eficientes incluirán tipos de medios propios, y podremos aprovecharlos gracias a picture
, sin JavaScript, sin dependencias del servidor y con toda la velocidad de <img>
.
El futuro de las imágenes responsivas
Todos los patrones de lenguaje de marcado que se analizaron aquí fueron un gran trabajo en términos de estandarización: cambiar la funcionalidad de algo tan establecido y fundamental para la Web como <img>
no fue poca tarea, y el conjunto de problemas que intentaban resolver esos cambios fue, por cierto, mucho más amplio. Si crees que hay mucho por mejorar con estos patrones de lenguaje de marcado, estás absolutamente en lo correcto. Desde el principio, estos estándares se diseñaron como punto de referencia para que las tecnologías futuras puedan basarse.
Todas estas soluciones han dependido necesariamente del lenguaje de marcado para que se incluyan en la carga útil inicial del servidor y lleguen a tiempo para que el navegador solicite las fuentes de imágenes, una limitación que llevó al atributo sizes
, presuntamente difícil de manejar.
Sin embargo, desde que estas funciones se introdujeron en la plataforma web, se introdujo un método nativo para diferir las solicitudes de imágenes.
Los elementos <img>
con el atributo loading="lazy"
no se solicitan hasta que se conozca el diseño de la página, a fin de diferir las solicitudes de imágenes fuera del viewport inicial del usuario hasta más adelante en el proceso de renderización de la página, lo que podría evitar solicitudes innecesarias. Como el navegador entiende por completo el diseño de la página en el momento en que se realizan estas solicitudes, se propuso un atributo sizes="auto"
como adición a la especificación HTML para evitar la tarea de escribir atributos sizes
escritos manualmente en estos casos.
También se agregará el elemento <picture>
en el horizonte para hacer coincidir algunos cambios excepcionalmente emocionantes en la forma en que damos estilo a los diseños de página. Si bien la información de viewport es una base sólida para las decisiones de diseño de alto nivel, nos impide adoptar un enfoque de desarrollo a nivel de todos los componentes, es decir, un componente que se puede incluir en cualquier parte del diseño de una página, con estilos que responden al espacio que ocupa el componente en sí. Este problema generó la creación de consultas de contenedor, un método para aplicar diseño a los elementos en función del tamaño de su contenedor superior, en lugar de solo el viewport.
Si bien la sintaxis de la consulta de contenedores se acaba de estabilizar (y la compatibilidad con el navegador es muy limitada al momento de escribirlo), la incorporación de las tecnologías de navegador que la habiliten proporcionará al elemento <picture>
una forma de hacer lo mismo: un posible atributo container
que permita criterios de selección <source>
según el espacio que ocupa el <img>
del elemento <picture>
, en lugar del tamaño del viewport.
Si esto suena un poco poco claro, hay una buena razón: estos debates sobre los estándares web son constantes, pero están lejos de estar resueltos: aún no los puedes usar.
Si bien el lenguaje de marcado de imágenes responsiva promete facilitar el trabajo con el paso del tiempo, como con cualquier tecnología web, hay una serie de servicios, tecnologías y frameworks que ayudan a aliviar la carga de escribir a mano este lenguaje de marcado disponible. En el próximo módulo, veremos cómo integrar todo lo que aprendimos sobre formatos de imagen, imágenes responsivas y compresión en un flujo de trabajo de desarrollo moderno.