Блочная модель: из чего сделана каждая коробка
Почему элемент занимает больше места, чем ты задал, и как починить это одной строкой box-sizing.
Что ты научишься делать
- Разберёшь элемент на четыре слоя: content, padding, border, margin
- Поймёшь, почему 300px превращаются в 340px
- Научишься чинить это одной строкой box-sizing
#Каждый элемент — коробка
Что бы ты ни написал в разметке — абзац, картинку, кнопку — браузер видит прямоугольник. И у каждого прямоугольника четыре слоя, вложенных друг в друга.
| Слой | Что это | Виден? |
|---|---|---|
content | Само содержимое: текст, картинка | Да |
padding | Внутренний отступ между содержимым и рамкой | Красится фоном элемента |
border | Рамка вокруг padding | Да, если задана |
margin | Внешний отступ до соседей | Нет, всегда прозрачный |
#Главная ловушка: 300 превращается в 340
Ты задал карточке width: 300px. Добавил padding: 16px и border: 4px. Открыл — карточка занимает 340 пикселей.
Так работает значение по умолчанию: width задаёт ширину только содержимого. Отступы и рамка добавляются сверху.
.card { width: 300px; /* ширина содержимого */ padding: 16px; /* + 16 слева и 16 справа */ border: 4px solid; /* + 4 слева и 4 справа */}/* Итого на странице: 300 + 32 + 8 = 340px */Пока карточка одна, это терпимо. Но как только ты ставишь три карточки по 33 % в ряд шириной 900px — они не помещаются, потому что каждая на самом деле шире, чем 300.
#Починка одной строкой
*, *::before, *::after { box-sizing: border-box;}border-box меняет смысл слова width: теперь оно означает ширину всей коробки вместе с отступами и рамкой. Задал 300 — получил 300, а содержимому осталось 300 − 32 − 8 = 260.
Это первое, что пишут в любом проекте. Считать размеры становится просто, и вёрстка перестаёт неожиданно расползаться.
Так не надо
.col {
width: 33.33%;
padding: 16px;
}
/* три колонки не влезают в строку */Проценты считаются от содержимого, а отступы добавляются сверху. Сумма получается больше 100 %, и третья колонка падает вниз.
Так надо
* { box-sizing: border-box; }
.col {
width: 33.33%;
padding: 16px;
}
/* ровно три в строку */Теперь 33.33 % — это ширина всей колонки вместе с отступами. Три штуки дают ровно 100 %.
#Сокращённые записи
У padding и margin одинаковая логика записи. Количество значений определяет, к каким сторонам они относятся.
| Запись | Значение |
|---|---|
padding: 16px | Все четыре стороны |
padding: 16px 24px | Сверху и снизу 16, слева и справа 24 |
padding: 16px 24px 8px | Сверху 16, по бокам 24, снизу 8 |
padding: 16px 24px 8px 12px | По часовой стрелке: верх, право, низ, лево |
padding-top: 16px | Только одна сторона |
#Схлопывание вертикальных отступов
Странность, которая сбивает с толку всех: у первого абзаца margin-bottom: 20px, у второго margin-top: 30px. Между ними будет не 50 пикселей, а 30.
Соседние вертикальные отступы схлопываются: побеждает больший, а не сумма. С горизонтальными такого не происходит.
Схлопывание отключается, если между отступами есть хоть что-то: рамка, внутренний отступ, или если родитель стал флекс- либо грид-контейнером. Именно поэтому во флекс-раскладке схлопывания нет — и почему многие просто задают отступы только в одну сторону.
#width, height и ограничители
Жёстко заданная высота — почти всегда ошибка: текста может оказаться больше, и он вылезет за пределы блока. Ширину тоже лучше ограничивать, а не фиксировать.
| Свойство | Что делает | Когда брать |
|---|---|---|
width | Жёсткая ширина | Редко: иконки, аватарки |
max-width | Не шире, но может быть уже | Почти всегда: контейнеры, картинки, текст |
min-width | Не уже, но может быть шире | Кнопки, ячейки таблиц |
height | Жёсткая высота | Почти никогда |
min-height | Не ниже, но может вырасти | Экранные блоки, карточки одинаковой высоты |
.container { /* до 1200px тянется, дальше стоит по центру */ max-width: 1200px; margin: 0 auto; padding: 0 24px;}/* Картинка никогда не вылезет за родителя */img { max-width: 100%; height: auto;}Где спотыкаются почти все
Не задан box-sizing: border-box
Почему Все расчёты ширины ломаются: колонки не помещаются, сетка расползается. Причину найти сложно, потому что «я же написал 33 процента».
Как надо Первым правилом в файле: *, *::before, *::after { box-sizing: border-box; }
Отступы между блоками заданы с двух сторон
Почему Из-за схлопывания получается не то расстояние, которое ожидал, и в разных местах по-разному.
Как надо Договорись с собой: только margin-bottom. Или используй gap во флексах и гридах.
Жёсткая высота у блока с текстом
Почему Текст длиннее ожидаемого — и он вылезает за рамку или обрезается.
Как надо Бери min-height: блок не будет ниже заданного, но при необходимости вырастет.
Отступ у последнего элемента списка
Почему Снизу образуется лишний зазор, из-за которого блок выглядит несимметрично.
Как надо .item:last-child { margin-bottom: 0; } — или просто перейди на gap.
- Блочная модель box model
- Четыре слоя любого элемента: content, padding, border, margin.
- Внутренний отступ padding
- Расстояние от содержимого до рамки. Закрашивается фоном элемента.
- Внешний отступ margin
- Расстояние до соседних элементов. Всегда прозрачный.
- box-sizing box-sizing
- Определяет, входят ли padding и border в заданную ширину.
- Схлопывание отступов margin collapsing
- Соседние вертикальные margin объединяются в один, равный большему.
Проверь себя
200 + 20·2 + 5·2 = 250. При <code>content-box</code> отступы и рамка добавляются к заданной ширине.
Теперь 200px — это вся коробка. Содержимому остаётся 200 − 40 − 10 = 150.
Вертикальные отступы схлопываются: побеждает больший, а не сумма.
Фон растекается под содержимое и внутренний отступ, до внешнего края рамки. Margin всегда прозрачный.
Жёсткий <code>width: 1200px</code> на телефоне вызовет горизонтальную прокрутку. <code>max-width</code> — нет.
Задание
Карточка товара с точными отступами
Свёрстай карточку по описанию:
- включи
box-sizing: border-boxдля всех элементов; - ширина карточки ровно 280px — вместе с отступами и рамкой;
- внутренний отступ 20px со всех сторон;
- рамка 2px;
- скругление 14px;
- у заголовка внутри — отступ снизу не меньше 8px, сверху нулевой.
Подсказка 1
Первое правило: *, *::before, *::after { box-sizing: border-box; }
Подсказка 2
Рамке нужен стиль и цвет, иначе её не будет видно: border: 2px solid #ddd;
Подсказка 3
Заголовку: .card__title { margin: 0 0 8px; }
Показать решение
<article class="card"> <h2 class="card__title">Раф на кокосовом</h2> <p class="card__text">Мягкий, сладкий, без коровьего молока.</p> <p class="card__price">240 ₽</p></article>*, *::before, *::after { box-sizing: border-box;}.card { width: 280px; padding: 20px; border: 2px solid #e0e0ea; border-radius: 14px; background: #fff;}.card__title { margin: 0 0 8px; font-size: 1.15rem;}.card__text { margin: 0 0 12px; color: #5b6070; font-size: 0.9rem;}.card__price { margin: 0; font-weight: 700;}