El modelo de caja
En resumen: El modelo de caja dice que cada elemento es una caja de contenido, relleno, borde y margen; con box-sizing: border-box el ancho incluye el relleno y el borde.
Problema
Las tarjetas se salen de la pantalla
La página de los panes en un teléfono de 360px de ancho. Cada tarjeta tiene width: 100%: tan ancha como la columna. Aun así, las tarjetas se salen por la derecha, y toda la página se desplaza hacia el lado.
Consola
La columna mide 360 − 2 × 16 = 328px. La tarjeta debía medir eso, y mide 362px: 34px más que la columna. Esos 34px no son un error del número 100%. Están en lo que mide width.
La idea
Cuatro capas
Para CSS, cada elemento es una caja rectangular. Tiene cuatro capas, de dentro hacia fuera:
- el contenido: el texto, la imagen, los hijos;
padding(relleno): el espacio entre el contenido y el borde. El fondo del elemento lo rellena;border(borde): el marco;margin(margen): el espacio fuera del borde, hasta las cajas vecinas. Es transparente.
Por defecto width es solo el ancho del contenido. El relleno y el borde se suman por fuera, a ambos lados:
.card { width: 100%; /* 328px: el contenido */ padding: 16px; /* + 2 × 16 */ border: 1px solid #ccc; /* + 2 × 1 = 362px */}Por eso la tarjeta del principio mide 362px. padding: 16px vale para los cuatro lados. También puedes fijarlos por separado: padding: 8px 16px (arriba y abajo, izquierda y derecha) o padding-left: 16px.
Míralo
Mira la caja
La caja se construye capa por capa. La línea de arriba es width: fíjate en lo que mide.
/* por defecto: box-sizing: content-box */.card { width: 280px; padding: 24px; border: 8px solid #2b3a55; margin: 16px;}.card.bb { box-sizing: border-box;}Paso a paso, en palabras
- En el centro está el contenido: el texto de la tarjeta. Por defecto width es solo el ancho del contenido: 280px.
- padding es el espacio entre el contenido y el borde. Está a ambos lados: 280 + 2 × 24 = 328px. El fondo de la tarjeta también lo rellena.
- border es el marco, también a ambos lados. La caja ya mide 344px, no 280: width no decía cuánto mide la tarjeta en la pantalla.
- margin es el espacio fuera del borde, entre esta caja y la siguiente. No tiene fondo y no es parte de la tarjeta, pero ocupa sitio: 376px.
- box-sizing: border-box cambia lo que significa width: ahora es el ancho hasta el borde. La tarjeta mide 280px, y el contenido se encoge a 280 − 48 − 16 = 216px.
- El mismo CSS, dos significados de width. Con content-box la tarjeta mide 344px; con border-box, 280px: lo que dice width. Por eso casi todos los proyectos ponen border-box en todos los elementos.
Las mismas dos tarjetas en el navegador. Lo azul es el párrafo de dentro: mide lo que el contenido de la tarjeta. En las herramientas de desarrollo (F12) selecciona la tarjeta: el panel Calculado dibuja la misma caja con los números de cada capa.
Consola
La idea
box-sizing: border-box
Hacer cuentas de cabeza genera errores: quieres una tarjeta tan ancha como la columna y tienes que restar el relleno y el borde. box-sizing: border-box cambia lo que significa width: es el ancho hasta el borde, incluido. El relleno y el borde se restan por dentro, y el contenido se encoge.
Entonces width: 100% es de verdad la columna, sea cual sea el relleno. Por eso casi todos los proyectos empiezan con:
*, *::before, *::after { box-sizing: border-box;}* alcanza a todos los elementos. box-sizing no se hereda, así que la regla es para todos los elementos, no solo para <html>. ::before y ::after son trozos de contenido que CSS añade por sí mismo; también son cajas.
Pruébalo
Haz que quepan las tarjetas
Haz que las tarjetas quepan en la pantalla del teléfono con box-sizing: border-box. El relleno (16px) y el borde (1px) se quedan.
Consola
Solución
index.html<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Panes</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>Panes</h1>
<article class="card">
<h2>Pan Dobrudzha</h2>
<p class="price">1,80 €</p>
<button type="button">Añadir al carrito</button>
</article>
<article class="card">
<h2>Pan de centeno</h2>
<p class="price">2,10 €</p>
<button type="button">Añadir al carrito</button>
</article>
</main>
</body>
</html>
style.css*, *::before, *::after {
box-sizing: border-box;
}
body { font-family: system-ui, sans-serif; margin: 0; }
main { padding: 16px; }
.card {
width: 100%;
padding: 16px;
border: 1px solid #ccc;
margin-bottom: 12px;
}
Con border-box, width: 100% son 328px incluidos el relleno y el borde, y el contenido se queda con 328 − 32 − 2 = 294px. width: 90% cabe en esta pantalla por casualidad, pero en otra volverías a hacer cuentas. Sin relleno la tarjeta cabe, pero el texto toca el borde.
La regla con la que empiezan casi todos los proyectos:
*, *::before, *::after {
box-sizing: ;
}border-box: width y height llegan hasta el borde, incluido. El valor por defecto es content-box.
La idea
Los márgenes se fusionan
Dos cajas, una debajo de la otra: el margen inferior de la de arriba y el margen superior de la de abajo no se suman. Se fusionan, y se queda el mayor.
Consola
- Las notas tienen 24px debajo y 16px encima. Entre ellas hay 24px, no 40px.
- El encabezado del banner tiene
margin: 20px 0. El banner no tiene relleno ni borde arriba, así que el margen del encabezado sale por encima: el fondo naranja empieza justo encima del texto. Conpadding: 1px 16pxen el banner, el fondo cubre también el margen.
Solo se fusionan los márgenes verticales. A izquierda y derecha siempre se suman. En flex y grid, las próximas lecciones, ni siquiera los verticales se fusionan.
La idea
block, inline e inline-block
No todas las cajas se comportan igual. La propiedad display dice cómo se coloca una caja entre las demás.
block: empieza en una línea nueva y ocupa todo el ancho de su padre. El ancho, el alto, el margen y el relleno se aplican.div,p,h1yarticleson así.inline: se coloca dentro de la línea de texto y mide lo que su contenido. El ancho y el alto no se aplican, un margen vertical no mueve nada, y un relleno vertical se pinta, pero no separa las líneas.span,aystrongson así.inline-block: se coloca en la línea como inline, pero el ancho, el alto, el margen y el relleno se aplican como en block.
Consola
Las dos etiquetas tienen las mismas propiedades, y la segunda es además inline-block. La primera es inline: width: 64px y el margen no se aplican, y el rojo del relleno sobresale por encima y por debajo de la línea sin separarla de las demás. La segunda es inline-block: mide 64 + 2 × 8 = 80px de ancho (aquí sigue siendo content-box), y el párrafo se hace más alto para que quepa.
Pruébalo
Las etiquetas
Las etiquetas −20% y Nuevo deben medir 64px de ancho cada una y estar una al lado de la otra en una línea. Ahora mismo el ancho no se aplica.
Consola
Solución
index.html<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Leche fresca 3%</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<article>
<h2>Leche fresca 3%</h2>
<p><span class="badge">−20%</span> <span class="badge">Nuevo</span></p>
<p class="price">1,92 €</p>
</article>
</body>
</html>
style.cssbody { font-family: system-ui, sans-serif; margin: 16px; }
.badge {
display: inline-block;
width: 64px;
padding: 4px 0;
text-align: center;
color: #fff;
background: #c0392b;
}
display: inline-block le da a la etiqueta las medidas de una caja, y se queda en la línea de texto. Con block el ancho se aplica, pero cada etiqueta queda en una línea nueva. inline es el valor por defecto de un <span>, así que no cambia nada.
La idea
Unidades
Hasta ahora todo estaba en px. CSS tiene también otras unidades, y cada una sirve para algo:
px: una medida fija. Buena para bordes y huecos pequeños.rem: el tamaño de fuente de<html>(16px, salvo que alguien lo haya cambiado).1.5remson 24px en toda la página. Si una persona agrandó la fuente en los ajustes del navegador, rem crece con ella y px no. Por eso las fuentes y los espacios van mejor en rem.em: el tamaño de fuente del propio elemento; enfont-size, el del padre. Un relleno en em crece con el texto de un botón.%: del padre.width: 50%es la mitad del ancho del contenido del padre.vwyvh: el 1% del ancho y del alto de la ventana.
Consola
El encabezado mide 1.5rem = 24px. La tarjeta es el 50% de 320px = 160px. Su fuente es de 18px, así que padding: 1em son 18px. 1.25rem en el encabezado de la tarjeta son 20px: a rem no le importan los 18px de la tarjeta. 0.75em en <small> viene de la fuente del párrafo, que heredó 18px: 13,5px.
Pruébalo
Encuentra el error
El menú mide 20px, el submenú de dentro, 25px, y el siguiente nivel es aún mayor. ¿Qué línea tiene la culpa?
Elige la línea con el error.
em en font-size es el tamaño del padre. Cada .menu anidado multiplica por 1,25: 16, 20, 25, 31,25. Con font-size: 1.25rem, cada nivel mide 20px.
Más precisamente, para curiosos
Los porcentajes en padding y margin salen del ancho del padre, también arriba y abajo. padding-top: 50% hace una caja de la mitad de alto que de ancho es su padre.
100vw y la barra de desplazamiento. vw incluye la barra de desplazamiento. Si la página tiene una barra vertical visible, un elemento con width: 100vw es más ancho que la página y provoca un desplazamiento horizontal. Para «todo el ancho», width: 100% es más seguro.
vh en el teléfono. La barra de direcciones aparece y desaparece. 100vh es la altura con la barra oculta, así que, cuando la barra se ve, el borde inferior queda por debajo de la pantalla. 100dvh sigue la altura real en cada momento.
outline se dibuja como un borde, pero no ocupa espacio y no mueve nada. Por eso el foco de los botones se muestra con él.
min-width y max-width ponen límites: max-width: 100% evita que una imagen se salga de la columna.
Formularios. En Chromium los botones son border-box por defecto, mientras que los campos (input) son content-box. La regla con * los iguala.
Cómo evitar la fusión. El margen de un hijo no sale a través del padre si el padre tiene relleno o borde arriba, o si es display: flow-root. Así el padre crea un contexto de maquetación nuevo (block formatting context).
Un margen negativo tira de la caja hacia fuera y la superpone a sus vecinas. Rara vez lo necesitas.
Ejercicio
El catálogo en el teléfono
El catálogo en un teléfono (360px). Pon border-box en todos los elementos para que las tarjetas quepan en la pantalla. La tarjeta: padding 1rem, un borde 1px solid #ddd y 12px de espacio debajo de cada una. Los encabezados de los productos miden 1.25rem. Las etiquetas −20% y Nuevo son inline-block, con padding 2px 8px, texto blanco y fondo rojo (#c0392b). El botón mide lo que el interior de la tarjeta: width: 100%.
Consola
Solución
index.html<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Panes</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>Panes</h1>
<article class="card">
<h2>Pan Dobrudzha</h2>
<span class="badge">−20%</span>
<p class="price">1,80 €</p>
<button type="button">Añadir al carrito</button>
</article>
<article class="card">
<h2>Pan de centeno</h2>
<p class="price">2,10 €</p>
<button type="button">Añadir al carrito</button>
</article>
<article class="card">
<h2>Pan de espelta</h2>
<span class="badge">Nuevo</span>
<p class="price">3,40 €</p>
<button type="button">Añadir al carrito</button>
</article>
</main>
</body>
</html>
style.css*, *::before, *::after {
box-sizing: border-box;
}
body { font-family: system-ui, sans-serif; margin: 0; }
main { padding: 16px; }
.card {
width: 100%;
padding: 1rem;
border: 1px solid #ddd;
margin-bottom: 12px;
}
.card h2 { font-size: 1.25rem; }
.badge {
display: inline-block;
padding: 2px 8px;
color: #fff;
background: #c0392b;
}
.card button { width: 100%; }
*, *::before, *::after { box-sizing: border-box; } es la primera regla: sin ella las tarjetas miden 362px. margin-bottom: 12px en la tarjeta da el espacio. 1rem son 16px y 1.25rem son 20px, sea lo que sea lo que las rodee. El botón con width: 100% mide 294px: la columna son 328px, menos 2 × 16 de relleno y 2 × 1 de borde de la tarjeta.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: El documento HTML · Selectores y la cascada
- Continúa en: Flexbox: fila y columna · Grid: filas y columnas · Diseño responsive
- Ejercicios sobre el tema: CSS: el aspecto de la página