Схлопывание margin (margin collapse) — это когда вертикальные отступы двух соседних блоков не складываются, а «схлопываются» в один — берётся больший из двух. Это не баг, а часть спецификации CSS, но именно из‑за него часто «ломается» верстка: отступы получаются меньше ожидаемых, элементы прилипают друг к другу, а при масштабировании всё едет.
Где вообще бывает схлопывание
Оно работает только для вертикальных margin (top/bottom) и только между:
- соседними родственными элементами (siblings),
- родителем и первым/последним дочерним элементом,
- пустыми блоками (у которых нет ни контента, ни padding, ни border).
Горизонтальные margin никогда не схлопываются — они всегда суммируются.
Пример 1: соседи
<div class="box">Блок 1</div> <div class="box">Блок 2</div>
.box { margin-bottom: 20px; }

Между блоками будет 20 px, а не 40 px. Браузер берёт больший из двух смежных отступов.
Пример 2: родитель и первый ребёнок
<div class="container"> <div class="item">Элемент</div> </div>
.container { margin-top: 50px; } .item { margin-top: 30px; }

Результат: отступ сверху у контейнера будет 50 px (больший из двух), а не 80 px. Margin «вытекает» наружу.
Как гарантированно избежать схлопывания
Есть несколько рабочих способов. Выбирай по задаче.
Способ 1: использовать border или padding у родителя
Любое значение border или padding (даже 1 px) отключает схлопывание между родителем и ребёнком.
.container { padding-top: 1px; /* или border-top: 1px solid transparent; */ } .item { margin-top: 30px; }
Теперь margin ребёнка не будет схлопываться с отступом родителя. Минус: появляется лишний 1 px, но это почти незаметно, а логика становится предсказуемой.
Способ 2: создать BFC (Block Formatting Context)
Самый правильный способ: заставить контейнер создать свой контекст форматирования. Тогда его margin не будет схлопываться с дочерними.
Рабочие варианты:
.container { overflow: auto; /* самый частый вариант */ /* или */ display: flow-root; /* современный и чистый вариант */ }
Пример:
<div class="container"> <div class="item">Элемент</div> </div>
.container { margin-top: 50px; overflow: auto; /* отключает схлопывание */ } .item { margin-top: 30px; }

Теперь сверху будет 80 px: 50 px контейнера + 30 px элемента.
display: flow-root делает то же самое, но без побочных эффектов overflow (вроде появления скролла, если контент чуть больше).
Способ 3: использовать только margin у одного уровня
Если ты контролируешь стили, проще вообще не задавать margin одновременно родителю и ребёнку. Например, делай отступы только у детей:
.item + .item { margin-top: 20px; }
Тогда между элементами будет ровно 20 px, без сюрпризов.
Или наоборот — только у контейнера:
.container { margin: 20px; } /* у .item margin нет */
Способ 4: заменить margin на gap (если можно использовать Grid/Flex)
Для списков и сеток лучше вообще не использовать margin для отступов между элементами, а использовать gap:
.list { display: flex; flex-direction: column; gap: 20px; }
gap не схлопывается и ведёт себя предсказуемо. Это особенно удобно в PunBB‑шаблонах для списков тем, комментариев и т. п.
Частые места, где это реально «ломает» верстку
- Заголовки в контейнерах. Часто делают
margin-topу заголовка иmarginу секции — получается не тот отступ, который ожидаешь. Решение: либо только у заголовка, либо BFC у секции. - Списки и карточки. Если у каждой карточки
margin-bottom, а у списка тоже есть отступ — они схлопнутся. Решение:gapу списка или margin только у карточек, но не у обоих. - Адаптив и масштабирование. При увеличении шрифта в браузере отступы могут «съезжать», если они завязаны на схлопывающиеся margin. Решение: фиксируй логику через BFC или gap.
Практический пример (список тем)
Допустим, у тебя список тем форума, и нужно, чтобы между темами был ровный отступ 16 px, а у самого списка сверху — 24 px.
Плохо (будет схлопывание):
.topics-list { margin-top: 24px; } .topic-item { margin-bottom: 16px; }
Хорошо (гарантированный результат):
.topics-list { margin-top: 24px; display: flow-root; /* отключаем схлопывание с первым .topic-item */ } .topic-item { margin-bottom: 16px; }
Или через gap:
.topics-list { display: grid; grid-template-columns: 1fr; gap: 16px; margin-top: 24px; } .topic-item { margin: 0; /* убираем margin у элементов */ }
Что выбрать в разных ситуациях
- Нужно быстро и надёжно:
display: flow-rootу родителя. - Делаешь сетку/список:
display: grid/flex+gap. - Нельзя менять структуру: добавь
padding-top: 1pxилиborder-top: 1px solid transparentу родителя. - Хочешь максимально чистую логику: используй margin только на одном уровне (либо у родителя, либо у детей).
