Las cuatro cajas y box-sizing
El modelo de caja completo, qué significa realmente content-box frente a border-box, qué caja mira cada propiedad de pintado, y por qué el margen nunca entra en el cálculo.
Todo elemento genera cuatro cajas concéntricas, y casi todas las propiedades que escribes se refieren a una de ellas sin decirlo. box-sizing no cambia el modelo: cambia a cuál de las cuatro se aplica width. Entender esa distinción explica de golpe por qué el margen nunca se incluye, por qué el fondo se pinta por debajo del borde y por qué overflow recorta donde recorta.
- Nombrar las cuatro cajas y decir qué separa cada una.
- Explicar qué cambia exactamente
box-sizingy qué no toca. - Saber a qué caja se refiere cada propiedad de fondo, recorte y sombra.
- Escribir un reinicio del modelo de caja defendible.
Las cuatro cajas
flowchart TB
subgraph M[Caja de margen]
subgraph B[Caja de borde]
subgraph P[Caja de relleno]
C[Caja de contenido]
end
end
end
style M fill:#f9e2af,color:#11111b
style B fill:#f38ba8,color:#11111b
style P fill:#94e2d5,color:#11111b
style C fill:#a6e3a1,color:#11111bDe dentro hacia fuera: la caja de contenido encierra el texto y los hijos; la caja de relleno la amplía con padding; la caja de borde añade el grosor de border; y la caja de margen añade margin, que es espacio vacío por fuera de todo.
Las tres primeras son partes del elemento. La cuarta no lo es: el margen no pertenece al elemento, describe su relación con lo que tiene alrededor. De esa diferencia salen casi todas las rarezas del resto del nivel, empezando por que los márgenes colapsan y los rellenos no.
box-sizing y por qué ganó border-box
Por defecto, width y height dimensionan la caja de contenido. Eso significa que el tamaño ocupado es la suma de lo que pediste más el relleno más el borde.
.a {
box-sizing: content-box; /* valor inicial */
inline-size: 200px;
padding: 20px;
border: 2px solid;
}
/* Ancho ocupado: 200 + 20 + 20 + 2 + 2 = 244px */
Con border-box, width dimensiona la caja de borde, y el relleno y el borde se comen espacio por dentro:
.b {
box-sizing: border-box;
inline-size: 200px;
padding: 20px;
border: 2px solid;
}
/* Ancho ocupado: 200px. Contenido: 200 - 40 - 4 = 156px */
La historia merece contarse porque es la única vez que un navegador acertó contra la especificación. Internet Explorer 5 implementaba el modelo de caja de borde; el resto del mundo y el W3C se decantaron por el de contenido, y durante años se citó aquello como el ejemplo canónico de navegador que hacía las cosas mal. Cuando llegó el diseño responsive quedó claro que el modelo cómodo era el otro: si mezclas anchos en porcentaje con rellenos en píxeles, content-box obliga a hacer aritmética que no se puede hacer, porque no conoces el porcentaje. box-sizing se añadió para poder elegir, y la práctica se decantó unánimemente por el modelo que se había ridiculizado.
Lo que box-sizing no hace: el margen nunca entra. No existe ningún valor que incluya el margen en width, y no lo hay porque el margen no forma parte del elemento.
Y un caso límite que conviene conocer: con border-box, si el relleno más el borde superan el width pedido, la caja de contenido se queda en cero y el ancho usado pasa a ser el de relleno más borde. La caja no se hace más pequeña que sus propios adornos.
Qué caja mira cada propiedad
| Propiedad | Caja de referencia |
|---|---|
width y height |
contenido, o borde con border-box |
background-color |
se pinta hasta la caja de borde |
background-origin |
por defecto la caja de relleno |
background-clip |
por defecto la caja de borde |
border-radius |
redondea la caja de borde |
overflow |
recorta en la caja de relleno |
box-shadow |
parte de la caja de borde |
outline |
por fuera de la caja de borde, sin ocupar sitio |
Dos de estas filas explican bugs frecuentes.
El fondo se pinta por debajo del borde. Por eso un borde punteado o semitransparente deja ver el color de fondo entre los puntos. Se corrige con background-clip: padding-box.
.pastilla {
background: canvas;
border: 4px dashed rgb(0 0 0 / 0.3);
background-clip: padding-box; /* el fondo no asoma por el borde */
}
overflow recorta en la caja de relleno, no en la de contenido. Por eso el padding-bottom de un contenedor con desplazamiento parece desaparecer al llegar al final: no desaparece, es que el contenido puede desplazarse por encima de él. Y por eso outline nunca queda recortado por el overflow de su propio elemento: está fuera de la caja de borde.
El reinicio que conviene escribir
El reinicio universal es una de las tres o cuatro líneas que todo proyecto tiene, y hay dos versiones que no son equivalentes.
/* Version directa: todo a border-box, sin excepciones. */
*, *::before, *::after { box-sizing: border-box; }
/* Version heredable: permite que un subarbol vuelva a content-box. */
:root { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }
La primera es más simple y es la que usa casi todo el mundo. La segunda paga una indirección a cambio de poder escribir .widget-de-terceros { box-sizing: content-box; } y que el cambio alcance a todo el subárbol; es útil cuando integras HTML que no controlas y que asume el modelo antiguo.
Incluye los pseudo-elementos en el selector: no los heredan del elemento que los genera y sin ellos te quedan dos cajas con un modelo distinto al resto.
* { box-sizing: border-box } sin los pseudo-elementos es el error más común del reinicio, y produce fallos raros justo en los sitios decorativos: un ::before que sirve de barra o de punto y que mide un par de píxeles más de lo que dice su regla. Cuesta cero añadirlos.
La forma habitual de explicar box-sizing —“con border-box el relleno va por dentro”— es útil y esconde lo importante. Las cuatro cajas existen siempre y sus tamaños relativos no dependen de box-sizing: el relleno mide lo que mide, el borde mide lo que mide, y la distancia del contenido al exterior es la misma en los dos modelos. Lo único que cambia es contra qué caja se resuelve la restricción que impone width, que es una propiedad que no fija un tamaño sino que participa en un sistema de ecuaciones junto con el relleno, el borde y el espacio disponible. Con content-box la incógnita se despeja desde dentro; con border-box, desde fuera. Esa reformulación tiene dos consecuencias que valen la lección entera. La primera es que deja de ser un misterio por qué no existe un margin-box: el margen no es una dimensión del elemento sino de la separación entre elementos, y por tanto no aparece en la ecuación del tamaño de nadie; aparece en la ecuación del flujo, que es un sistema distinto y donde ocurren cosas que en el sistema del tamaño serían impensables, como que dos valores se fundan en uno. La segunda es que te da el criterio para leer cualquier propiedad nueva que se refiera a cajas: la pregunta útil no es “¿qué hace esto?” sino “¿sobre qué caja de las cuatro opera y en qué sistema participa, en el del tamaño o en el del flujo?”. Con esa pregunta, background-clip, overflow, border-radius y outline dejan de ser una lista que memorizar y pasan a ser consecuencias evidentes de dónde se sitúa cada una.
- Construye la misma caja con los dos modelos y comprueba los anchos ocupados a mano.
- Fuerza el caso donde el relleno supera al
widthconborder-boxy observa qué ancho resulta. - Pon un borde punteado sobre un fondo de color y arregla el asomo con
background-clip. - Comprueba que
outlineno queda recortado por eloverflowdel propio elemento. - Escribe las dos versiones del reinicio y explica cuándo elegirías la heredable.