Skip to content
This page has been auto-translated and may contain errors.View in English

El modelo de caja

docs.scrimba.com

Una vez que empiezas a espaciar las cosas con CSS, te encuentras con el mismo puzzle que todos: estableces un ancho, añades algo de relleno, y el elemento termina siendo más ancho de lo que pediste. Dos párrafos se sientan más cerca juntos de lo que los números dicen que deberían. Nada de esto es un error. Es el modelo de caja, el conjunto de reglas que decide cuánto espacio ocupa cada elemento, y una vez que puedas visualizarlo, la mayoría de tus sorpresas de espaciado dejan de serlo.

Cada elemento es una caja

CSS trata cada elemento de la página como un rectángulo, incluso una palabra única o un enlace. Ese rectángulo se construye a partir de cuatro capas apiladas de dentro hacia afuera, y esto es el modelo de caja: el contenido en el medio, luego relleno alrededor de él, luego un borde, luego margen en el exterior.

Imagina una foto enmarcada en una pared. La foto es el contenido. El marco blanco alrededor de la foto es el relleno, espacio de respiración dentro del marco. El marco de madera en sí es el borde. Y la pared vacía entre este marco y el siguiente es el margen. Cada elemento que diseñas se empaqueta de la misma manera, de dentro hacia afuera.

css
.card {
  padding: 16px;              /* espacio dentro, alrededor del contenido */
  border: 2px solid #333;     /* el marco */
  margin: 24px;               /* espacio fuera, empujando a los vecinos */
}

Cada elemento que el navegador renderiza es una caja rectangular, y esa caja tiene cuatro partes medidas hacia afuera desde el centro: la caja de contenido (donde vive tu texto o imagen), el relleno, el borde y el margen. Ya has visto esta idea en cómo funciona CSS; el modelo de caja es la parte que rige el tamaño y el espaciado.

El orden importa porque cada capa envuelve la anterior. El relleno se sienta entre el contenido y el borde, así que un color de fondo llena el contenido y el relleno pero se detiene en el borde exterior. El margen se sienta fuera del borde y es siempre transparente, por lo que no puedes dar a un margen un color.

css
.card {
  padding: 16px;              /* dentro del borde, comparte el fondo */
  border: 2px solid #333;
  margin: 24px;              /* fuera del borde, siempre transparente */
  background: #f4f4f4;       /* rellena contenido + relleno, se detiene en el borde */
}

Cada elemento renderizado genera al menos una caja rectangular, y el modelo de caja es la especificación de sus cuatro regiones anidadas. De dentro hacia afuera: la caja de contenido (el área que contiene el texto, imagen o cajas secundarias del elemento), la caja de relleno (contenido más relleno, el espacio transparente que aún lleva el fondo del elemento), la caja de borde (caja de relleno más el borde, el borde visible), y la caja de margen (caja de borde más margen, la región transparente más externa que separa el elemento de sus vecinos).

Dos consecuencias surgen de este anidamiento que te atrapan después. Primero, los fondos pintan la caja de relleno, no la caja de margen, así que el relleno te da espacio interior coloreable y clickeable mientras el margen te da separación vacía que no puedes diseñar. Segundo, el ancho y la altura que declaras no describen toda la caja por defecto; solo describen una región de ella, y cuál región depende de box-sizing, que las secciones siguientes hacen precisa.

css
.card {
  padding: 16px;
  border: 2px solid #333;
  margin: 24px;
  background: #f4f4f4;       /* pinta contenido + relleno, nunca margen */
}
JunoCada elemento es una caja Cada elemento es una caja con cuatro capas de dentro hacia afuera: contenido, luego relleno, luego borde, luego margen. La imagen de la foto enmarcada se quedó conmigo: foto, marco, marco, hueco al siguiente marco. Consigue esa imagen en tu cabeza y el resto de este capítulo se sigue de ella.
JunoCada elemento es una caja Contenido, relleno, borde, margen, envolviendo hacia afuera en ese orden. El fondo rellena el contenido y el relleno y se detiene en el borde, y el margen es siempre transparente, por lo que margin no toma color. Mantén el anidamiento recto y las reglas de espaciado dejan de sentirse arbitrarias.
JunoCada elemento es una caja Cuatro regiones anidadas: caja de contenido, caja de relleno, caja de borde, caja de margen. El fondo pinta hasta la caja de borde y nunca el margen, así que el relleno es interior coloreable y el margen es separación vacía. Y tu width declarado solo dimensiona una de estas regiones, que es la razón completa por la que existe la siguiente sección en box-sizing.

padding, border, y margin

