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-box y entender por qué es el estándar de facto
  • Controlar tamaños con width, height, min-* y max-*
  • Centrar elementos con margin: auto y entender el colapso de márgenes
  • Dar forma a las cajas con border, border-radius, outline y box-shadow
  • Distinguir los tipos de display y las formas de ocultar elementos
  • Gestionar el desbordamiento con overflow
  • Posicionar elementos con position y controlar su apilamiento con z-index
  • Conocer float y el clearfix por razones históricas
  • Adaptar imágenes con object-fit y mantener proporciones con aspect-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-box miden “50 % + padding” cada una, suman más del 100 % y la segunda baja a la línea siguiente. Con border-box caben perfectamente una al lado de la otra.

⚠️ Importante: El margen nunca se incluye en width, ni siquiera con border-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: auto ocupa todo el ancho disponible de su contenedor.
  • Alto: con height: auto, mide lo que necesite su contenido.

⚠️ Importante: Evita fijar height en elementos con texto. Si el texto crece (traducciones, letra más grande, pantalla estrecha), se desborda y se solapa con lo de abajo. Usa min-height en 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 mide auto, el porcentaje no tiene referencia y se ignora. Para alturas relativas a la pantalla usa vh/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: auto centra horizontalmente elementos de bloque con ancho. No centra verticalmente (en flujo normal, margin-top: auto vale 0), ni centra elementos inline. Para centrar texto o elementos en línea usa text-align: center en 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, nunca margin-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-style no hay borde, aunque pongas grosor y color. Por eso border: 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% con aspect-ratio: 1 y object-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 (con border lo 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) y display: 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 un width: 100vw, una imagen sin max-width o un margen negativo). El truco del outline rojo 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. position permite 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: relative del 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 fixed deja de ser relativo a la ventana si algún ancestro tiene transform, filter o perspective: 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 tiene overflow: hidden, auto o scroll; 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-index solo funciona en elementos posicionados (position distinto de static) o que sean hijos de un contenedor flex o grid. En un elemento static no 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-index de 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: relative o absolute con un z-index distinto de auto.
  • position: fixed o sticky (siempre).
  • Hijos de flex o grid con z-index distinto de auto.
  • opacity menor que 1.
  • transform, filter, perspective o clip-path distintos de none.
  • 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 float para maquetar columnas o menús: usa Flexbox o Grid. float sigue 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: cover es 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-ratio se usaba el truco de padding-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 con aspect-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