← назад к разделу

Каждый элемент на странице — прямоугольная коробка, даже если выглядит кругом. Из чего состоит эта коробка и как коробки расталкивают друг друга — это блочная модель, самый базовый пласт CSS. Здесь же живут два исторических сюрприза — «неправильная» ширина и схлопывающиеся отступы, — на которые натыкается каждый.

Четыре слоя коробки

Изнутри наружу: content (сам контент) → padding (поле между контентом и рамкой) → border (рамка) → margin (внешний отступ до соседей).

.card {
  width: 300px;
  padding: 16px;
  border: 2px solid #ddd;
  margin: 24px;
}

Практическая разница padding и margin: padding — внутри элемента, по нему кликается элемент, на нём виден фон; margin — снаружи, прозрачен и «не принадлежит» элементу. Расширить кликабельную зону кнопки — padding; отодвинуть кнопку от соседа — margin.

box-sizing: почему width «врёт»

Исторический сюрприз номер один: по умолчанию width задаёт ширину только контента. Карточка выше займёт 300 + 16·2 + 2·2 = 336px. Добавили padding — элемент распух, вёрстка поехала.

Режим box-sizing: border-box меняет смысл width на интуитивный: «вся коробка с padding и border = 300px», контент ужимается сам. Первые строки почти любого проекта:

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

Это настолько стандарт, что встречая «загадочно» распухающие элементы в старом проекте, первым делом проверяйте, есть ли этот сброс.

Ещё о размерах: width/height — жёсткие; min-width/max-width — ограничители поверх. Идиома резиновой колонки текста — max-width: 65ch; width: 100%: тянется, но не шире удобной длины строки. И запомните пары процентов: width: 50% — от ширины родителя, а вот padding: 10% и даже margin-top: 10% считаются тоже от ширины родителя (не высоты!) — исторический факт, на котором строится трюк «блок с фиксированной пропорцией», но который удивляет в вертикальных отступах. Для пропорций сегодня есть честное свойство aspect-ratio: 16 / 9.

Схлопывание отступов: куда пропал margin

Сюрприз номер два. Вертикальные margin соседних блоков не складываются, а схлопываются: побеждает больший.

h2 { margin-bottom: 24px; }
p  { margin-top: 16px; }
/* расстояние между ними — 24px, не 40px */

Для соседних абзацев это даже удобно (интервалы не удваиваются). Неудобны два других случая:

  • Родитель и первый ребёнок. Margin-top первого ребёнка «вытекает» наружу и двигает родителя: шапка карточки отъезжает, хотя у самой карточки отступа нет. Схлопывание сквозь границу родитель-ребёнок останавливают padding или border у родителя, либо современный display: flow-root.
  • Пустые элементы — их верхний и нижний margin схлопываются друг с другом.

Важно знать границы явления: схлопываются только вертикальные margin блочных элементов в обычном потоке. Во flex- и grid-контейнерах схлопывания нет вовсе — ещё одна причина, почему с ними жить проще. Там же отступы между детьми удобнее задавать свойством gap, а не margin.

Отрицательные margin легальны: margin-top: -8px подтягивает элемент вверх, отрицательные боковые — растягивают элемент шире родителя (классический приём «полоса на всю ширину внутри узкой колонки»). Инструмент острый — пользоваться осознанно.

border-radius и box-shadow: география коробки

border-radius скругляет углы — от лёгкого 4px до круга 50% (для квадратного элемента). Значения можно задавать по углам, а через слеш — эллиптически (border-radius: 60px / 30px). Скругление обрезает фон и рамку, но не содержимое: картинка внутри скруглённой карточки торчит углами, пока у карточки нет overflow: hidden.

box-shadow рисует тень по контуру коробки (уже с учётом радиуса): x y blur spread color, ключевое слово inset — тень внутрь, а через запятую — несколько слоёв:

.card {
  box-shadow:
    0 1px 2px rgba(0,0,0,.06),
    0 8px 24px rgba(0,0,0,.10); /* многослойная «мягкая» тень */
}

Тень не участвует в раскладке — не расталкивает соседей и не меняет размер коробки. Этим она удобна для состояний наведения: тень появилась, вёрстка не дрогнула. Тем же свойством часто рисуют «рамку без сдвига»: box-shadow: 0 0 0 2px blue вместо border, который изменил бы размеры.

Коротко

  • Коробка: content → padding → border → margin. Padding — кликабельная зона с фоном, margin — прозрачное расстояние до соседей.
  • box-sizing: border-box на всём — стандартный сброс: width означает всю коробку, а не только контент.
  • Проценты в padding/margin считаются от ширины родителя; для пропорций — aspect-ratio.
  • Вертикальные margin схлопываются: между соседями побеждает больший, margin ребёнка вытекает сквозь родителя без padding/border. Во flex/grid схлопывания нет, отступы там — через gap.
  • border-radius обрезает фон, но не контент (нужен overflow: hidden); box-shadow не влияет на раскладку — идеален для hover-состояний.

Что почитать дальше

  • Поток документа: display и наследие float — как коробки выстраиваются друг за другом.
  • Единицы и адаптивность — px, rem, проценты и clamp: чем измерять коробки.
  • Каскад и специфичность — если пропустили начало цикла.