← Назад к вопросам

Как поведут себя стыкующиеся Margin-Bottom и Margin-Top?

2.0 Middle🔥 142 комментариев
#HTML и CSS

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Margin Collapse (схлопывание маржинов)

Это одна из самых запутанных концепций CSS. Расскажу точно как это работает.

Что такое margin collapse?

Когда два элемента с вертикальными маржинами стоят рядом, браузер не складывает их просто так. Вместо этого берёт БОЛЬШИЙ из двух маржинов.

<!-- Пример -->
<div class="element1">Элемент 1</div>
<div class="element2">Элемент 2</div>

<style>
  .element1 {
    margin-bottom: 30px;
  }
  
  .element2 {
    margin-top: 20px;
  }
</style>

<!-- ОЖИДАЕШЬ: 30px + 20px = 50px расстояния
     ПОЛУЧАЕШЬ: max(30px, 20px) = 30px расстояния
     
     Маржины СХЛОПЫВАЮТСЯ!
-->

Визуальный пример

┌─────────────────────┐
│  Элемент 1          │
│ margin-bottom: 30px │
└─────────────────────┘
         ↓ 30px (НЕ 50px!)
         ↓
┌─────────────────────┐
│  Элемент 2          │
│ margin-top: 20px    │
└─────────────────────┘

Когда маржины схлопываются?

Случай 1: Соседние элементы (siblings)

<div class="box1">Box 1</div>
<div class="box2">Box 2</div>

<style>
  .box1 { margin-bottom: 40px; }
  .box2 { margin-top: 20px; }
</style>

<!-- Расстояние = max(40px, 20px) = 40px -->

Случай 2: Родитель и первый потомок

<div class="parent">
  <div class="child">Потомок</div>
</div>

<style>
  .parent {
    margin-top: 50px;
    /* нет padding, border, или content */
  }
  
  .child {
    margin-top: 30px;
  }
</style>

<!-- Маржин ребёнка "протекает" наружу!
     Маржин родителя также 50px (не 50+30=80)
     
     ЖДЁШЬ: родитель имеет margin-top 50px,
           ребёнок имеет margin-top 30px,
           всего 80px от верхнего элемента
     
     ПОЛУЧАЕШЬ: всего max(50px, 30px) = 50px!
     Маржин ребёнка "отменяет" маржин родителя
-->

Визуально:

БЕЗ margin collapse (с padding):
┌────────────────────────────────┐
│ PARENT                         │
│ padding-top: 10px              │
│  ┌──────────────────────────┐  │
│  │ CHILD                    │  │
│  │ margin-top: 30px         │  │
│  │ (маржин НЕ протекает)    │  │
│  └──────────────────────────┘  │
└────────────────────────────────┘

С margin collapse (БЕЗ padding):
┌────────────────────────────────┐
│ PARENT                         │ <- margin-top протекает!
│ margin-top: 50px               │ <- расстояние = 50px
│  ┌──────────────────────────┐  │
│  │ CHILD                    │  │
│  │ margin-top: 30px         │  │ <- маржин потомка 
│  │ (маржин ПРОТЕКАЕТ наружу)│  │    не добавляется
│  └──────────────────────────┘  │
└────────────────────────────────┘

Как предотвратить margin collapse?

Метод 1: Добавь padding родителю

.parent {
  margin-top: 50px;
  padding-top: 1px; /* даже 1px помогает! */
}

.child {
  margin-top: 30px;
}

/* Теперь маржин ребёнка НЕ протекает */

Метод 2: Добавь border родителю

.parent {
  margin-top: 50px;
  border-top: 1px solid transparent; /* невидимая граница */
}

.child {
  margin-top: 30px;
}

/* Теперь маржин ребёнка НЕ протекает */

Метод 3: Добавь overflow родителю

.parent {
  margin-top: 50px;
  overflow: hidden; /* или auto */
}

.child {
  margin-top: 30px;
}

/* Теперь маржин ребёнка НЕ протекает */

Метод 4: Сделай родителя flex/grid

.parent {
  margin-top: 50px;
  display: flex; /* flex контейнер не схлопывает маржины */
}

.child {
  margin-top: 30px;
}

/* Теперь маржин ребёнка НЕ протекает */

Метод 5: Используй gap вместо margin

.parent {
  display: flex;
  flex-direction: column;
  gap: 20px; /* вместо margin между элементами */
}

/* gap НЕ схлопывается */

Практические примеры

Пример 1: Списки

<ul>
  <li>Элемент 1</li>
  <li>Элемент 2</li>
  <li>Элемент 3</li>
