wandres.dev
EL MODELO DE CAJA · box-sizing, márgenes y colapso

gap frente a margin: por qué evita el colapso por diseño

Qué hace que gap no pueda colapsar nunca, los cinco problemas de los márgenes que desaparecen con él, dónde funciona hoy y cómo conseguir separación en flujo de bloque.

⏱ 16 min

gap no evita el colapso de márgenes con una regla especial ni con una excepción en la especificación: lo evita porque no hay nada que colapsar. Un margen es una propiedad de cada hijo y por tanto hay dos por cada hueco; gap es una propiedad del contenedor y por tanto hay una sola por hueco. Todo lo demás —que no sobre espacio al final, que funcione al partir en varias líneas, que se cambie desde un solo sitio— se deriva de ese cambio de propietario.

🎯 Al terminar esta lección sabrás
  • Explicar por qué gap no puede colapsar ni dejar espacio sobrante.
  • Enumerar los problemas de los márgenes que desaparecen al cambiar de propietario.
  • Saber en qué modelos de layout funciona gap hoy.
  • Conseguir separación en flujo de bloque sin depender del colapso.

Por qué gap no puede colapsar

El colapso ocurre cuando dos márgenes hablan de la misma arista. Para que haya dos márgenes hacen falta dos declaraciones, una en cada hijo, y eso es exactamente lo que gap elimina.

flowchart TB
subgraph M[Con margenes el hueco lo declaran los hijos]
  H1[Hijo A declara margen inferior] --> HU1[Un solo hueco con dos exigencias]
  H2[Hijo B declara margen superior] --> HU1
  HU1 --> R1[Hay que fundirlas y sobra espacio en los extremos]
end
subgraph G[Con gap el hueco lo declara el contenedor]
  C1[El contenedor declara gap] --> HU2[Un valor por hueco]
  HU2 --> R2[Nada que fundir y ningun extremo afectado]
end
style R1 fill:#f9e2af,color:#11111b
style R2 fill:#a6e3a1,color:#11111b

Además, gap no forma parte del flujo normal: es un parámetro del algoritmo de layout de flex, grid o multicolumna. El colapso de márgenes es un fenómeno del flujo de bloque, y dentro de un contenedor flex o grid ese flujo ya no existe.

Y hay una tercera propiedad que se deriva de la definición: gap describe la separación entre elementos, nunca los extremos. Un contenedor con tres hijos y gap: 1rem tiene dos huecos, no cuatro.

Los cinco problemas que desaparecen

El colapso. Ya está. No hay dos valores que fundir.

El espacio sobrante al final. Con márgenes, el último hijo aporta un margen inferior que nadie quería, y de ahí sale ese :last-child { margin-block-end: 0 } que aparece en todos los proyectos y que hay que repetir en cada componente. Con gap no existe el problema porque no hay hueco después del último.

El envoltorio en varias líneas. Cuando un contenedor flex parte en varias líneas, los márgenes horizontales dejan espacio en los extremos de cada línea y los verticales tienen que ponerse a mano. row-gap y column-gap resuelven ambos ejes con un solo valor cada uno y sin márgenes negativos compensatorios.

/* La solucion antigua, con margenes negativos para compensar los extremos. */
.lista-vieja { display: flex; flex-wrap: wrap; margin: -0.5rem; }
.lista-vieja > * { margin: 0.5rem; }

/* La actual. */
.lista { display: flex; flex-wrap: wrap; gap: 1rem; }

El cambio en un solo sitio. Ajustar el ritmo de una lista con márgenes exige tocar la regla de los hijos y comprobar que no rompe el caso del último. Con gap es un valor en el contenedor.

El componente que declara cosas sobre sus vecinos. El de fondo. Un componente con margin-block-end está afirmando algo sobre lo que vendrá después, que es lo único que no puede saber. Con gap, esa afirmación la hace el contenedor, que sí conoce a todos sus hijos.

Dónde funciona y qué hacer en flujo de bloque

Hoy gap funciona en contenedores flex, en contenedores grid y en multicolumna, donde column-gap controla la separación entre columnas.