Las tres capas de espaciado cada una hacen un trabajo. Padding es espacio en el interior, entre el contenido y el borde. Margin es espacio en el exterior, empujando otros elementos lejos. El borde es la línea trazada entre ellos.

Puedes establecer un valor para los cuatro lados a la vez, o dirigirse a un solo lado:

css
.card {
  padding: 20px;             /* los cuatro lados obtienen 20px */
  margin-bottom: 32px;       /* solo el borde inferior */
}

La abreviatura padding: 20px es una forma rápida de decir arriba, derecha, abajo e izquierda todos juntos. Cuando necesitas un lado diferente del resto, usa la propiedad específica como margin-bottom.

Padding es espacio dentro del borde, margin es espacio fuera de él, y el borde es el borde entre ellos. La regla a mantener es que margin empuja a los vecinos lejos, padding crece el propio interior del elemento. Si un fondo se ve apretado contra su texto, ese es un trabajo de padding. Si dos elementos se sientan demasiado cerca, ese es un trabajo de margin.

Cada uno toma una abreviatura que rellena los lados en sentido de las agujas del reloj desde la parte superior. Un valor se aplica a los cuatro lados, dos valores establecen vertical luego horizontal, cuatro valores van arriba, derecha, abajo, izquierda:

css
.hero {
  padding: 40px 24px;        /* 40px arriba y abajo, 24px izquierda y derecha */
  margin: 0 auto;            /* sin margen vertical, auto izquierda y derecha para centrar */
}

.card {
  padding-left: 16px;        /* por lado cuando solo un borde difiere */
}

Padding, borde y margen son tres controles de espaciado independientes con comportamientos diferentes, no tres nombres para el mismo hueco. Padding es la región transparente dentro del borde que aún lleva el fondo y aún responde a clics. Margin es la región transparente fuera del borde que separa el elemento de sus hermanos y no lleva el fondo. El borde es el borde trazado que también toma espacio de diseño igual a su ancho.

Los tres aceptan la abreviatura en sentido de las agujas del reloj (uno, dos, tres o cuatro valores expandiendo a arriba / derecha / abajo / izquierda), y los tres tienen longhands por lado. Dos comportamientos valen la pena sostener precisamente. Los márgenes pueden ser negativos, que tira un elemento hacia o sobre un vecino y es una herramienta de diseño legítima, mientras que padding no puede ir por debajo de cero. Y margin: 0 auto centra un bloque horizontalmente solo cuando el elemento tiene un ancho definido, porque auto se resuelve dividiendo el espacio horizontal sobrante, y no hay espacio sobrante para dividir si la caja ya llena su contenedor.

css
.hero {
  padding: 40px 24px;        /* pares en sentido de las agujas: vertical, horizontal */
  margin: 0 auto;            /* centra solo con un ancho establecido en .hero */
  border-bottom: 1px solid #ddd;  /* borde longhand, un borde, toma 1px de diseño */
}

.card {
  margin-top: -8px;          /* margen negativo tira la tarjeta hacia arriba */
}
Junopadding, border, y margin Padding es espacio dentro del borde, margin es espacio fuera de él, y el borde es la línea entre. Usa padding cuando el texto se siente apretado dentro de una caja, y margin cuando dos cajas se sientan demasiado cerca. Un valor como padding: 20px cubre los cuatro lados, o nombra un lado como margin-bottom cuando solo uno necesita cambiar.
Junopadding, border, y margin Padding crece el propio interior del elemento, margin empuja a sus vecinos lejos, y saber cuál es el que quieres ahorra mucho adivinanzas. La abreviatura va en sentido de las agujas del reloj desde la parte superior: un valor para todos los lados, dos para vertical luego horizontal. Cuando un fondo abraza su texto demasiado apretado es padding lo que quieres, no margin.
Junopadding, border, y margin Padding lleva el fondo y clics, margin es separación transparente, y el borde toma espacio de diseño real igual a su ancho. Los márgenes pueden ir negativos para tirar elementos juntos, padding no. Y margin: 0 auto solo centra un bloque que tiene un ancho definido, porque auto no tiene espacio sobrante para dividir de otra manera.

box-sizing: content-box vs border-box

Aquí viene la sorpresa que atrapa a casi todos. Estableces una tarjeta a width: 300px, añades algo de relleno, y la tarjeta termina siendo más ancha que 300 píxeles en la página. No hiciste nada mal. Por defecto, width solo mide el contenido en el medio, y cualquier relleno y borde se añaden encima.

Hay una solución de una línea que hace que el ancho se comporte como esperabas. Establece box-sizing: border-box, y ahora el ancho que escribes es el ancho completo de la caja, relleno y borde incluidos:

