box-sizing определяет, что именно входит в заданные width и height элемента. По умолчанию (content-box) они относятся только к содержимому, а padding и border добавляются сверх них - поэтому элемент часто получается шире, чем задано. Значение border-box заставляет width и height включать padding и border, так что итоговый размер остаётся именно таким, каким его задали.
В этом уроке
Попробуй прямо сейчас: удали строку box-sizing: border-box; - блок станет заметно шире 200px, потому что вернётся расчёт content-box по умолчанию.
Что такое box-sizing: как считается ширина по умолчанию (content-box)
box-sizing: content-box - значение по умолчанию, действует даже если само свойство нигде не написано. При нём width и height относятся только к области содержимого, а padding и border добавляются к ней снаружи. Поэтому реальная (видимая) ширина элемента равна width + padding с двух сторон + border с двух сторон - и почти всегда оказывается больше того числа, что написано в width.
width: 200px; padding: 20px; border: 5px solid; (content-box) - реальная ширина получится 250px (200+20+20+5+5), а не 200px.
Ожидание, что элемент с width: 200px всегда займёт ровно 200px места - при content-box это верно только если padding и border равны нулю.
Как border-box меняет расчёт ширины и высоты
box-sizing: border-box переворачивает расчёт: теперь width и height - это итоговый, видимый размер элемента, включающий и padding, и border. Сама область содержимого при этом сжимается: content = width - padding с двух сторон - border с двух сторон. Margin в этот расчёт не входит никогда, ни при content-box, ни при border-box - отступ снаружи элемента всегда добавляется дополнительно.
box-sizing: border-box; width: 200px; padding: 20px; border: 5px solid; - реальная ширина остаётся ровно 200px, а не 250px.
Ставят border-box и ждут, что margin тоже "впишется" в width - margin по-прежнему добавляется снаружи, box-sizing на него не влияет вообще.
Почему box-sizing не наследуется и как включить border-box для всех элементов
box-sizing - это НЕ наследуемое свойство: если поставить его на body или на родительский блок, дочерние элементы это не почувствуют и останутся на content-box по умолчанию. Поэтому на практике box-sizing: border-box почти всегда ставят сразу на все элементы через универсальный селектор * (иногда вместе с *::before и *::after) - это и убирает частую причину "поломанной" вёрстки: при content-box колонка шириной 50% с padding в px реально становится ШИРЕ 50%, а с border-box padding поглощается внутри тех же 50%.
* { box-sizing: border-box; } - стандартный сброс, применяется одинаково ко всем элементам сразу, наследование тут ни при чём.
body { box-sizing: border-box; }, в расчёте что подействует на все элементы страницы - не подействует, потому что свойство не наследуется.
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Задают width в процентах и padding в px при content-box (по умолчанию) | Итоговая ширина получается больше заданного процента, колонки не помещаются в ряд | Поставить box-sizing: border-box - padding войдёт в процент, а не добавится сверху |
| Ставят box-sizing: border-box на body или на родителя, рассчитывая на наследование | Дочерние элементы остаются на content-box - свойство не наследуется | Ставить через универсальный селектор * (или явный приём html + *{box-sizing:inherit}) |
| Применяют border-box только к части элементов макета | В одной вёрстке одновременно два разных способа расчёта ширины - легко запутаться | Ставить сброс сразу на весь документ через *, а не выборочно |
| Считают, что border-box "втягивает" в width ещё и margin | Реальные внешние отступы больше, чем ожидалось при раскладке | Помнить, что margin всегда остаётся снаружи, box-sizing на него не влияет |
Шпаргалка
| Что нужно | Как сделать |
|---|---|
| Значение по умолчанию | box-sizing: content-box; |
| Ширина = только содержимое | content-box (padding и border добавляются сверх width) |
| Ширина = итоговый видимый размер | border-box (padding и border входят в width) |
| Формула реальной ширины (content-box) | width + padding*2 + border*2 |
| Формула реальной ширины (border-box) | ровно width (содержимое = width - padding*2 - border*2) |
| Сбросить на все элементы разом | * { box-sizing: border-box; } |
Коротко: отвечаем на вопросы
Ставить box-sizing: border-box через * для всех элементов сразу - это нормально?
Да, это широко принятая, стандартная практика - так делают в Bootstrap и множестве других реальных проектов, и все современные браузеры это поддерживают. Единственная оговорка возникает в компонентных подходах, где глобальный сброс иногда сознательно переопределяют локально под конкретный компонент, но само по себе применение border-box ко всем элементам безопасно.
Если поставить box-sizing: border-box на body, подействует ли это на все элементы страницы?
Нет. box-sizing не наследуется по умолчанию, поэтому дочерние элементы body всё равно останутся на content-box. Из-за этого box-sizing обычно ставят через универсальный селектор *, либо используют приём html { box-sizing: border-box; } вместе с *, *::before, *::after { box-sizing: inherit; }, который сначала явно передаёт значение по наследованию, а затем позволяет переопределить его для отдельного компонента.