Módulo 09: Modelo de cajas y posicionamiento
Objetivos del módulo
- Comprender que cada elemento HTML es una caja con contenido, relleno, borde y margen
- Usar
box-sizing: border-boxy entender por qué es el estándar de facto - Controlar tamaños con
width,height,min-*ymax-* - Centrar elementos con
margin: autoy entender el colapso de márgenes - Dar forma a las cajas con
border,border-radius,outlineybox-shadow - Distinguir los tipos de
displayy las formas de ocultar elementos - Gestionar el desbordamiento con
overflow - Posicionar elementos con
positiony controlar su apilamiento conz-index - Conocer
floaty el clearfix por razones históricas - Adaptar imágenes con
object-fity mantener proporciones conaspect-ratio
1. Todo es una caja
📘 Concepto: Para el navegador, cada elemento HTML es una caja rectangular. Un párrafo, un enlace, una imagen, incluso el
<body>. Maquetar con CSS consiste, en el fondo, en decidir el tamaño de esas cajas, el espacio entre ellas y dónde se colocan.
Piensa en un cuadro colgado en la pared: la imagen es el contenido, el paspartú blanco que la rodea es el relleno, el marco es el borde y la distancia a la pared de al lado es el margen.
┌──────────────────────────────────────────────┐
│ MARGIN │ ← espacio EXTERIOR, transparente
│ ┌──────────────────────────────────────┐ │
│ │ BORDER │ │ ← el borde (visible si tiene estilo)
│ │ ┌──────────────────────────────┐ │ │
│ │ │ PADDING │ │ │ ← espacio INTERIOR, toma el fondo
│ │ │ ┌──────────────────────┐ │ │ │
│ │ │ │ CONTENT │ │ │ │ ← texto, imagen, hijos...
│ │ │ │ (width × height) │ │ │ │
│ │ │ └──────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────┘
| Capa | Propiedad | ¿Tiene fondo? | Descripción |
|---|---|---|---|
| Contenido | width, height | Sí | Donde está el texto o los hijos |
| Relleno | padding | Sí (el del elemento) | Espacio entre el contenido y el borde |
| Borde | border | Tiene su propio color | Línea alrededor del relleno |
| Margen | margin | No (transparente) | Espacio entre esta caja y las demás |
💡 Consejo: En DevTools, al seleccionar un elemento, la pestaña Computed (y el final de Styles) muestra este mismo diagrama con los valores reales. Al pasar el ratón por un elemento, el navegador colorea su contenido (azul), relleno (verde) y margen (naranja).
2. padding y margin
Ambas propiedades admiten entre uno y cuatro valores, que siguen el sentido de las agujas del reloj empezando por arriba: arriba, derecha, abajo, izquierda.
.caja {
padding: 20px; /* 1 valor: los cuatro lados */
padding: 10px 20px; /* 2 valores: vertical | horizontal */
padding: 10px 20px 30px; /* 3 valores: arriba | horizontal | abajo */
padding: 10px 20px 30px 40px; /* 4 valores: arriba | derecha | abajo | izquierda */
}
/* Lados individuales */
.caja {
margin-top: 1rem;
margin-right: 0;
margin-bottom: 2rem;
margin-left: 0;
}
💡 Consejo: Para recordar el orden de cuatro valores, piensa en la palabra TRouBLe (Top, Right, Bottom, Left): si te equivocas, tendrás problemas.
Propiedades lógicas
CSS moderno ofrece versiones lógicas que no dependen de la dirección física, sino de la del texto. En español (de izquierda a derecha y de arriba a abajo), inline es horizontal y block es vertical:
.caja {
padding-block: 1rem; /* arriba y abajo */
padding-inline: 2rem; /* izquierda y derecha */
margin-inline: auto; /* centrado horizontal */
margin-block-end: 1rem; /* margen inferior */
}
Las verás con más detalle en el módulo 14; tienen la ventaja de funcionar automáticamente en idiomas que se escriben de derecha a izquierda, como el árabe.
Diferencias clave
- El padding forma parte de la caja: toma su color de fondo y aumenta el área donde se puede hacer clic (en un botón, un padding generoso es más accesible).
- El margin es exterior y transparente. Admite valores negativos (para solapar elementos) y el valor
auto. - El padding no admite valores negativos.
3. box-sizing: el gran cambio
Por defecto (box-sizing: content-box), width y height definen solo el contenido. El padding y el borde se suman por fuera. Esto lleva a cálculos poco intuitivos:
.caja {
width: 300px;
padding: 20px;
border: 5px solid black;
}
/* Ancho visible con content-box: 300 + 20 + 20 + 5 + 5 = 350px 😱 */
Con box-sizing: border-box, width incluye contenido + padding + borde. Si dices 300px, la caja mide 300px:
.caja {
box-sizing: border-box;
width: 300px;
padding: 20px;
border: 5px solid black;
}
/* Ancho visible: 300px. El contenido se ajusta solo: 300 - 40 - 10 = 250px */
box-sizing | width mide… | Ancho final con width: 300px; padding: 20px; border: 5px |
|---|---|---|
content-box (por defecto) | Solo el contenido | 350px |
border-box | Contenido + padding + borde | 300px |
Este es el motivo por el que todos los proyectos profesionales (y Bootstrap) empiezan con:
*,
*::before,
*::after {
box-sizing: border-box;
}
💻 Ejemplo: Dos columnas al 50 % con padding. Con
content-boxmiden “50 % + padding” cada una, suman más del 100 % y la segunda baja a la línea siguiente. Conborder-boxcaben perfectamente una al lado de la otra.
⚠️ Importante: El margen nunca se incluye en
width, ni siquiera conborder-box. Es espacio exterior a la caja.
4. Tamaños: width, height, min-* y max-*
.tarjeta {
width: 100%; /* ocupa el ancho del contenedor... */
max-width: 400px; /* ...pero nunca más de 400px */
min-height: 200px; /* al menos 200px de alto, crece si el contenido lo necesita */
}
| Propiedad | Uso |
|---|---|
width / height | Tamaño fijo o relativo |
min-width / min-height | Tamaño mínimo: la caja puede crecer |
max-width / max-height | Tamaño máximo: la caja puede encoger |
Valor auto | El navegador calcula el tamaño (por defecto) |
fit-content, min-content, max-content | Tamaño según el contenido |
El comportamiento por defecto de los bloques
- Ancho: un elemento de bloque con
width: autoocupa todo el ancho disponible de su contenedor. - Alto: con
height: auto, mide lo que necesite su contenido.
⚠️ Importante: Evita fijar
heighten elementos con texto. Si el texto crece (traducciones, letra más grande, pantalla estrecha), se desborda y se solapa con lo de abajo. Usamin-heighten su lugar: “al menos esto, y más si hace falta”.
⚠️ Importante: Una altura en porcentaje (
height: 50%) solo funciona si el padre tiene una altura definida. Si el padre mideauto, el porcentaje no tiene referencia y se ignora. Para alturas relativas a la pantalla usavh/dvh.
/* Imágenes fluidas: nunca más anchas que su contenedor */
img {
max-width: 100%;
height: auto; /* mantiene la proporción */
}
5. Centrado horizontal con margin: auto
Si un elemento de bloque tiene un ancho definido (menor que su contenedor) y márgenes laterales auto, el navegador reparte el espacio sobrante a partes iguales: el elemento queda centrado.
.contenedor {
max-width: 1100px;
margin: 0 auto; /* 0 arriba/abajo, auto izquierda/derecha */
padding-inline: 1rem; /* para que el contenido no toque los bordes en móvil */
}
/* Equivalente moderno */
.contenedor {
width: min(100% - 2rem, 1100px);
margin-inline: auto;
}
⚠️ Importante:
margin: autocentra horizontalmente elementos de bloque con ancho. No centra verticalmente (en flujo normal,margin-top: autovale 0), ni centra elementosinline. Para centrar texto o elementos en línea usatext-align: centeren el padre; para centrar en ambos ejes, Flexbox o Grid (módulos 10 y 11).
6. El colapso de márgenes
📘 Concepto: Cuando dos márgenes verticales se tocan en el flujo normal, no se suman: se fusionan en uno solo, del tamaño del mayor. Es el margin collapsing, una de las rarezas más famosas de CSS.
Caso 1: hermanos adyacentes
<p class="a">Párrafo A</p>
<p class="b">Párrafo B</p>
.a { margin-bottom: 30px; }
.b { margin-top: 20px; }
/* Separación real entre A y B: 30px (el mayor), NO 50px */
Tiene sentido tipográfico: si cada párrafo tiene margin-block: 1em, la separación entre dos párrafos es 1em, no 2em.
Caso 2: padre e hijo
Si un padre no tiene ni borde, ni padding, ni nada que separe su margen del de su primer hijo, el margen del hijo “se escapa” fuera del padre:
<div class="tarjeta">
<h2>Título</h2>
</div>
.tarjeta { background-color: #e0f2fe; }
.tarjeta h2 { margin-top: 40px; }
/* Sorpresa: el fondo azul no empieza 40px por encima del título;
es la tarjeta entera la que baja 40px */
Cómo evitarlo
| Solución | Código |
|---|---|
| Añadir padding al padre | .tarjeta { padding-top: 1px; } o, mejor, un padding real |
| Añadir borde al padre | .tarjeta { border-top: 1px solid transparent; } |
| Crear un contexto de formato de bloque | .tarjeta { display: flow-root; } |
| Usar Flexbox o Grid en el padre | Los márgenes no colapsan dentro de contenedores flex o grid |
Usar gap en lugar de márgenes | Con flex/grid (módulos 10 y 11) |
💡 Consejo: Muchos desarrolladores adoptan una regla sencilla para evitar sorpresas: margen solo en una dirección (por ejemplo, siempre
margin-bottom, nuncamargin-top). Recuerda también que el colapso solo ocurre con márgenes verticales y en flujo normal; nunca con márgenes horizontales.
7. Bordes, esquinas y contornos
border
.caja {
border: 2px solid #1d3557; /* abreviada: grosor estilo color */
}
/* Lados individuales */
.titulo {
border-bottom: 3px dashed #e63946;
border-left: 6px solid #457b9d;
}
/* Subpropiedades */
.caja {
border-width: 1px;
border-style: dotted;
border-color: gray;
}
border-style | Aspecto |
|---|---|
solid | Línea continua |
dashed | Discontinua (guiones) |
dotted | Punteada |
double | Doble línea |
groove / ridge / inset / outset | Efectos 3D (anticuados) |
none | Sin borde (por defecto) |
⚠️ Importante: Sin
border-styleno hay borde, aunque pongas grosor y color. Por esoborder: 2px red;no muestra nada: falta el estilo.
border-radius
.redondeado { border-radius: 8px; } /* las cuatro esquinas */
.pildora { border-radius: 999px; } /* botón "píldora" */
.circulo { width: 120px; height: 120px; border-radius: 50%; } /* círculo (si es cuadrado) */
.esquinas { border-radius: 20px 0 20px 0; } /* sup-izq, sup-der, inf-der, inf-izq */
.hoja { border-radius: 0 50% 0 50%; } /* forma de hoja */
💡 Consejo: Para avatares circulares a partir de fotos rectangulares, combina
border-radius: 50%conaspect-ratio: 1yobject-fit: cover(sección 13).
outline
El contorno se dibuja por fuera del borde, pero no ocupa espacio en el modelo de cajas: no empuja a otros elementos. Por eso es ideal para el indicador de foco.
.boton:focus-visible {
outline: 3px solid #f59e0b;
outline-offset: 3px; /* separa el contorno del elemento */
}
border | outline | |
|---|---|---|
| Ocupa espacio | Sí | No |
| Lados individuales | Sí | No |
Sigue border-radius | Sí | Sí, en los navegadores actuales |
| Uso típico | Diseño | Foco, depuración |
💡 Consejo: Truco de depuración:
* { outline: 1px solid red; }dibuja el contorno de todas las cajas sin alterar el diseño (conborderlo alterarías, porque ocupa espacio).
8. Sombras: box-shadow
Sintaxis: desplazamiento-x desplazamiento-y desenfoque expansión color. Los dos primeros son obligatorios.
.sombra-suave { box-shadow: 0 2px 8px rgb(0 0 0 / 0.1); }
.sombra-fuerte { box-shadow: 0 10px 30px rgb(0 0 0 / 0.3); }
.sombra-dura { box-shadow: 6px 6px 0 #1d3557; } /* estilo "neobrutalista" */
.anillo { box-shadow: 0 0 0 4px #93c5fd; } /* expansión sin desenfoque: anillo */
.interior { box-shadow: inset 0 2px 6px rgb(0 0 0 / 0.2); } /* sombra interior */
/* Varias sombras combinadas: más realistas */
.elevada {
box-shadow:
0 1px 2px rgb(0 0 0 / 0.06),
0 4px 8px rgb(0 0 0 / 0.08),
0 16px 32px rgb(0 0 0 / 0.1);
}
💡 Consejo: Define niveles de elevación como variables (
--sombra-1,--sombra-2,--sombra-3) y úsalos de forma coherente: tarjetas en reposo con nivel 1, al pasar el ratón nivel 2, modales nivel 3. Las sombras sutiles con poca opacidad quedan mucho más profesionales que una sombra negra intensa.
9. display: el tipo de caja
La propiedad display decide cómo se comporta una caja respecto a las demás.
block, inline e inline-block
<div>Bloque 1</div>
<div>Bloque 2</div>
<span>En línea 1</span>
<span>En línea 2</span>
<a href="#" class="etiqueta">Etiqueta</a>
.etiqueta {
display: inline-block;
padding: 0.25rem 0.75rem;
margin: 0.25rem;
background-color: #e0f2fe;
border-radius: 999px;
}
| Característica | block | inline | inline-block |
|---|---|---|---|
| Empieza en línea nueva | Sí | No | No |
| Ancho por defecto | Todo el disponible | El de su contenido | El de su contenido |
Respeta width / height | Sí | No | Sí |
margin / padding verticales | Sí | Se pintan, pero no empujan otras líneas | Sí |
| Ejemplos por defecto | div, p, h1, section, ul | span, a, strong, em | button, input, img* |
* Técnicamente img, input y button son elementos inline “de reemplazo”, que sí aceptan tamaño, por lo que se comportan como inline-block.
/* Convertir enlaces de un menú en "botones" con área de clic generosa */
.menu a {
display: block;
padding: 0.75rem 1rem;
}
💡 Consejo: Existen más valores que cambian por completo la forma de maquetar:
display: flex(módulo 10),display: grid(módulo 11),display: flow-root(sección 6),display: contents(la caja desaparece y sus hijos ocupan su lugar) ydisplay: list-item.
Ocultar elementos
| Técnica | ¿Ocupa espacio? | ¿Recibe clics? | ¿Lo lee un lector de pantalla? |
|---|---|---|---|
display: none | No | No | No |
Atributo HTML hidden | No | No | No |
visibility: hidden | Sí (deja el hueco) | No | No |
opacity: 0 | Sí | Sí | Sí |
Clase .visualmente-oculto | No (prácticamente) | No | Sí |
La última técnica sirve para textos que solo deben leer los lectores de pantalla (por ejemplo, la etiqueta de un botón que solo muestra un icono). Bootstrap la incluye como .visually-hidden:
.visualmente-oculto {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
10. overflow: cuando el contenido no cabe
Si fijas el tamaño de una caja y su contenido es mayor, el contenido se desborda (por defecto se ve por fuera). overflow decide qué hacer:
| Valor | Comportamiento |
|---|---|
visible | Por defecto: el contenido sobresale de la caja |
hidden | Se recorta lo que sobra (sin barras) |
clip | Como hidden, pero ni siquiera permite scroll por código |
scroll | Siempre muestra barras de desplazamiento |
auto | Muestra barras solo si hacen falta (lo más habitual) |
/* Caja de mensajes con scroll vertical */
.chat {
height: 300px;
overflow-y: auto;
}
/* Tabla ancha en móvil: scroll horizontal solo en la tabla, no en toda la página */
.tabla-responsive {
overflow-x: auto;
}
/* Tarjeta con esquinas redondeadas: recorta la imagen de dentro a las esquinas */
.tarjeta {
border-radius: 12px;
overflow: hidden;
}
⚠️ Importante: Si tu página tiene un scroll horizontal inesperado, algún elemento es más ancho que la pantalla. No lo tapes con
body { overflow-x: hidden; }: busca al culpable en DevTools (suele ser unwidth: 100vw, una imagen sinmax-widtho un margen negativo). El truco deloutlinerojo de la sección 7 ayuda mucho.
11. position: sacar elementos del flujo
📘 Concepto: Por defecto, las cajas siguen el flujo normal: los bloques se apilan uno debajo de otro y los elementos en línea se colocan como palabras de un texto.
positionpermite desplazar elementos o sacarlos de ese flujo para colocarlos donde quieras.
| Valor | ¿Sigue en el flujo? | Se posiciona respecto a… | Uso típico |
|---|---|---|---|
static | Sí | No se puede desplazar (por defecto) | — |
relative | Sí (conserva su hueco) | Su propia posición original | Ajustes finos; referencia para hijos absolutos |
absolute | No | El ancestro posicionado más cercano | Insignias, iconos, superposiciones |
fixed | No | La ventana (viewport) | Cabeceras fijas, botón “volver arriba” |
sticky | Sí | Su contenedor con scroll, hasta un umbral | Cabeceras de tabla, menús laterales |
Las propiedades top, right, bottom y left (o la abreviada inset) indican el desplazamiento. No tienen efecto con static.
11.1. relative
El elemento se desplaza visualmente, pero su hueco original se mantiene: los demás elementos no se mueven.
.ligeramente-abajo {
position: relative;
top: 10px; /* 10px hacia abajo desde su posición normal */
left: 20px; /* 20px hacia la derecha */
}
11.2. absolute
El elemento sale del flujo (los demás se comportan como si no existiera) y se coloca respecto a su bloque contenedor: el ancestro más cercano con position distinto de static. Si no hay ninguno, toma como referencia el documento inicial.
El patrón más importante de todo el posicionamiento: padre relative, hijo absolute.
<article class="producto">
<span class="producto__insignia">-20 %</span>
<img src="zapatillas.jpg" alt="Zapatillas de running azules">
<h3>Zapatillas Run</h3>
</article>
.producto {
position: relative; /* 1. el padre se convierte en la referencia */
border: 1px solid #e5e7eb;
}
.producto__insignia {
position: absolute; /* 2. el hijo se coloca respecto al padre */
top: 10px;
right: 10px;
padding: 0.25rem 0.5rem;
background-color: #dc2626;
color: white;
font-weight: bold;
border-radius: 4px;
}
⚠️ Importante: Si olvidas el
position: relativedel padre, la insignia se colocará respecto a la página entera (u otro ancestro posicionado) y aparecerá en la esquina superior derecha de la ventana. Es el error de posicionamiento más común.
Con inset: 0 (equivale a top: 0; right: 0; bottom: 0; left: 0) un elemento absoluto cubre todo su contenedor, ideal para velos sobre imágenes:
.foto { position: relative; }
.foto__velo {
position: absolute;
inset: 0; /* cubre toda la foto */
background: linear-gradient(transparent 50%, rgb(0 0 0 / 0.7));
display: flex;
align-items: flex-end; /* texto abajo (módulo 10) */
padding: 1rem;
color: white;
}
11.3. fixed
Se posiciona respecto a la ventana y no se mueve al hacer scroll.
/* Botón "volver arriba" siempre visible abajo a la derecha */
.volver-arriba {
position: fixed;
bottom: 1.5rem;
right: 1.5rem;
}
/* Cabecera fija */
.cabecera-fija {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 4rem;
background-color: white;
box-shadow: 0 2px 6px rgb(0 0 0 / 0.1);
}
/* Como la cabecera ya no ocupa espacio, compensamos para que no tape el contenido */
body {
padding-top: 4rem;
}
⚠️ Importante: Un elemento
fixeddeja de ser relativo a la ventana si algún ancestro tienetransform,filteroperspective: pasa a posicionarse respecto a ese ancestro. Si tu elemento fijo “se mueve con el scroll”, revisa sus ancestros.
11.4. sticky
Un híbrido: se comporta como relative hasta que, al hacer scroll, alcanza el umbral indicado (top: 0); entonces se queda “pegado” como si fuera fixed, pero solo dentro de su contenedor padre.
/* Cabecera que se queda pegada arriba al hacer scroll (y sigue ocupando su espacio) */
.cabecera {
position: sticky;
top: 0;
z-index: 10;
background-color: white; /* ¡necesaria! si no, el contenido se verá a través */
}
/* Encabezados de una tabla larga */
thead th {
position: sticky;
top: 0;
background-color: #f3f4f6;
}
/* Menú lateral que acompaña al contenido */
.barra-lateral {
position: sticky;
top: 5rem;
align-self: start; /* necesario dentro de flex/grid (módulos 10 y 11) */
}
⚠️ Importante:
sticky“no funciona” en tres situaciones típicas: falta el umbral (top,bottom…); algún ancestro tieneoverflow: hidden,autooscroll; o el contenedor padre es igual de alto que el elemento (no hay recorrido en el que pegarse).
12. z-index y contextos de apilamiento
Cuando los elementos posicionados se solapan, z-index decide cuál queda encima: el valor mayor se ve por delante.
.modal-fondo { position: fixed; inset: 0; z-index: 100; background: rgb(0 0 0 / 0.5); }
.modal { position: fixed; z-index: 101; }
⚠️ Importante:
z-indexsolo funciona en elementos posicionados (positiondistinto destatic) o que sean hijos de un contenedor flex o grid. En un elementostaticno tiene efecto.
El misterio del z-index: 9999 que no funciona
📘 Concepto: Ciertas propiedades crean un contexto de apilamiento (stacking context): un grupo aislado de capas. Los
z-indexde los hijos solo compiten dentro de su grupo. Es como un edificio: da igual que una persona esté en la oficina “9999” de la planta 1; siempre estará por debajo de cualquiera de la planta 2.
flowchart TD
R["Raíz del documento"] --> A["Cabecera<br/>position: relative; z-index: 2"]
R --> B["Contenido<br/>position: relative; z-index: 1"]
B --> M["Menú desplegable<br/>z-index: 9999"]
A --> L["Logo"]
En este ejemplo, el menú desplegable nunca podrá quedar por encima de la cabecera: está atrapado dentro del contexto de “Contenido” (z-index 1), que en su conjunto está por debajo de “Cabecera” (z-index 2).
Crean un contexto de apilamiento, entre otros:
position: relativeoabsolutecon unz-indexdistinto deauto.position: fixedosticky(siempre).- Hijos de flex o grid con
z-indexdistinto deauto. opacitymenor que 1.transform,filter,perspectiveoclip-pathdistintos denone.isolation: isolate(creado precisamente para esto).
/* Aísla las capas internas de un componente para que no interfieran con el resto de la página */
.tarjeta {
isolation: isolate;
}
💡 Consejo: No uses valores arbitrarios como 9999. Define una escala en variables y respétala en todo el proyecto:
--z-desplegable: 100; --z-cabecera: 200; --z-modal: 300; --z-aviso: 400;. Bootstrap hace lo mismo con valores fijos para cada componente.
13. float y clearfix (histórico)
float se creó para que el texto rodeara a una imagen, como en las revistas. Durante años se usó (abusó) para maquetar columnas, hasta que llegaron Flexbox y Grid.
<article class="noticia">
<img class="noticia__foto" src="faro.jpg" alt="Faro al atardecer">
<p>Lorem ipsum... (un texto largo que rodea la imagen)</p>
</article>
.noticia__foto {
float: left; /* la imagen se va a la izquierda y el texto fluye a su derecha */
width: 200px;
margin: 0 1rem 1rem 0;
}
El problema y el clearfix
Los elementos flotantes salen parcialmente del flujo: si el padre solo contiene flotantes, mide cero de alto y su fondo o borde “desaparecen”. La solución clásica era el clearfix:
/* Clearfix clásico: un pseudo-elemento al final que "limpia" los flotantes */
.clearfix::after {
content: "";
display: table;
clear: both;
}
/* Solución moderna, más limpia */
.noticia {
display: flow-root; /* el contenedor engloba a sus hijos flotantes */
}
La propiedad clear: both hace que un elemento se coloque debajo de cualquier flotante anterior.
⚠️ Importante: Hoy no uses
floatpara maquetar columnas o menús: usa Flexbox o Grid.floatsigue siendo válido para su propósito original: hacer que el texto rodee una imagen o una cita destacada.
14. object-fit y aspect-ratio
object-fit: encajar imágenes y vídeos
Cuando fuerzas una imagen a un tamaño con proporciones diferentes a las suyas, se deforma. object-fit (equivalente a background-size pero para <img> y <video>) lo soluciona:
.galeria img {
width: 100%;
height: 250px;
object-fit: cover; /* cubre la caja recortando lo que sobra, sin deformar */
object-position: center top; /* qué parte de la imagen conservar */
}
| Valor | Comportamiento |
|---|---|
fill | Por defecto: se estira y deforma para llenar la caja |
cover | Cubre la caja manteniendo la proporción; recorta lo que sobra |
contain | Cabe entera manteniendo la proporción; puede dejar huecos |
none | Tamaño original, recortada si es necesario |
scale-down | El menor entre none y contain |
aspect-ratio: proporciones fijas
Define una relación ancho/alto. El navegador calcula la dimensión que falte:
.video-contenedor { aspect-ratio: 16 / 9; width: 100%; } /* panorámico */
.avatar { aspect-ratio: 1; width: 80px; border-radius: 50%; object-fit: cover; }
.tarjeta__imagen { aspect-ratio: 4 / 3; width: 100%; object-fit: cover; }
💡 Consejo:
aspect-ratio+object-fit: coveres la combinación perfecta para galerías y tarjetas: todas las imágenes tienen el mismo tamaño aunque los ficheros originales sean de proporciones distintas. Además, reservar el espacio de la imagen evita los “saltos” del contenido mientras carga (lo que Google mide como CLS).
💻 Ejemplo: Antes de
aspect-ratiose usaba el truco depadding-top: 56.25%(9 / 16 = 0,5625) en un contenedor con un hijo absoluto para mantener la proporción 16:9 de los vídeos incrustados. Si lo ves en código antiguo, ya sabes qué hace; hoy basta conaspect-ratio: 16 / 9.
15. Ejercicios
Ejercicio 1: Disecciona una caja
Crea una div con width: 300px, padding: 25px, border: 5px solid y margin: 20px. Calcula su ancho total visible con content-box y con border-box, y compruébalo en el diagrama de cajas de DevTools. Después cambia el padding usando 2, 3 y 4 valores y anota qué lado cambia en cada caso.
Ejercicio 2: Contenedor centrado y colapso de márgenes
Crea una página con un .contenedor centrado de ancho máximo 900px. Dentro, una tarjeta con fondo de color cuyo primer hijo sea un h2 con margin-top: 40px. Observa el colapso de márgenes y soluciónalo de tres formas distintas (padding, display: flow-root y display: flex con flex-direction: column).
Ejercicio 3: Botones y etiquetas
Crea un conjunto de botones (inline-block) con padding en em, border-radius de píldora, un botón con borde de 2px y fondo transparente, un botón con sombra de elevación que aumenta en :hover, y un :focus-visible con outline y outline-offset. Debajo, una lista de etiquetas (tags) como enlaces inline-block.
Ejercicio 4: Tarjeta de producto con insignia
Maqueta una tarjeta de producto con imagen (proporción 4:3 con aspect-ratio y object-fit: cover), insignia de descuento posicionada en la esquina superior izquierda, un icono de “favorito” en la esquina superior derecha, título, precio tachado y precio actual. Usa el patrón padre relative / hijos absolute.
Ejercicio 5: Cabecera pegajosa y botón flotante
Crea una página larga con una cabecera sticky que se quede arriba al hacer scroll, una tabla de 50 filas cuyo thead también sea sticky, y un botón fixed “Volver arriba” en la esquina inferior derecha (que enlace a #inicio). Comprueba que la cabecera no tapa los encabezados de la tabla ajustando sus top.
Ejercicio 6: Ventana modal
Crea una ventana modal con CSS: un fondo oscuro semitransparente fixed con inset: 0 y, centrada encima, una caja blanca con título, texto, sombra fuerte y un botón de cerrar posicionado en su esquina. Usa una escala de z-index en variables. Pista: para centrar la caja puedes usar position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); (la transformación desplaza la caja la mitad de su propio tamaño; la verás en el módulo 13).
Ejercicio 7: Revista con float
Maqueta un artículo de revista con una imagen flotando a la izquierda rodeada por el texto y una cita destacada flotando a la derecha con borde lateral grueso. Asegúrate de que el contenedor engloba a los flotantes con display: flow-root. Añade al final un pie que use clear: both.
Resumen
| Concepto | Sintaxis / idea clave |
|---|---|
| Modelo de cajas | Contenido → padding → border → margin |
| Valores abreviados | 1 a 4 valores en sentido horario: arriba, derecha, abajo, izquierda |
box-sizing | border-box: width incluye padding y borde |
| Tamaños flexibles | max-width, min-height; evita height fijo con texto |
| Centrado horizontal | max-width + margin-inline: auto |
| Colapso de márgenes | Márgenes verticales adyacentes se fusionan; se evita con flow-root, flex, grid o padding |
| Bordes | border: 2px solid color; border-radius: 50% para círculos |
| Contorno | outline no ocupa espacio; outline-offset |
| Sombras | box-shadow: x y desenfoque expansión color; inset para interior |
display | block, inline, inline-block, none, flex, grid, flow-root |
| Ocultar | display: none (sin hueco), visibility: hidden (con hueco), .visualmente-oculto |
overflow | visible, hidden, auto, scroll, clip |
position | static, relative, absolute, fixed, sticky + top/right/bottom/left/inset |
| Patrón clave | Padre relative, hijo absolute |
z-index | Solo en posicionados o hijos flex/grid; compite dentro de su contexto de apilamiento |
float | Solo para que el texto rodee imágenes; contenedor con display: flow-root |
| Imágenes | object-fit: cover + aspect-ratio: 16 / 9 |