css
.card {
  box-sizing: border-box;    /* el ancho ahora incluye relleno y borde */
  width: 300px;
  padding: 20px;             /* la tarjeta se mantiene en 300px de ancho, no 340px */
}

Por defecto cada elemento usa box-sizing: content-box, lo que significa que el width que estableces se aplica solo a la caja de contenido. Luego se añaden relleno y borde fuera de él, así que la caja que realmente ves en la página es más ancha que el número que escribiste. Este es el gotcha clásico de dimensionamiento detrás de cien diseños rotos.

Haz las matemáticas una vez y es claro. Con content-box, un ancho de 300px más 20px de relleno en cada lado más un borde de 2px en cada lado renderiza en 300 + 40 + 4 = 344px. Cambia a border-box y width ahora significa la caja de borde: el relleno y el borde se restan desde adentro, así que el elemento se mantiene exactamente en 300px y el contenido se encoge para encajar.

css
.card {
  box-sizing: content-box;   /* el predeterminado */
  width: 300px;
  padding: 20px;
  border: 2px solid #333;
  /* ancho real renderizado: 300 + 20 + 20 + 2 + 2 = 344px */
}

Porque esto es casi siempre lo que quieres, el movimiento estándar es establecerlo globalmente con el selector universal para que cada elemento se opte:

css
* {
  box-sizing: border-box;    /* una regla, dimensionamiento predecible en todas partes */
}

box-sizing decide qué región tu width y height miden, y tiene dos valores. El valor inicial es content-box: width establece la caja de contenido, y relleno y borde se diseñan fuera de ese ancho, así que la caja de borde, la huella real en la página, es width + padding-left + padding-right + border-left + border-right. La alternativa es border-box: width establece la caja de borde directamente, y el navegador resta relleno y borde desde adentro, encogiéndose la caja de contenido a lo que queda.

La aritmética vale la pena decirla exactamente, porque es toda la razón por la que la propiedad existe. Dado width: 300px; padding: 20px; border: 2px:

css
/* content-box (predeterminado): relleno y borde se añaden encima */
.card {
  box-sizing: content-box;
  width: 300px;
  padding: 20px;
  border: 2px solid #333;
  /* caja de borde = 300 + 40 + 4 = 344px; caja de contenido = 300px */
}

/* border-box: relleno y borde comen en el ancho declarado */
.card {
  box-sizing: border-box;
  width: 300px;
  padding: 20px;
  border: 2px solid #333;
  /* caja de borde = 300px; caja de contenido = 300 - 40 - 4 = 256px */
}

Recomienda border-box: hace que el dimensionamiento sea componible. La razón es práctica: con border-box, un conjunto de columnas declaradas en width: 25% se mantienen en un cuarto del contenedor sin importar qué relleno añadas, porque el relleno ya no cambia el tamaño externo. Esa predictibilidad es por qué el reinicio casi universal la aplica a todo, generalmente incluyendo pseudo-elementos, así nada se desliza atrás a content-box:

css
*, *::before, *::after {
  box-sizing: border-box;
}

Una nota de precisión: esto solo rige width y height. Los márgenes siempre están fuera de la caja de borde independientemente de box-sizing, así que nunca cuentan hacia el tamaño declarado, que se conecta directamente al colapso de margen a continuación. Si necesitas unidades para dimensionar estas cajas de forma confiable, unidades de dimensionamiento cubre cómo se resuelven porcentajes, rem y unidades de viewport.

Junobox-sizing: content-box vs border-box Por defecto, el width que estableces solo mide el contenido en el medio, así que relleno y borde hacen la caja más grande que el número que escribiste. Establecer box-sizing: border-box lo arregla: el ancho se convierte en la caja completa, relleno y borde incluidos. Este me atrapó durante años, así que vale la pena recordarlo temprano.
Junobox-sizing: content-box vs border-box El predeterminado content-box significa que width solo dimensiona el contenido, así que relleno y borde empujan la caja real más ancha de lo que pediste. Una tarjeta de 300px con 20px de relleno y un borde de 2px realmente renderiza en 344px. Cambia a border-box y el ancho significa la caja completa, que es por qué establecerla en * globalmente es la primera línea estándar de la mayoría de las hojas de estilos.
Junobox-sizing: content-box vs border-boxcontent-box añade relleno y borde fuera de tu ancho, así que una caja de 300px se convierte en 344px; border-box los resta desde adentro y la mantiene en 300px. Usa border-box porque los anchos de porcentaje se mantienen componibles cuando el relleno ya no cambia el tamaño externo. Aplicalo a * más los pseudo-elementos para que nada silenciosamente caiga de vuelta al predeterminado.