</ul>

<style>
  li {
    margin-bottom: 15px;
  }
</style>

<!-- Маржины между элементами схлопываются!
     Расстояние между li = 15px (не 30px)
-->

<!-- ПРАВИЛЬНОЕ решение -->
<style>
  ul {
    display: flex;
    flex-direction: column;
    gap: 15px; /* используем gap */
  }
  
  li {
    /* margin-bottom удалить */
  }
</style>

Пример 2: Карточка с содержимым

<div class="card">
  <h2>Заголовок</h2>
  <p>Описание</p>
</div>

<style>
  .card {
    background: white;
    padding: 20px; /* ВАЖНО: padding предотвращает collapse */
  }
  
  h2 {
    margin-top: 0; /* дополнительная мера безопасности */
    margin-bottom: 10px;
  }
  
  p {
    margin-top: 0; /* дополнительная мера безопасности */
  }
</style>

Пример 3: Контейнер и потомки

<div class="container">
  <header class="header">Заголовок</header>
  <main class="main">Содержимое</main>
</div>

<style>
  .container {
    margin-top: 30px;
    padding-top: 1px; /* предотвращаем collapse */
  }
  
  .header {
    margin-top: 50px; /* это НЕ добавляется к .container */
  }
</style>

Когда margin collapse НЕ работает

Margin collapse НЕ работает в следующих случаях:

/* 1. Flex контейнеры */
.flex-parent {
  display: flex; /* маржины НЕ схлопываются */
}

/* 2. Grid контейнеры */
.grid-parent {
  display: grid; /* маржины НЕ схлопываются */
}

/* 3. Элементы с position: absolute */
.absolute {
  position: absolute; /* маржины НЕ схлопываются */
}

/* 4. Элементы с position: fixed */
.fixed {
  position: fixed; /* маржины НЕ схлопываются */
}

/* 5. Элементы с float */
.floated {
  float: left; /* маржины НЕ схлопываются */
}

/* 6. Горизонтальные маржины (margin-left, margin-right) */
.horizontal {
  margin-left: 20px; /* НИКОГДА не схлопываются */
  margin-right: 20px; /* НИКОГДА не схлопываются */
}

Отрицательные маржины

Маржины могут быть и отрицательными:

<div class="box1" style="margin-bottom: -10px;">Box 1</div>
<div class="box2" style="margin-top: 20px;">Box 2</div>

<!-- Маржины ВСЕГДА схлопываются даже если отрицательные!
     Результат = max(-10px, 20px) = 20px
     
     Если обе отрицательные:
     margin-bottom: -20px;
     margin-top: -30px;
     Результат = max(-20px, -30px) = -20px (БОЛЬШИЙ из отрицательных)
-->

React компонент с правильными маржинами

// ПЛОХО - ненадёжное расстояние
function CardList() {
  return (
    <div>
      <Card /> {/* margin-bottom: 20px */}
      <Card /> {/* margin-top: 20px */}
      <Card />
    </div>
  );
}

/* Расстояние между карточками = 20px (схлопываются!) */

// ХОРОШО - надёжное решение
function CardList() {
  return (
    <div className="card-list">
      <Card />
      <Card />
      <Card />
    </div>
  );
}

.card-list {
  display: flex;
  flex-direction: column;
  gap: 20px; /* гарантированное расстояние */
}

// ХОРОШО - с padding родителя
function CardList() {
  return (
    <div className="card-container">
      <Card />
      <Card />
      <Card />
    </div>
  );
}

.card-container {
  padding: 10px; /* предотвращаем collapse */
}

Итого

Margin collapse - это когда браузер берёт БОЛЬШИЙ из двух соприкасающихся вертикальных маржинов вместо их суммирования.

Когда происходит:

  • Соседние элементы (верхний margin одного + нижний margin другого)
  • Родитель и первый потомок (если нет padding/border/overflow)

Как избежать:

  1. Используй gap вместо margin (лучший способ)
  2. Добавь padding к родителю (даже 1px)
  3. Добавь border к родителю
  4. Используй flex или grid контейнеры
  5. Добавь overflow: hidden

Помни:

  • Горизонтальные маржины (left/right) НИКОГДА не схлопываются
  • Margin collapse работает ТОЛЬКО для вертикальных маржинов
  • Modern подход: используй gap в flex/grid контейнерах
  • Legacy подход: управляй margin collapse с padding/border/overflow
Как поведут себя стыкующиеся Margin-Bottom и Margin-Top? | PrepBro