Llevarlo al flujo de bloque —que un div con display: block acepte gap entre sus hijos— está especificado y en marcha, pero en 2026 no puedes contar con ello. Mientras tanto hay dos respuestas prácticas y una de ellas es claramente mejor.

La buena: convertir el contenedor en una rejilla de una columna. No cambia nada visible, corta el colapso, y te da gap.

.pila {
  display: grid;
  gap: var(--espacio, 1rem);
}

Los hijos siguen apilándose verticalmente y ocupando el ancho disponible, igual que en flujo de bloque. Lo que cambia es que ahora son elementos grid, sus márgenes no colapsan, y la separación la declara el contenedor.

La aceptable: el selector del búho. Aplica margen solo a partir del segundo hijo, con lo que resuelve el problema del último elemento sin tocar el modelo de layout.

.pila-en-flujo > * + * {
  margin-block-start: var(--espacio, 1rem);
}

Sigue usando márgenes, así que sigue colapsando con los márgenes propios de los hijos, y no resuelve el caso de varias líneas. Es la opción correcta cuando necesitas conservar el flujo de bloque por alguna razón concreta —contención de flotantes, comportamiento de float con el texto— y no lo es en ningún otro caso.

💡
La pila con excepciones

El patrón de la pila se vuelve mucho más útil cuando el espacio es un parámetro heredable y cada nivel puede reajustarlo, en lugar de un número escrito en cada componente.

.pila { display: grid; gap: var(--espacio-pila, 1rem); }
.pila.densa { --espacio-pila: 0.5rem; }
.pila.holgada { --espacio-pila: 2rem; }

/* Y un hijo concreto puede pedir mas aire por encima de el. */
.pila > .separado { margin-block-start: var(--espacio-extra, 1rem); }

Como son elementos grid, ese margen extra no colapsa con nada y se suma limpiamente al gap.

gap no es una propiedad nueva, es un cambio de propietario

Lo que hace gap no se aprecia leyendo su definición, porque su definición es trivial: separación entre pistas. Lo importante es a quién se le pregunta. Durante veinte años, el espaciado en CSS fue una propiedad de cada elemento, y eso obligaba a que cada componente declarase cuánto aire quería a su alrededor sin tener ninguna información sobre qué habría a su alrededor. Todos los problemas clásicos del espaciado son síntomas de esa mala atribución, y una vez la identificas se ven como uno solo. El margen del último elemento sobra porque el elemento no sabe que es el último. Los márgenes colapsan porque dos elementos han opinado sobre el mismo hueco sin coordinarse y alguien tiene que arbitrar. El envoltorio en varias líneas se rompe porque el elemento no sabe si está al principio de una línea. Cambiar el ritmo obliga a tocar los hijos porque son ellos los que lo declaran. Los cuatro desaparecen a la vez en cuanto la separación la declara quien conoce a todos los participantes, y gap no hace nada más que eso. De ahí sale la única regla de arquitectura de espaciado que de verdad hace falta y que conviene aplicar sin excepciones: ningún componente lleva márgenes exteriores; el espacio siempre lo pone el contenedor. Es incómoda al principio porque exige que existan contenedores con nombre —una pila, una fila, una rejilla— en vez de dejar que cada componente se apañe, y esa es la parte que cuesta: no puedes adoptarla a medias, porque un solo componente con margin-block-end te devuelve todos los problemas de coordinación. A cambio, el espaciado deja de ser el lugar donde se acumulan las excepciones y pasa a ser lo que siempre debió ser, un puñado de primitivas de layout con un parámetro.

⚔️ Cambia de propietario
  1. Monta una lista con márgenes y cuenta cuántas reglas de excepción necesitas.
  2. Rehazla con gap y compara el número de declaraciones.
  3. Reproduce el caso de varias líneas con márgenes negativos y sustitúyelo por row-gap y column-gap.
  4. Convierte un contenedor de bloque en rejilla de una columna y comprueba que nada visible cambia salvo el colapso.
  5. Audita un componente tuyo y quítale todos los márgenes exteriores. Anota qué se rompe y quién debería arreglarlo.