Una imagen no se maqueta como un párrafo. Llega con un tamaño propio —el del archivo—, y lo que el CSS hace es decidir cuánto sitio ocupa, qué parte de ella se ve y con qué aspecto se dibuja. Este documento recorre esas tres cosas por separado.
Lo que no se toca en ningún caso es el archivo. Todo lo que sigue es presentación: la foto que el navegador descarga es siempre la misma.
Un <img> es un elemento de tipo inline: se coloca dentro del
renglón, como una palabra más, en vez de ocupar un bloque propio. La consecuencia es que
debajo de la imagen aparece una franja de unos pocos píxeles que nadie ha pedido.
No es un margen. Es el espacio que toda línea de texto reserva para las letras que bajan
del renglón, la p o la g, y la imagen se apoya en ese renglón como si fuera
una letra más. Se quita con display: block, que la saca del renglón y la convierte en un
bloque.
la franja de debajo, y cómo se quita
<div class="caja"> <img src="IMG/naturaleza-valle.jpg" alt="Valle con niebla al amanecer"> </div> <div class="caja"> <img class="bloque" src="IMG/naturaleza-valle.jpg" alt="Valle con niebla al amanecer"> </div>
body {
font-family: Verdana, sans-serif;
font-size: .9rem;
line-height: 1.6;
}
.caja {
width: 14rem;
margin-bottom: 1rem;
background: #c2341f; /* el rojo que asoma es la franja */
}
.caja img { width: 100%; }
.bloque { display: block; }
El rojo que asoma bajo la primera es el fondo del contenedor. La segunda, con
display: block, no deja ninguno.
Una imagen trae su propio tamaño, el del archivo, y lo conserva aunque no quepa donde se la pone. Si la foto mide 3000 píxeles de ancho y el contenedor 400, la imagen no se achica sola: se sale de la caja y aparece una barra de desplazamiento horizontal.
Los dos recuadros de abajo miden lo mismo. En el primero la imagen va tal como viene; en
el segundo lleva max-width: 100%.
una imagen que no cabe
<p class="rotulo">Sin ninguna regla — la foto no cabe en el recuadro</p> <div class="ventana"> <div class="marco"> <p class="medida">Este recuadro mide 12rem</p> <img class="suelta" src="IMG/naturaleza-valle.jpg" alt="Valle con niebla al amanecer"> </div> </div> <p class="rotulo">Con max-width: 100% — se ajusta</p> <div class="marco"> <p class="medida">Este recuadro mide 12rem</p> <img class="ajustada" src="IMG/naturaleza-valle.jpg" alt="Valle con niebla al amanecer"> </div>
body {
font-family: Verdana, sans-serif;
font-size: .9rem;
line-height: 1.6;
}
.marco {
width: 12rem;
border: 2px solid #c2341f; /* el rojo marca dónde acaba el recuadro */
}
.rotulo { margin: 1rem 0 .3rem; font-size: .8rem; color: #555; }
/* el texto ocupa el ancho del recuadro: sirve de referencia */
.medida {
margin: 0;
padding: .2rem .4rem;
background: #c2341f;
color: #fff;
font-size: .75rem;
}
.suelta {
display: block; /* sin más: ocupa lo que mide el archivo */
}
.ajustada {
display: block;
max-width: 100%;
height: auto;
}
/* solo para que el ejemplo quepa en esta página: recorta lo que sobresale */
.ventana { height: 11rem; overflow: hidden; }
La primera atraviesa el borde rojo y sigue hacia la derecha. En una página real no hay nada que la detenga: se sale de la pantalla y el navegador pone una barra de desplazamiento horizontal para toda la página.
Se corrige con tres declaraciones, que conviene escribir juntas desde el primer día:
img {
max-width: 100%; /* nunca más ancha que su contenedor */
height: auto; /* el alto se recalcula solo */
display: block;
}
max-width y no width: con max-width la imagen se
achica si hace falta, pero no se estira por encima de su tamaño real, que la dejaría
borrosa. Y height: auto es imprescindible: sin él, al reducirse el ancho el
alto se queda como estaba y la foto se aplasta.
con y sin la regla, en un contenedor estrecho
<p class="rotulo">Sin height: auto — se deforma</p> <div class="marco"> <img class="mal" src="IMG/retrato-rizado.jpg" alt="Primer plano de mujer con pelo rizado"> </div> <p class="rotulo">Con max-width y height: auto</p> <div class="marco"> <img class="bien" src="IMG/retrato-rizado.jpg" alt="Primer plano de mujer con pelo rizado"> </div>
body {
font-family: Verdana, sans-serif;
font-size: .9rem;
line-height: 1.6;
}
.marco {
width: 12rem;
border: 1px solid #999;
margin-bottom: 1rem;
}
.rotulo { margin: 0 0 .3rem; font-size: .8rem; color: #555; }
.mal {
width: 100%;
height: 8rem; /* alto fijo: la foto se aplasta */
display: block;
}
.bien {
max-width: 100%;
height: auto;
display: block;
}
Conviene además escribir width y height en el HTML, con las
medidas reales del archivo:
<img src="IMG/valle.jpg" alt="Valle con niebla" width="1600" height="900">
No sirven para dar tamaño —de eso se ocupa el CSS— sino para que el navegador conozca la proporción antes de que la foto llegue y reserve el hueco. Sin ellos, el texto que hay debajo da un salto cuando la imagen termina de cargar.
Cuando la caja tiene unas medidas y la foto otra proporción, hay que decidir qué pasa.
object-fit tiene tres respuestas.
covercontainfillla misma foto y la misma caja, con los tres valores
<div class="row"> <div> <p class="rotulo">cover</p> <img class="hueco cover" src="IMG/bn-cupula.jpg" alt="Cúpula del Palacio de Bellas Artes"> </div> <div> <p class="rotulo">contain</p> <img class="hueco contain" src="IMG/bn-cupula.jpg" alt="Cúpula del Palacio de Bellas Artes"> </div> <div> <p class="rotulo">fill</p> <img class="hueco fill" src="IMG/bn-cupula.jpg" alt="Cúpula del Palacio de Bellas Artes"> </div> </div>
body {
font-family: Verdana, sans-serif;
font-size: .9rem;
line-height: 1.6;
}
.row { display: flex; flex-wrap: wrap; gap: 1rem; }
.rotulo { margin: 0 0 .3rem; font-size: .8rem; color: #555; }
.hueco {
display: block;
width: 9rem;
height: 7rem; /* la caja: más ancha que alta */
background: #e6e6ee; /* se ve donde la foto no llega */
}
.cover { object-fit: cover; }
.contain { object-fit: contain; }
.fill { object-fit: fill; }
Con cover el navegador recorta por el centro, y en un retrato eso puede
cortar la cabeza. object-position dice qué parte de la foto debe sobrevivir al
recorte. Funciona igual que background-position: dos valores, horizontal y
vertical, en palabras o en porcentaje.
el mismo recorte, mirando a tres sitios distintos
<div class="row"> <div> <p class="rotulo">top</p> <img class="hueco arriba" src="IMG/retrato-contraluz.jpg" alt="Retrato de mujer a contraluz"> </div> <div> <p class="rotulo">center (por defecto)</p> <img class="hueco centro" src="IMG/retrato-contraluz.jpg" alt="Retrato de mujer a contraluz"> </div> <div> <p class="rotulo">bottom</p> <img class="hueco abajo" src="IMG/retrato-contraluz.jpg" alt="Retrato de mujer a contraluz"> </div> </div>
body {
font-family: Verdana, sans-serif;
font-size: .9rem;
line-height: 1.6;
}
.row { display: flex; flex-wrap: wrap; gap: 1rem; }
.rotulo { margin: 0 0 .3rem; font-size: .8rem; color: #555; }
/* la foto es vertical y la caja apaisada: lo que sobra es alto,
y por eso object-position tiene algo que mover */
.hueco {
display: block;
width: 11rem;
aspect-ratio: 4 / 3;
object-fit: cover;
}
.arriba { object-position: top; }
.centro { object-position: center; }
.abajo { object-position: bottom; }
Dos avisos. object-position no hace nada sin un recorte previo: si la foto
cabe entera, no hay nada que desplazar. Y solo desplaza en la dirección en la que
sobra foto. Aquí la imagen es vertical y la caja apaisada, así que lo que sobra es
alto y top y bottom se notan. Con una foto apaisada en una caja
cuadrada ocurriría lo contrario: lo que sobra sería ancho, y habría que escribir
left o right.
Poner un alto en rem funciona hasta que la pantalla cambia de ancho:
entonces la caja se vuelve cada vez más apaisada. aspect-ratio declara la forma
en vez del alto, y el alto sale del ancho que haya.
tres proporciones, todas con cover
<div class="row"> <div class="col"> <p class="rotulo">1 / 1</p> <img class="p cuadrada" src="IMG/bn-caballo.jpg" alt="Caballo en blanco y negro"> </div> <div class="col"> <p class="rotulo">4 / 3</p> <img class="p clasica" src="IMG/bn-caballo.jpg" alt="Caballo en blanco y negro"> </div> <div class="col"> <p class="rotulo">3 / 4</p> <img class="p vertical" src="IMG/bn-caballo.jpg" alt="Caballo en blanco y negro"> </div> </div>
body {
font-family: Verdana, sans-serif;
font-size: .9rem;
line-height: 1.6;
}
.row { display: flex; flex-wrap: wrap; gap: 1rem; }
.col { flex: 1; }
.rotulo { margin: 0 0 .3rem; font-size: .8rem; color: #555; }
.p {
display: block;
width: 100%; /* el ancho lo da la columna */
object-fit: cover;
}
.cuadrada { aspect-ratio: 1 / 1; }
.clasica { aspect-ratio: 4 / 3; }
.vertical { aspect-ratio: 3 / 4; }
Estreche la ventana: las tres conservan su forma. Eso es lo que no se consigue con un alto fijo.
Una sola propiedad cubre desde una esquina suavizada hasta el círculo completo. El valor
es el radio de la curva; en porcentaje, se mide sobre el tamaño del elemento, de modo que
50% redondea hasta donde se puede: un círculo si la caja es cuadrada, una
elipse si no lo es.
cuatro formas con la misma propiedad
<div class="row"> <div> <p class="rotulo">sin nada</p> <img class="f" src="IMG/bn-caballo.jpg" alt="Caballo en blanco y negro"> </div> <div> <p class="rotulo">esquinas suaves</p> <img class="f suave" src="IMG/bn-caballo.jpg" alt="Caballo en blanco y negro"> </div> <div> <p class="rotulo">círculo</p> <img class="f circulo" src="IMG/bn-caballo.jpg" alt="Caballo en blanco y negro"> </div> <div> <p class="rotulo">una esquina</p> <img class="f gota" src="IMG/bn-caballo.jpg" alt="Caballo en blanco y negro"> </div> </div>
body {
font-family: Verdana, sans-serif;
font-size: .9rem;
line-height: 1.6;
}
.row { display: flex; flex-wrap: wrap; gap: 1rem; }
.rotulo { margin: 0 0 .3rem; font-size: .8rem; color: #555; }
.f {
display: block;
width: 8rem;
aspect-ratio: 1 / 1;
object-fit: cover;
}
.suave { border-radius: .8rem; }
.circulo { border-radius: 50%; }
/* cuatro valores: arriba-izq, arriba-der, abajo-der, abajo-izq */
.gota { border-radius: 2.5rem 0 2.5rem 0; }
Una miniatura es una imagen pequeña que representa a una imagen mayor, la cual se abre al pulsarla. Esa es la definición: sin el enlace no hay miniatura, solo una foto pequeña.
El marco no forma parte de la definición, pero es lo que habitualmente permite
reconocerla como miniatura. Son tres propiedades: border para la línea,
padding para el blanco entre la línea y la foto, y border-radius
para suavizar la esquina.
Conviene además que responda al ratón, porque se puede pulsar. Una sombra que aparece con
una transition basta.
El tamaño en pantalla lo fija el CSS con width, pero eso no cambia el archivo que se descarga. Poner
width: 10rem sobre una foto de 3000 píxeles hace que el navegador se baje los
dos megas enteros para dibujar 160 píxeles. Con doce miniaturas en una página son
veinticuatro megas para enseñar una cuadrícula.
Una miniatura existe precisamente para no cargar la grande. Así que son
dos archivos: el pequeño en el src y el grande en el
href.
<a href="IMG/arroyo.jpg">
<img src="IMG/arroyo-mini.jpg"
alt="Arroyo de montaña en un bosque de Trentino"
width="320" height="240">
</a>
El archivo pequeño se genera al preparar el sitio, con el mismo programa con que se retoca la foto o con una orden de consola. Conviene que sea del orden del doble del tamaño al que se va a ver, para que no se vea borrosa en una pantalla de alta densidad: para una miniatura de 160 píxeles, un archivo de 320.
El width y el height del HTML son los del archivo pequeño, y
sirven para reservar el hueco, como se vio al principio. El tamaño final lo sigue decidiendo
el CSS.
En el ejemplo de abajo el src apunta al archivo grande porque es el único
que hay en la carpeta. En un sitio real sería el reducido.
la misma miniatura, sin marco y con marco
<div class="row"> <a href="IMG/naturaleza-arroyo.jpg"> <img class="thumbnail" src="IMG/naturaleza-arroyo.jpg" alt="Arroyo de montaña en un bosque de Trentino"> </a> <a href="IMG/naturaleza-arroyo.jpg" class="enlace"> <img class="thumbnail enmarcada" src="IMG/naturaleza-arroyo.jpg" alt="Arroyo de montaña en un bosque de Trentino"> </a> </div> <p class="pie">Las dos son enlaces. Solo la segunda se reconoce como miniatura; pase el ratón por encima.</p>
body {
font-family: Verdana, sans-serif;
font-size: .9rem;
line-height: 1.6;
}
.row { display: flex; flex-wrap: wrap; gap: 1rem; align-items: start; }
.pie { font-size: .8rem; color: #555; }
.thumbnail {
display: block;
width: 10rem;
aspect-ratio: 4 / 3;
object-fit: cover;
}
/* el marco: nada de esto la hace miniatura, pero la hace reconocible */
.enmarcada {
border: 1px solid #ddd;
border-radius: .25rem;
padding: .3rem; /* el blanco entre el borde y la foto */
}
.enlace .thumbnail {
transition: box-shadow 250ms, border-color 250ms;
}
.enlace:hover .thumbnail {
border-color: #2a2a55;
box-shadow: 0 0 .4rem .1rem #9b9bc0;
}
El paso siguiente es envolver la foto en un contenedor con su propio fondo y un texto debajo. La sombra se le da al contenedor, no a la imagen, para que abarque también el pie.
box-shadow recibe cuatro medidas y un color: desplazamiento horizontal,
desplazamiento vertical, desenfoque y extensión. Dos sombras separadas por una coma
—una corta y densa, otra larga y difusa— imitan mejor cómo se comporta la luz real que una
sola.
una tarjeta con su pie
<div class="row"> <figure class="card"> <img src="IMG/ciudad-amanecer.jpg" alt="Rascacielos de Ciudad de México al amanecer"> <figcaption>Ciudad al amanecer</figcaption> </figure> <figure class="card"> <img src="IMG/naturaleza-arroyo.jpg" alt="Arroyo de montaña en un bosque de Trentino"> <figcaption>Arroyo de montaña</figcaption> </figure> </div>
body {
font-family: Verdana, sans-serif;
font-size: .9rem;
line-height: 1.6;
}
.row { display: flex; flex-wrap: wrap; gap: 1.2rem; }
.card {
width: 11rem;
margin: 0;
padding: .6rem;
background: #fff;
box-shadow: 0 .25rem .5rem #c4c4c4,
0 .4rem 1.2rem #dedede;
}
.card img {
display: block;
width: 100%;
aspect-ratio: 1 / 1;
object-fit: cover;
}
.card figcaption {
padding-top: .6rem;
text-align: center;
font-size: .8rem;
}
El elemento es <figure> y el pie <figcaption>,
que es el HTML que existe para esto: una imagen con su leyenda. Un <div>
se vería igual y diría menos.
filter aplica a la imagen el mismo tipo de ajuste que un programa de
retoque, pero en el navegador y sin tocar el archivo.
grayscale(100%)sepia(100%)blur(.2rem)brightness(), contrast(), saturate()100% aumentan; por debajo, reducen.hue-rotate(90deg)invert(100%)drop-shadow()los ocho filtros sobre la misma foto
<div class="galeria"> <div><img class="g" src="IMG/naturaleza-valle.jpg" alt="Valle con niebla"><p>sin filtro</p></div> <div><img class="g gris" src="IMG/naturaleza-valle.jpg" alt="Valle con niebla"><p>grayscale(100%)</p></div> <div><img class="g sepia" src="IMG/naturaleza-valle.jpg" alt="Valle con niebla"><p>sepia(100%)</p></div> <div><img class="g borroso" src="IMG/naturaleza-valle.jpg" alt="Valle con niebla"><p>blur(.15rem)</p></div> <div><img class="g claro" src="IMG/naturaleza-valle.jpg" alt="Valle con niebla"><p>brightness(140%)</p></div> <div><img class="g duro" src="IMG/naturaleza-valle.jpg" alt="Valle con niebla"><p>contrast(180%)</p></div> <div><img class="g vivo" src="IMG/naturaleza-valle.jpg" alt="Valle con niebla"><p>saturate(250%)</p></div> <div><img class="g girado" src="IMG/naturaleza-valle.jpg" alt="Valle con niebla"><p>hue-rotate(120deg)</p></div> <div><img class="g negativo" src="IMG/naturaleza-valle.jpg" alt="Valle con niebla"><p>invert(100%)</p></div> </div>
body {
font-family: Verdana, sans-serif;
font-size: .9rem;
line-height: 1.6;
}
.galeria {
display: grid;
gap: .7rem;
grid-template-columns: repeat(4, 1fr);
}
.galeria p {
margin: .3rem 0 0;
font-family: ui-monospace, Consolas, monospace;
font-size: .72rem;
color: #555;
}
.g {
display: block;
width: 100%;
aspect-ratio: 4 / 3;
object-fit: cover;
}
.gris { filter: grayscale(100%); }
.sepia { filter: sepia(100%); }
.borroso { filter: blur(.15rem); }
.claro { filter: brightness(140%); }
.duro { filter: contrast(180%); }
.vivo { filter: saturate(250%); }
.girado { filter: hue-rotate(120deg); }
.negativo { filter: invert(100%); }
Las dos dibujan una sombra, pero no de lo mismo. box-shadow es una propiedad
de la caja y proyecta el rectángulo del elemento.
filter: drop-shadow() proyecta lo que de verdad se ve, saltándose
las partes transparentes.
Sobre una fotografía rectangular las dos se ven igual. La diferencia aparece con un PNG o un SVG con transparencia, como estos iconos.
la misma sombra sobre una imagen con transparencia
<div class="row"> <div> <p class="rotulo">sin sombra</p> <img class="icono" src="IMG/bombilla-on.svg" alt="Bombilla encendida"> </div> <div> <p class="rotulo">box-shadow</p> <img class="icono caja" src="IMG/bombilla-on.svg" alt="Bombilla encendida"> </div> <div> <p class="rotulo">filter: drop-shadow</p> <img class="icono silueta" src="IMG/bombilla-on.svg" alt="Bombilla encendida"> </div> </div>
body {
font-family: Verdana, sans-serif;
font-size: .9rem;
line-height: 1.6;
}
.row { display: flex; flex-wrap: wrap; gap: 1.5rem; }
.rotulo { margin: 0 0 .5rem; font-size: .8rem; color: #555; }
.icono {
display: block;
width: 5rem;
height: auto;
}
/* la sombra del rectángulo, aunque la imagen sea transparente */
.caja {
box-shadow: .3rem .3rem .5rem #999;
}
/* la sombra de la silueta */
.silueta {
filter: drop-shadow(.3rem .3rem .3rem #999);
}
En la segunda se ve el recuadro del archivo, que no debería verse. En la tercera, la
sombra sigue el contorno de la bombilla. Para iconos y logotipos recortados la buena es
siempre drop-shadow.
Los filtros se pueden encadenar separándolos por espacios, y se aplican en el orden escrito:
filter: grayscale(100%) contrast(130%) brightness(90%);
Un filtro es una propiedad animable, así que admite transition. El recurso
más común en una galería es tener las fotos en blanco y negro y devolverles el color cuando
el ratón pasa por encima.
el color vuelve al pasar el ratón
<div class="row"> <img class="apagada" src="IMG/naturaleza-arroyo.jpg" alt="Arroyo de montaña en un bosque de Trentino"> <img class="apagada" src="IMG/ciudad-amanecer.jpg" alt="Rascacielos de Ciudad de México al amanecer"> <img class="apagada" src="IMG/retrato-rizado.jpg" alt="Primer plano de mujer con pelo rizado"> </div>
body {
font-family: Verdana, sans-serif;
font-size: .9rem;
line-height: 1.6;
}
.row { display: flex; flex-wrap: wrap; gap: .8rem; }
.apagada {
display: block;
width: 9rem;
aspect-ratio: 1 / 1;
object-fit: cover;
filter: grayscale(100%);
transition: filter 400ms;
}
.apagada:hover {
filter: grayscale(0%);
}
Una imagen de tipo block se centra como cualquier otro elemento de tipo block: con
márgenes laterales automáticos. No funciona mientras siga siendo de tipo inline,
así que display: block va primero.
img {
display: block;
margin-inline: auto; /* margen izquierdo y derecho automáticos */
max-width: 100%;
}
Si la imagen está dentro de un contenedor flex o grid, no hace falta nada de esto:
justify-content: center en el padre hace el trabajo.
Una foto puede ponerse de dos maneras, y la elección no es de gusto.
<img> | background-image |
|
|---|---|---|
| Qué es | Contenido del documento | Decoración de una caja |
| Texto alternativo | Sí, con alt |
No existe |
| La encuentra un buscador | Sí | No |
| Sale al imprimir | Sí | A menudo no |
| Encaje en la caja | object-fit |
background-size |
| Para qué sirve | Una fotografía que importa | Texturas, degradados, adornos |
La regla práctica: si quitando la imagen la página pierde información, es un
<img>. Si solo pierde adorno, es un fondo.
display: block en las imágenes, siempre: un <img> nace
de tipo inline, y eso deja una franja debajo e impide centrarlo con
márgenes automáticos.max-width: 100% con height: auto. Un alto fijo deforma.width y height en el HTML reservan el hueco y evitan el
salto al cargar; el tamaño lo sigue dando el CSS.object-fit: cover llena sin deformar; object-position decide
qué trozo se conserva.aspect-ratio en lugar de un alto fijo.border-radius: 50% da un círculo solo si la caja es cuadrada.filter es animable, de ahí el blanco y negro que recupera el color.<img> con su alt; lo que es
adorno, en background-image.Las fotos son de Pexels, con licencia libre: se pueden usar y modificar sin pagar y sin citar la fuente. Citarla no es obligatorio, pero es lo correcto, y aquí sirve además de ejemplo de cómo se hace.
naturaleza-arroyo.jpg — Arroyo de montaña en un bosque de Trentino. Foto de Pluto91 Operatic, ficha en Pexels.naturaleza-valle.jpg — Valle con niebla al amanecer. Foto de Johannes Plenio, ficha en Pexels.retrato-contraluz.jpg — Retrato de mujer a contraluz. Foto de Asep Awaludin, ficha en Pexels.retrato-rizado.jpg — Primer plano de mujer con pelo rizado. Foto de Eric Quinones, ficha en Pexels.bn-cupula.jpg — Cúpula del Palacio de Bellas Artes en blanco y negro. Foto de ἐμμανυελ, ficha en Pexels.bn-caballo.jpg — Caballo en blanco y negro. Foto de Ruhul Amin Akash, ficha en Pexels.ciudad-amanecer.jpg — Rascacielos de Ciudad de México al amanecer. Foto de Yuriko Salazar Luna, ficha en Pexels.