Colapso de margen

Una sorpresa más, y es sobre márgenes apilados. Pon dos párrafos uno encima del otro, da al primero un margen inferior de 24px y al segundo un margen superior de 16px, y podrías esperar 40px de hueco entre ellos. Realmente obtienes 24px.

Esto se llama colapso de margen. Cuando dos márgenes verticales se encuentran, el navegador no los suma. Mantiene el mayor de los dos y descarta el más pequeño. Así que el margen más grande gana, y el hueco es 24px, no 40px. Solo sucede con márgenes superior e inferior, nunca con relleno.

css
.intro {
  margin-bottom: 24px;
}
.details {
  margin-top: 16px;
  /* el hueco entre ellos es 24px, el margen más grande, no 40px */
}

Cuando el margen inferior de un bloque se encuentra con el margen superior del siguiente, los dos no se suman. Colapsan en un margen único igual al mayor de los dos. Dale un párrafo margin-bottom: 24px y el siguiente margin-top: 16px, y el espacio entre ellos es 24px, no 40px. Este es el colapso de margen, y solo afecta los márgenes verticales.

La razón para saberlo es que cambia cómo razonas sobre espaciado. Si un hueco es más pequeño que lo que los dos números sugieren, probablemente estés viendo un colapso, y añadir al margen más pequeño no hace nada hasta que pase al más grande. La forma limpia de controlar el ritmo vertical es establecer margen en una dirección solo, por ejemplo un margin-bottom en cada bloque y ningún margin-top, así que el colapso no tiene nada que resolver.

css
.article > * {
  margin-top: 0;
  margin-bottom: 24px;       /* una dirección, así que los huecos son predecibles */
}

El colapso de margen es la regla que los márgenes verticales adyacentes en flujo normal se fusionan en uno, cuyo tamaño es el mayor de los dos (o el más positivo, con negativos manejados sumando el mayor positivo y el más negativo). Flujo normal es el apilamiento predeterminado de arriba a abajo de cajas de bloque antes de que cualquier flex, grid o float lo cambie. Dale a .intro un margen inferior de 24px y a .details un margen superior de 16px y el hueco es 24px, porque el navegador resuelve ambos en un margen colapsado único.

La parte precisa es saber qué sí y qué no colapsa, porque las escapadas son cómo paras una sorpresa:

  • Afecta solo márgenes verticales. Los márgenes horizontales nunca colapsan; dos elementos lado a lado siempre suman sus márgenes enfrentados.
  • Cualquier cosa entre los dos márgenes detiene el colapso: un borde, cualquier relleno, o contenido en el padre entre el margen de un hijo y el suyo propio.
  • Los márgenes no colapsan a través de un límite de contexto de formato. Un contenedor flex o grid establece uno para sus hijos, así que los elementos dentro nunca colapsan sus márgenes, que es una razón por la que los diseños modernos se sienten más predecibles que los de la era de float.
  • Un padre y su primer o último hijo pueden colapsar a través uno del otro cuando nada los separa, así que el margen superior de un hijo puede aparecer inesperadamente fuera del padre. Añadir padding-top o un borde al padre lo detiene.
css
/* El colapso sucede: hermanos adyacentes en flujo normal */
.intro { margin-bottom: 24px; }
.details { margin-top: 16px; }   /* el hueco se resuelve en 24px */

/* El colapso se detiene: un borde entre los márgenes */
.section {
  border-top: 1px solid #eee;    /* ahora ambos márgenes se aplican, sin fusión */
}
JunoColapso de margen Cuando un margen inferior se encuentra con un margen superior, el navegador mantiene el más grande y suelta el más pequeño, en lugar de sumarlos. Así que 24px abajo encontrándose con 16px arriba da un hueco de 24px, no 40px. Solo sucede con márgenes superior e inferior, y sorprende a todos la primera vez.
JunoColapso de margen Los márgenes verticales entre bloques colapsan al mayor de los dos, nunca la suma, así que un hueco puede ser más pequeño que lo que los números sugieren. Cuando eso sucede, crecer el margen más pequeño no hace nada hasta que supere el más grande. Establecer margen en una dirección solo, como margin-bottom en cada bloque, evita todo esto.
JunoColapso de margen Los márgenes verticales adyacentes en flujo normal se fusionan al valor más grande, y los márgenes horizontales nunca lo hacen. Un borde o relleno entre ellos detiene el colapso, y los contenedores flex y grid lo detienen completamente porque hacen un nuevo contexto de formato. Observa el caso padre-y-primer-hijo, donde el margen superior de un hijo escapa del padre hasta que añadas padding-top o un borde para sostenerlo.