Margin Collapsed revisado: Márgenes desaparecidos o que empujan ancestros 25.2.18
El problema de los márgenes que colapsan (margin collapsed) empujando a su padre hacia abajo o que entre hermanos desaparece uno de ellos quedando más juntos que lo deseado. A fondo y con varias solucciones.
Margin Collapsed revisado: Márgenes desaparecidos o que empujan ancestros
Artículo publicado originalmente el 24/02/1013. Actualizado en Febrero de 2015, Febrero de 2017 y Febrero de 2018
¿Te ha pasado? Tienes un contenedor principal y dentro tu h1 con un margen superior. Vas al navegador y te encuentras que el background del body deja un hueco en la parte superior. Descubres que el margen del h1 en vez de afectarle a él separándolo de su contenedor afecta a un abuelo suyo (el body).
Esta es una situación que suele desquiciar un poco a quien se está iniciando en Css. Porque por más que pones todos los márgenes habidos y por haber a cero, el espacio continúa ahí.
Por si has tenido la suerte de no saber de qué hablo, mira el ejemplo siguiente
See the Pen bNLzdo by Kseso (@Kseso) on CodePen.
Si te fijas en el código verás que el único elemento que tiene declarado un margen es el h1
. Todos los demás están puestos a cero: * {margin: 0;}
Parece magia negra u brujería. En el ejemplo, el margen del h1 se lo queda no su padre (el div que lo contiene) sino su abuelo, el body que se separa esos px de la parte superior de la ventana y deja ver el fondo del html (la franja superior roja).
Además hay otro efecto añadido. Al haber declarado al body
una altura del 100%, el margen del h1
fuerza la aparición del scroll vertical.
El porqué sucede: Collapsed margins
Dice la especificación sobre el comportamiento de los márgenes:
En CSS los márgenes adyacentes (ningún padding o border que los separe) de dos o más cajas (que pueden estar una al lado de la otra o anidadas) se combinan para formar un solo margen.
Los márgenes laterales (horizontales) no se combinan o cierran entre ellos, sólo los superiores e inferiores (verticales).
Esto es lo que se conoce como márgenes cerrados o collapsed margins. Y ya que estamos con lo que dice la especificación, un poco más de ella para terminar de comprender este comportamiento:
- Dos o más márgenes verticales adyacentes de cajas de bloques en el flujo normal se cierran. El ancho del margen resultante es el máximo de los anchos de los márgenes adyacentes. En el caso de márgenes negativos, el máximo absoluto de los márgenes adyacentes negativos es restado del máximo de los márgenes adyacentes positivos. Si no hay ningún margen positivo, el máximo absoluto de los márgenes adyacentes negativos es restado de cero.
- Los márgenes verticales entre una caja flotante y cualquier otra caja no se cierran.
- Los márgenes de cajas con posiciones absoluta y relativa no se cierran.
Tengo una mala noticia para ti, ahí fuera no hay ninguna propiedad que controle el "collapsed margins" a voluntad a diferencia de los bordes adyacentes que sí se controlan: border-collapse
Así que con lo anterior ya sabes la raíz del problema o qué está sucediendo. Y lo más importante, te ahorras fatigas y minutos de intentos vanos para hacer desaparecer un margen que no existe (el del body del ejemplo) y que se manifieste el que sí está declarado en el Css (el del h1).
Pero no preocuparse, pese a que este cierre de márgenes se hace de forma automática y que no haya algo como margin-collapse: none al estilo de los bordes en las tablas, sí hay soluciones. Y varias posibles.
El cierre de márgenes (margin collapsed) y tipo de display
Antes de pasar a ver las formas de evitar que los márgenes colapsen hay que tener presente que es un efecto dependiente del tipo de display
usado.
Por lo tanto el efecto del margin collapse
no se producirá:
- Entre elementos con
display: table-cell
odisplay: table-row
- En el espacio del
display: grid
los márgenes de sus ítems no colapsan. - En el espacio del
display: flex
los márgenes de sus ítems no colapsan.
Evitando el cierre de márgenes (margin collapsed)
Si no estás en las situaciones anteriores (tipo de display) y quieres evitar el cierre de márgenes, puedes optar por alguna de las siguientes solucciones.
Básicamente el colapso de los márgenes se evita si entre los márgenes que se cierran se intrpone algún tipo de padding, border, o elemento del tipo "inline" (y un espacio en blanco textual
-no de barra espaciadora- también lo es.
Pero recuerda que cada una de las respuestas al cierre de los márgenes tendrá sus efectos no deseados en según qué casos. Así que si te topas con el collapsed margins
debes evaluar cuál se adapta mejor en cada situación.
Actuando sobre el padre
Si vuelves a la primera cita de este artículo de las especificaciones, verás que una condición es que no haya paddings o bordes interpuestos entre los márgenes. Así que sólo es necesario añadir un borde entre el margen del h1 y el borde cerrado. Esto es, al div que contiene al h1. Arreglado como ves en la captura de abajo:
El mismo resultado se obtiene si en vez de declarar un borde se declara un padding
.
Ahora el "desplazamiento" que hemos anulado en el body se ha trasladado al interior del div, separando el h1 del borde superior según el valor declarado para su margen superior y el scroll vertical ya no aparece.
Actuando sobre el abuelo
Como en el ejemplo hay implicados tres elementos de forma premeditada (el h1 que tiene el margen, su padre el div y su abuelo el body), también podemos actual sobre el abuelo en vez del padre de la misma forma, vía border
o padding
.
Pero con un resultado ligéramente distinto:
En este caso, el abuelo (el body) queda libre del cierre de los márgenes, no así el div. Por lo tanto desaparece el espacio superior del body pero es el div el que pasa a desplazarse por el margen del h1.
Evita el desborde del margen: overflow
Una tercera vía de soluccionar el cierre de los márgenes es evitar que haya desbordamientos en el padre (el div). Sólo es necesario declarar overflow con un valor distinto de visible
(que es el "valor por defecto").
Elementos flotados y posicionados
Otras posibles soluciones para evitar el colapso de los márgenes pasan no por impedir que se cierren, sino para que no aplique este efecto.
Si vuelves a la segunda cita de las especificaciones, punto 1 y 2, verás que el cierre o colapso de los márgenes no se produce cuando la caja que lo tiene declarado está flotada o posicionada como absolute (y fixed es un tipo de absolute). Lógico. Salen del flujo.
Márgenes colapsados en elementos anidados
Un caso particular se da cuando tenemos varios elementos anidados, uno dentro de otro, dentro del tercero..., como el siguiente html:
<div class="caja a">
<div class="caja b">
<div class="caja c">
<div class="caja d">
<div class="caja e">
Los márgenes superiores e inferiores colapsan, los laterales no.
</div>
</div>
</div>
</div>
</div>
Con el siguiente Css declarado:
.caja {margin: 10px;}
.a {background: #444;}
.b {background: #777;}
.c {background: #aaa;}
.d {background: #ccc;}
.e {background: #eee;}
El resultado obtenido por efecto del cierre de los bordes es "sorprendente" si no cuentas con él. Es el de la imagen superior de la derecha. Sólo tenemos las franjas grises en los laterales de los elementos, no en la parte superior e inferior de los divs.
En esta situación se corrige con cualquiera de los métodos mencionados antes. Basta añadir un padding a los divs para que aparezcan todos los tonos de grises en los cuatro laterales de todas las cajas anidadas. Y si el añadir este px extra .caja {padding: 1px;} supone un quebranto siempre puedes recurrir o a restarlo del margen o usar la variante del modelo de caja box-sizing
.
Colapso de márgenes entre hermanos
@lucascepeda
— Kseso (@Kseso) 15 de febrero de 2015
Englobados en uno sólo: los que ignoran el Margin Collapsed
xD ;-)
En el caso anterior los elementos implicados estaban anidados, unos dentro de otros. Por eso las solucciones de interponer en un ancestro un borde, padding o evitar debordamientos, repito, en el ancestro, eran efectivas. Pero hay otro caso de cierre de márgenes o collapsed margins en que estas acciones no son válidas.
De nuevo a las especificaciones. Primera cita. Casos en que se produce el colapso: elementos anidados o adyacentes. Esto es, hermanos. Como la imagen de la derecha o este jsfiddle
Como ves, pese a tener un margen superior e inferior de 20px, entre los hermanos sólo hay una separación de 20px, no de 40.
En estas situaciones basta con intercalar en el html entre los hermanos cualquier elemento. Un simple espacio en blanco , no de teclado sino como entidad html, surte efecto y la separación vertical entre hermanos es la suma de sus márgenes. Puedes verlo en el siguiente pen, entre los dos últimos div
´s hay un
intercalado:
See the Pen LEQqzG by Kseso (@Kseso) on CodePen.
Márgenes positivos y negativos
Un caso particular de cierre de márgenes que colapsan se dan cuando uno de los valores es negativo. Tengamos el caso de dos hermanos:
.uno {margin-bottom: 20px;}
.dos {margin-top: -10px;}
En este caso la distancia que los separa es la suma de sus márgenes: 20px+(-10px)=10px
Si ocurriese que el resultado fuese un valor negativo el segundo elemento se dibujará tapando una parte del primero.
Kseso
the obCSServer ᛯ Ramajero Argonauta, Enredique Amanuense de CSS.#impoCSSible inside
Dicen que, en español, EsCss es el mejor blog de CSS. Posíblemente exageren.
@Kseso EsCss Kseso
En el último ejemplo de donde sale -10px? No será -5px?
ResponderEliminarDe un lapsus mio, George. Bien visto.
ResponderEliminarGracias.
Muchas gracias compañero, estoy iniciando en esto y no entendía porque pasaba esto.
ResponderEliminarGracias.
Excelente info, yo también me considero de los que ignoraban Margin collapsed.
ResponderEliminarMuchas gracias por compartir.
Un detalle sobre el agregado de '1px' ... si se usa '.01px' se "colapsa" igual, y en la práctica es '0px'. Es un truco para luego no compensar el pixel entero en otro valor.
ResponderEliminar¡Aja! la vieja técnica de achicar el píxel, Furoya
EliminarVeo que sigue funcionando, aunque creo recordar que tradicionalmente tenía el peligro de las inconsistencias entre navegadores (y versiones) al manejar valores no enteros en píxeles.
Un saludo
Tal cual. Hasta hace pocos años algunos navegadores ignoraban el subpixel, así que ponerlos con decimales era lo mismo que nada. Ahora la cosa ha cambiado bastante, por eso me pareció que se justificaba el comentario, aprovechando tu actualización del artículo.
EliminarGracias, y un saludo para vos también.
Porque colapsa el margen derecho si intento poner en body un width:100% y un margin de 20px?
ResponderEliminarCódigos, Pablo, códigos por favor.
EliminarYa se ha demostrado que como adivino no tengo futuro 😎
Un saludo
Buenos Días Señor. Mi nombre es Yelitza Lazaro, estudiante de la carrera Diseño Gráfico. mi motivo por el cual redacto estas palabras es porque en la materia principal de la carrera "Diseño" nos mandan a elaborar una revista sobre "trancscion de colores" del cual tengo que entrevistar a personas q se basen en este tema o tenga relación. Me gustaria saber si usted estaria de acuerdo ayudarme en este tema respondiendo unas series de preguntas dadas sobre el tema.
ResponderEliminarSin más nada que decir, espero su respuesta sobre lo dicho. feliz día..
Hola Yelitza Lazaro:
EliminarAunque quisiera ayudarte en poco podría hacerlo debido a mi desconocimiento y falta de experiencia en lo relativo a los campos que mencionas.
A poco que conozcas el blog y y su temática te darás cuenta de ello.
Ya lo siento, pero he de serte honesto.
Un saludo
.caja {margin: 10px;}
ResponderEliminar.a {background: #444;}
.b {background: #777;}
.c {background: #aaa;}
.d {background: #ccc;}
.e {background: #eee;}
Y donde esta la clase .caja ?
"Y donde esta la clase .caja?
EliminarLa clase '.caja', como cabría esperar Dennis, debe estar en la hoja de estilos o CSS y el elemento con clase ('class="caja"... />' en el html, tal como puedes ver en los códigos identificados y resaltados como "Código 1" y "Código 2" en el post.
Por si sigues sin verlo te adjunto captura de pantalla:
[img]https://4.bp.blogspot.com/-Mxvo0DDQFTA/XEHClBP5dZI/AAAAAAAAAGc/w4P4peChsX8zrqFzB45ycE6CMiaUXIe4ACLcBGAs/s800/Captura.JPG[/img]
Un saludo