html-css-imagenes-flexibles-multimedia

Imágenes responsive en HTML y CSS

  • 6 min

Una imagen flexible es un recurso que se adapta a su contenedor sin romper el diseño ni deformarse.

Cuando hacemos una web responsive, el texto suele portarse bastante bien. Las líneas se parten, los párrafos bajan, y todo más o menos encuentra su sitio.

Las imágenes y los vídeos, en cambio, tienen más carácter. Si una imagen mide 1600px de ancho y el móvil tiene 390px, el navegador no va a encogerla por educación si no se lo pedimos.

Y entonces aparece el enemigo clásico del responsive: el scroll horizontal. Esa barrita inferior que dice algo se ha salido por la derecha y nadie sabe exactamente qué.

Imágenes fluidas

El primer paso es asegurarnos de que las imágenes, vídeos e iframes no superen el ancho de su contenedor.

img,
video,
iframe {
  max-width: 100%;
  height: auto;
}
Copied!

Con max-width: 100%, el elemento puede medir menos que su tamaño original si no cabe. Con height: auto, conserva sus proporciones y no se aplasta.

<img src="paisaje.jpg" alt="Sendero entre montañas al atardecer">
Copied!

Si la imagen original mide 1200px y el contenedor mide 360px, el navegador la dibuja a 360px de ancho manteniendo su relación de aspecto. No se deforma y no rompe la página.

Esta regla suele ir en los estilos base del proyecto. Es pequeña, y muy útil.

Reservar espacio para evitar saltos

Un problema frecuente es que la página carga el texto primero y, cuando llega la imagen, todo salta hacia abajo. Eso se llama layout shift, y es bastante molesto.

Para evitarlo, conviene indicar dimensiones o usar aspect-ratio.

<img
  src="producto.jpg"
  alt="Botella reutilizable de acero azul"
  width="800"
  height="600"
>
Copied!

Aunque luego CSS la adapte, esos atributos ayudan al navegador a reservar el hueco correcto antes de descargar la imagen.

También podemos fijar una proporción desde CSS.

.imagen-card {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
Copied!

Así la tarjeta mantiene una altura estable, aunque la imagen tarde un poco en aparecer.

Recortes con object-fit

A veces no queremos que la imagen cambie libremente de altura. Por ejemplo, en una tarjeta quizá necesitamos que todas las fotos ocupen un rectángulo exacto, aunque cada imagen original tenga proporciones distintas.

Si ponemos width y height sin más, la imagen puede deformarse.

.avatar {
  width: 200px;
  height: 200px;
}
Copied!

Si la foto original es panorámica, la cara puede acabar aplastada. Y nadie quiere un avatar que parezca pasado por una prensa hidráulica.

Para resolverlo usamos object-fit.

.avatar {
  width: 200px;
  height: 200px;
  object-fit: cover;
  border-radius: 50%;
}
Copied!
  • fill: Rellena la caja deformando si hace falta. Es el valor por defecto.
  • contain: Muestra toda la imagen sin recortar, aunque sobren bandas.
  • cover: Rellena toda la caja recortando lo que sobre.
  • none: No escala la imagen.
  • scale-down: Usa el menor resultado entre none y contain.

En interfaces modernas usaréis muchísimo cover, porque permite crear miniaturas limpias y consistentes.

Controlar el punto de recorte

object-fit: cover recorta la imagen para rellenar la caja. El recorte se centra por defecto, pero podemos controlar el punto importante con object-position.

.foto-persona {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  object-position: center top;
}
Copied!

Esto es útil cuando queremos conservar la cara de una persona, el producto principal o una zona concreta de una fotografía.

.banner {
  width: 100%;
  height: 320px;
  object-fit: cover;
  object-position: 70% center;
}
Copied!

Con esto decimos que el punto importante está algo desplazado hacia la derecha.

srcset: imagenes en varias resoluciones

Lo que hemos visto anteriormente resuelve el problema visual. Pero también debemos hablar de los problemas de rendimiento.

Si tenemos una imagen enorme para escritorio, no tiene sentido que un móvil descargue esa misma imagen para mostrarla a tamaño pequeño.

Para eso tenemos srcset, que permite ofrecer varias versiones de la misma imagen.

<img
  src="foto-800.jpg"
  srcset="
    foto-400.jpg 400w,
    foto-800.jpg 800w,
    foto-1600.jpg 1600w
  "
  sizes="(max-width: 600px) 100vw, 50vw"
  alt="Paisaje de montaña con un lago al fondo"
>
Copied!

Cada 400w, 800w o 1600w indica el ancho real de ese archivo. El atributo sizes le dice al navegador qué tamaño ocupará la imagen en el layout.

Sin sizes, el navegador asume por defecto que la imagen ocupará 100vw. A veces acierta, pero si la imagen va en una columna de media pantalla, puede descargar una versión más grande de lo necesario.

<picture> para recortes distintos

srcset sirve para entregar la misma imagen en distintos tamaños. La etiqueta <picture> sirve cuando queremos imágenes distintas según el contexto.

Por ejemplo, una foto panorámica puede funcionar genial en escritorio, pero en móvil quizá deja al protagonista diminuto en una esquina.

<picture>
  <source
    media="(max-width: 799px)"
    srcset="montana-vertical.jpg"
  >
  <source
    media="(min-width: 800px)"
    srcset="montana-panoramica.jpg"
  >
  <img
    src="montana-panoramica.jpg"
    alt="Ruta de montaña con una persona caminando por el sendero"
  >
</picture>
Copied!

Esto se llama a veces arte direccional: no solo cambiamos el peso del archivo, también cambiamos el encuadre para que la imagen comunique bien en cada pantalla.

El <img> final dentro de <picture> no es opcional. Es el contenido de respaldo y también donde ponemos el alt. Si lo quitáis, la estructura queda coja.

Vídeos e iframes responsive

Los vídeos también deben adaptarse. Para un <video> normal, la misma regla base ayuda bastante.

video {
  max-width: 100%;
  height: auto;
}
Copied!

Para vídeos embebidos con <iframe>, como YouTube, hoy podemos usar aspect-ratio.

.video-responsive {
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
}
Copied!
<iframe
  class="video-responsive"
  src="https://www.youtube.com/embed/VIDEO_ID"
  title="Vídeo del curso"
  allowfullscreen
></iframe>
Copied!

Antes se usaban envoltorios con padding-bottom. Funcionaban, pero eran un poco más aparatosos. aspect-ratio nos deja hacerlo de forma más clara.

Carga diferida y prioridad

Las imágenes pueden pesar mucho. Si una imagen está lejos, debajo del primer pantallazo, no hace falta descargarla inmediatamente.

<img
  src="galeria-01.jpg"
  alt="Detalle de una interfaz responsive en móvil"
  loading="lazy"
>
Copied!

loading="lazy" le dice al navegador que puede retrasar la carga hasta que la imagen esté cerca de entrar en pantalla.

Para la imagen principal de la parte superior, en cambio, quizá queremos justo lo contrario: darle prioridad.

<img
  src="hero.jpg"
  alt="Página web abierta en un portátil y un móvil"
  fetchpriority="high"
>
Copied!

No pongáis loading="lazy" a la imagen principal que se ve nada más entrar. Si es lo primero que el usuario necesita ver, interesa cargarla pronto.