Piedra y pluma representando la optimización de imágenes

Optimización de imágenes: Guía web

Si tus imágenes pesan como un elefante mojado, tu negocio sangra por mil cortes invisibles. Esto no va de plugins mágicos ni de “sube el JPG y ya”. Va de estrategia, cirugía y propósito. Bienvenido al frente.

Acto I — La mentira de la medianía

Dogma reproducido

  • “Sube las imágenes en JPG a 80% y activa lazy-loading: suficiente.”
  • “El CDN ya optimiza solo. No perdamos tiempo.”
  • “El diseño manda, luego ya comprimimos si hace falta.”

Autopsia

  • Síntoma visible: homepage con héroe a pantalla completa, carrusel de logos y tres tarjetas de producto. Carga “percibida” lenta en móvil; el scroll tartamudea; CLS salta cuando aparece el hero.
  • Causa raíz: imágenes exportadas desde Figma a 2x sin recorte de área útil; PNG donde bastaba JPG; sin width/height; sin srcset/sizes; logos en PNG con fondo blanco; héroe sin prioridad ni preload; uso de background-image para contenido esencial.
  • Cadena de decisiones: “tenemos prisa” → “ya lo arreglaremos” → “pon lazy-loading a todo” → “el CDN hará magia” → deuda creciendo y nadie midiendo.

Evidencia mínima

  • Peso total imágenes (home, móvil): 4,6 MB → coste de datos medio en 3G ≈ 0,03 € por visita en mercados caros.
  • LCP p75 (móvil): 4,1 s. Objetivo ≤ 2,5 s. Penalización SEO y conversión.
  • CLS: 0,19 por no fijar dimensiones. Umbral recomendado ≤ 0,1.

Smells

  • Todos los img sin width/height. CLS casi garantizado.
  • PNG para fotografías, y logos rasterizados a 2000 px “por si acaso”.
  • Un único src para todos los anchos; nada de srcset/sizes; “lazy” hasta para el héroe.

Contraejemplo rápido

  • Antes (5 líneas)
HTML

<img src="/img/hero.jpg" alt="Nuevo modelo">
<img src="/img/logo1.png">
<img src="/img/card.jpg">
<img src="/img/card2.jpg">
<img src="/img/card3.jpg">
  • Después (5 líneas)
HTML

<link rel="preload" as="image" 
imagesrcset="/img/hero-1200.avif 1200w, /img/hero-800.avif 800w" imagesizes="(max-width:768px) 100vw, 50vw">
<img src="/img/hero-1200.avif" srcset="/img/hero-800.avif 800w, /img/hero-1200.avif 1200w" sizes="100vw" width="1200" height="800" fetchpriority="high" alt="Nuevo modelo">
<img src="/img/logo1.svg" width="120" height="40" loading="lazy">
<img src="/img/card-600.webp" srcset="/img/card-400.webp 400w, /img/card-600.webp 600w" sizes="(max-width:600px) 100vw, 600px" loading="lazy" decoding="async">
<!-- LCP baja ~35–55% en móvil, CLS≈0 -->
  • Regla de salida: si te has reconocido aquí, pon precio al daño: cada 0,5 s de LCP perdido y cada MB extra es fricción que convierte menos y se posiciona peor. Ya no es estética; son euros.

Acto II — La verdad

Visión

  • Una imagen en la web no es un archivo: es una decisión de entrega. Optimizar no es “comprimir”, es hacer llegar la intención visual adecuada con el menor coste de tiempo, datos y CPU. Diseño decide el porqué; la ingeniería orquesta el cómo. La estrategia manda: formato, tamaño, prioridad, momento y canal.

Modelo operativo (cadena de mando)

  • Contenido → ¿vector, foto, decorativo, crítico para la tarea?
  • Formato → SVG/AVIF/WebP/JPEG/PNG según contenido y compatibilidad.
  • Tamañocrop semántico, múltiples anchos, densidades, srcset/sizes.
  • Entregapreload/fetchpriority, decoding, loading, caché/CDN.
  • Presentaciónwidth/height, aspect-ratio, object-fit, image-set en CSS.
  • Control → métricas: LCP/CLS/transfer size/hits de caché. Automatiza o muere.

Tácticas de campo

  • 1) Elegir formato por intención
    • Haz: SVG para logos/íconos; AVIF para fotos/hero; WebP como reserva; PNG solo si necesitas transparencia sin banding o píxeles nítidos.
    • No uses: PNG para fotografía; JPG para logos; AVIF con grano extremo en UGC altamente comprimido (artefactos feos).
  • 2) Responsive de verdad con srcset/sizes
    • Haz: usa descriptores w y un sizes honesto al layout. Declara width/height para CLS≈0.
    • No uses: un único 2x gigante “por si acaso”; sizes genérico que siempre devuelve el mayor recurso.
  • 3) Prioriza como si fuera una evacuación
    • Haz: preload del LCP con imagesrcset/imagesizes; fetchpriority=»high» en el LCP; decoding=»async» en lo demás.
    • No uses: loading=»lazy» en el LCP; preloads redundantes sin srcset (derroche).
  • 4) Fondo ≠ contenido
    • Haz: si es decorativo, background-image con image-set() y media queries.
    • No uses: fondos para contenido con significado; pierdes alt, semántica y SEO.
  • 5) Automatiza el pipeline
    • Haz: genera variantes (ancho/calidad/formato) con sharp, ImageMagick o el CDN; purga EXIF, convierte a sRGB, limita calidad por métrica (SSIM/DSSIM).
    • No uses: exportar a mano “a ojo” en cada sprint.

