Задаёшь карточке width: 300px и padding: 20px, а она выходит шире трёхсот. Ставишь двум абзацам по margin: 12px, а между ними всё равно 12, а не 24. Оба сюрприза объясняет блочная модель — то, как браузер складывает размер элемента из частей.
В CSS два вида отступов: padding — внутренний, между содержимым и рамкой, а margin — внешний, между рамкой и соседями. padding закрашивается фоном элемента, margin всегда прозрачный. Чтобы width включала padding и рамку, ставят box-sizing: border-box, а между элементами во Flexbox и Grid удобнее gap.
Пример: две карточки с отступами
Открой пример в песочнице HTML/CSS ссылкой под вторым блоком: разметка попадёт во вкладку index.html, стили — в styles.css. У карточки рамка, внутренние отступы и отступ снизу до соседней, а у абзацев — свои отступы, которые ведут себя неожиданно.
HTML:
<article class="card">
<h2>Капучино</h2>
<p>Двойной эспрессо и молоко.</p>
<p>Готовим за три минуты.</p>
</article>
<article class="card">
<h2>Раф</h2>
<p>Сливки, ваниль и эспрессо.</p>
</article>
CSS:
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 1rem;
font-family: system-ui, sans-serif;
}
.card {
width: 18rem;
padding: 1rem 1.25rem;
margin-bottom: 1.5rem;
border: 2px solid #3a86ff;
border-radius: 0.75rem;
}
.card h2 {
margin: 0 0 0.5rem;
}
.card p {
margin: 0.75rem 0;
}
padding внутри рамки, margin снаружи
18rem с box-sizing: border-box на содержимое остаётся 244 px: 288 − 2 × 20 padding − 2 × 2 border. margin в ширину не входит — он отодвигает соседей снаружи.padding и margin: внутри и снаружи рамки
padding. Внутренний отступ: отодвигает текст от рамки. У карточки padding: 1rem 1.25rem — 16 пикселей сверху и снизу и 20 по бокам. Фон и обработчик щелчков у элемента включают padding: кнопка с большим padding и нажимается на большей площади.
margin. Внешний отступ: отодвигает соседей. margin-bottom: 1.5rem ставит 24 пикселя между карточками. Margin прозрачный, а ещё бывает отрицательным: margin-top: -10px подтянет элемент вверх. Отрицательного padding не бывает.
Отдельные стороны. У каждого свойства есть варианты для одной стороны: padding-top, margin-left, margin-bottom и так далее. Отступ сверху — margin-top, слева — margin-left.
Короткая запись: от одного до четырёх значений
margin и padding принимают от одного до четырёх значений:
| Запись | Что значит |
|---|---|
margin: 10px | 10 со всех сторон |
margin: 10px 20px | 10 сверху и снизу, 20 слева и справа |
margin: 10px 20px 30px | 10 сверху, 20 по бокам, 30 снизу |
margin: 10px 20px 30px 40px | сверху, справа, снизу, слева — по часовой стрелке |
margin: 0 auto | сверху и снизу 0, по бокам поровну: блок по центру |
Порядок «по часовой стрелке» с верха легко запомнить по слову TRouBLe: top, right, bottom, left. Про margin: 0 auto подробнее в статье про выравнивание по центру.
Блочная модель и box-sizing: из чего складывается ширина
У каждого элемента четыре слоя: содержимое, вокруг него padding, потом рамка, потом margin. По умолчанию width задаёт ширину только содержимого, а padding и рамка прибавляются сверху. Карточка с width: 18rem (288 пикселей), padding 20 по бокам и рамкой 2 тогда займёт 288 + 40 + 4 = 332 пикселя.
box-sizing: border-box меняет счёт: width включает padding и рамку, и карточка ровно 288 пикселей — на содержимое остаётся 244. Так проще считать сетки, поэтому многие страницы начинают CSS с правила для всех элементов:
* {
box-sizing: border-box;
}
Margin в ширину не входит ни в одном режиме: он всегда снаружи.
Схлопывание: почему 12 + 12 = 12
У абзацев в примере margin: 0.75rem 0 — по 12 пикселей сверху и снизу. Казалось бы, между двумя абзацами будет 24, но браузер ставит 12. Это схлопывание: когда вертикальные margin двух блоков встречаются, остаётся только больший из них, а не сумма.
Схлопываются только вертикальные отступы обычных блоков друг под другом. Горизонтальные margin не схлопываются, и внутри Flexbox и Grid отступы тоже не схлопываются. Ещё один случай — margin первого ребёнка, который «вылезает» за родителя без padding и рамки: заголовок с margin-top сдвигает вниз весь блок. Лечится padding у родителя или display: flow-root.
gap: отступы между элементами без margin
Во Flexbox и Grid расстояние между элементами задают одним свойством у родителя:
.cards {
display: grid;
gap: 1.5rem;
}
gap ставит отступ только между элементами — ни перед первым, ни после последнего, — и с ним не нужно убирать margin у крайнего. Подробнее — в статьях про Flexbox и CSS Grid.
Отступ между строками текста — не margin, а line-height: line-height: 1.5 делает высоту строки в полтора размера шрифта.
Три правки, чтобы закрепить
Открой пример в песочнице и перед каждой правкой предскажи результат:
- Убери правило
* { box-sizing: border-box; }. Карточка станет шире: 332 пикселя вместо 288, потому что padding и рамка прибавятся к ширине. - Добавь правило
.card p + p { margin-top: 2rem; }. Между абзацами станет 32 пикселя, а не 12 + 32: из двух встретившихся отступов остаётся больший. - Убери у
.cardстроку сpadding. Текст прижмётся вплотную к рамке: внутреннего отступа больше нет.
Селектор p + p из второй правки — соседний элемент, подробнее в статье про селекторы CSS.