Demostración mínima (10 minutos, reproducible)

  • Objetivo: convertir una foto héroe 2400×1600 PNG (1,8 MB) a AVIF/WebP en 3 anchos (800/1200/1600), integrarla con preload, medir mejora.
  • Entrada: hero.png.
  • Pasos:
    • Instala sharp y genera variantes.
JS

import sharp from 'sharp';
const sizes = [800, 1200, 1600];
for (const w of sizes) {
  await sharp('hero.png').resize({ width: w }).toFormat('avif', { quality: 45 }).toFile(`hero-${w}.avif`);
  await sharp('hero.png').resize({ width: w }).toFormat('webp', { quality: 70 }).toFile(`hero-${w}.webp`);
}
  • Integra en HTML (LCP con prioridad, fallback a WebP).
HTML

<link rel="preload" as="image" imagesrcset="/img/hero-800.avif 800w, /img/hero-1200.avif 1200w, /img/hero-1600.avif 1600w" imagesizes="100vw">
<picture>
  <source type="image/avif" srcset="/img/hero-800.avif 800w, /img/hero-1200.avif 1200w, /img/hero-1600.avif 1600w" sizes="100vw">
  <source type="image/webp" srcset="/img/hero-800.webp 800w, /img/hero-1200.webp 1200w, /img/hero-1600.webp 1600w" sizes="100vw">
  <img src="/img/hero-1200.webp" width="1200" height="800" alt="Colección Otoño" fetchpriority="high">
</picture>
  • Fija el espacio y usa image-set solo en decorativos.
CSS

/* CLS a cero con dimensiones y relación */
.hero { aspect-ratio: 3 / 2; }
.banner--decorativo {
 background-image: 
image-set(url('/img/bg-1x.avif') 1x, 
url('/img/bg-2x.avif') 2x); 
background-size: 
cover; }
  • Salida esperada: peso del héroe ≈ 140–260 KB (según ancho), LCP p75 baja ~30–50% en móvil, CLS ≈ 0 por width/height y aspect-ratio.

Trade-offs

  • Más ficheros y rutas (varias variantes) → compensa con automatización y CDN.
  • Compatibilidad AVIF en navegadores antiguos → usa picture con fallback WebP/JPEG.
  • Preload mal usado puede derrochar ancho de banda → restringe al LCP y declara imagesizes.

Impacto en negocio

  • Time-to-change: cambiar una imagen global tarda minutos, no horas, al estar automatizado.
  • Coherencia de marca: nitidez/recorte consistentes; nada de logos borrosos ni fondos lavados.
  • Rendimiento/retención: menos rebote en móvil, mejor Core Web Vitals, mejor SEO y conversión.

Acto III — El manifiesto

Principios no negociables

  • Decide el propósito antes del formato.
  • SVG para identidad, AVIF/WebP para foto, PNG solo si está justificado.
  • Todo img lleva width y height o aspect-ratio.
  • El LCP tiene preload y fetchpriority=»high»; nada de lazy.
  • Siempre srcset + sizes con descriptores w, nunca un 2x único.
  • Recorta al motivo: no entregues píxeles invisibles.
  • Purgar EXIF, normalizar a sRGB y limitar calidad por métrica objetiva.
  • Automatiza la generación de variantes en CI/CD.
  • Decorativo en CSS, semántico en HTML con alt significativo.
  • Mide cada cambio: bytes, LCP, CLS, hit-ratio de caché.

Definición de Hecho (DoD)

  • Imágenes nuevas con srcset/sizes y width/height declarados.
  • Formato acorde a contenido (SVG/AVIF/WebP/JPEG/PNG) con fallback si procede.
  • Héroe y above the fold con preload + fetchpriority.
  • Decorativos sin texto embebido; alt vacíos si no aportan significado; alt descriptivo si lo tienen.
  • Pipeline genera ≥3 anchos y elimina metadatos; commit incluye script/config.
  • Se adjuntan métricas: tamaño transferido y efecto en LCP/CLS.

Métricas de guardarraíl

  • Peso total imágenes/home (móvil) ≤ 900 KB p75.
  • LCP p75 (móvil) ≤ 2,5 s.
  • CLS ≤ 0,1.
  • Hit-ratio CDN ≥ 85% para imágenes.
  • Variantes por imagen: al menos 3 anchos reales servidos.

Coto de caza (anti-patrones prohibidos)

  • PNG para fotografías “porque se ve mejor”. No entra.
  • Imágenes sin width/height. CLS garantizado: rechazado.
  • loading=»lazy» en el LCP. No.
  • Un único src gigante para todos los anchos. No.
  • Texto dentro de imágenes para titulares o CTA. Accesibilidad y SEO a la basura.

Plan de acción de 24 h

  • 1 Audita la home: lista de imágenes, uso (crítico/decorativo), formato, tamaño. Métrica: peso total actual.
  • 2 Monta script de variantes (sharp/ImageMagick) y genera 800/1200/1600 en AVIF+WebP. Métrica: bytes ahorrados.
  • 3 Implementa srcset/sizes y declara width/height en todos los img. Métrica: CLS tras cambio.
  • 4 Añade preload + fetchpriority al LCP. Métrica: LCP p75 en laboratorio (Lighthouse) y real si tienes RUM.
  • 5 Pasa logos a SVG o WebP nítido; mueve decorativos a CSS con image-set(). Métrica: número de recursos y caché CDN.

Cierre

  • Esta no es una guía de filtros y sliders. Es un sistema de decisiones con dientes. Apaga el piloto automático, mide, automatiza y entrega intención, no megabytes. El resto es ruido.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *