Към съдържанието
wannacode

Box model

Накратко: Box model казва, че всеки елемент е кутия от съдържание, padding, border и margin; с box-sizing: border-box ширината включва padding и рамката.

Проблем

Картите излизат от екрана

Страницата с хлябовете на телефон, широк 360px. Всяка карта е с width: 100%: колкото колоната. А картите излизат вдясно, и цялата страница се плъзга встрани.

HTML, CSS, JS

Колоната е 360 − 2 × 16 = 328px. Картата трябваше да е толкова, а е 362px: 34px по-широка от колоната. Тези 34px не са грешка в числото 100%. Те са в това какво мери width.

Идеята

Четири слоя

За CSS всеки елемент е правоъгълна кутия. Тя има четири слоя, отвътре навън:

  • съдържание (content): текстът, картинката, децата;
  • padding: мястото между съдържанието и рамката. Фонът на елемента го запълва;
  • border: рамката;
  • margin: мястото извън рамката, до съседните кутии. То е прозрачно.

По подразбиране width е ширината само на съдържанието. Padding и border се добавят отвън, и от двете страни:

.card {
width: 100%; /* 328px: съдържанието */
padding: 16px; /* + 2 × 16 */
border: 1px solid #ccc; /* + 2 × 1 = 362px */
}

Затова картата от началото е 362px. padding: 16px е за четирите страни. Може и поотделно: padding: 8px 16px (отгоре и отдолу, отляво и отдясно) или padding-left: 16px.

Виж го

Виж кутията

Кутията се строи слой по слой. Чертата отгоре е width: гледай какво мери тя.

/* по подразбиране: box-sizing: content-box */
.card {
width: 280px;
padding: 24px;
border: 8px solid #2b3a55;
margin: 16px;
}
.card.bb {
box-sizing: border-box;
}
Стъпка по стъпка, с думи
  1. В средата е съдържанието: текстът на картата. По подразбиране width е ширината само на съдържанието: 280px.
  2. padding е мястото между съдържанието и рамката. То е от двете страни: 280 + 2 × 24 = 328px. Фонът на картата го запълва.
  3. border е рамката, пак от двете страни. Кутията вече е 344px, а не 280: width не казваше колко широка е картата на екрана.
  4. margin е мястото извън рамката, между тази кутия и следващата. Няма фон и не е част от картата, но заема място: 376px.
  5. box-sizing: border-box сменя какво значи width: вече е ширината до рамката. Картата е 280px, а съдържанието се свива до 280 − 48 − 16 = 216px.
  6. Един и същ CSS, две значения на width. При content-box картата е 344px, при border-box е 280px: колкото казва width. Затова почти всеки проект слага border-box на всички елементи.

Същите две карти в браузъра. Синьото е абзацът вътре: той е широк колкото съдържанието на картата. В инструментите за разработчици (F12) избери картата: панелът Computed рисува същата кутия с числата на всеки слой.

HTML, CSS, JS

Идеята

box-sizing: border-box

Смятането наум е източник на бъгове: искаш карта, широка колкото колоната, а трябва да вадиш padding и border. box-sizing: border-box сменя значението на width: тя е ширината до рамката включително. Padding и border се вземат отвътре, а съдържанието се свива.

Така width: 100% е наистина колоната, каквото и да е padding-ът. Затова почти всеки проект започва с:

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

* хваща всеки елемент. box-sizing не се наследява, затова правилото е за всички, а не само за <html>. ::before и ::after са парчета съдържание, които CSS добавя сам; и те са кутии.

Пробвай

Вкарай картите

Направи картите да влизат в екрана на телефона със box-sizing: border-box. Padding-ът (16px) и рамката (1px) остават.

HTML, CSS, JS

Правилото, с което почти всеки проект започва:

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

Идеята

Margin-ите се слепват

Две кутии една под друга: долният margin на горната и горният margin на долната не се събират. Те се слепват, и остава по-големият.

HTML, CSS, JS
  • Бележките са с 24px отдолу и 16px отгоре. Между тях има 24px, а не 40px.
  • Заглавието в банера е с margin: 20px 0. Банерът няма padding и рамка отгоре, затова margin-ът на заглавието излиза над него: оранжевият фон започва точно над текста. С padding: 1px 16px на банера фонът обхваща и margin-а.

Слепват се само вертикалните margin-и. Отляво и отдясно те винаги се събират. В flex и grid, които са следващите уроци, не се слепват и вертикалните.

Идеята

block, inline и inline-block

Не всяка кутия се държи еднакво. Свойството display казва как стои кутията сред другите.

  • block: започва на нов ред и заема цялата ширина на родителя. Width, height, margin и padding важат. Такива са div, p, h1, article.
  • inline: стои в реда на текста и е широка колкото съдържанието си. Width и height не важат, вертикалният margin не мести нищо, а вертикалният padding се рисува, но не раздалечава редовете. Такива са span, a, strong.
  • inline-block: стои в реда като inline, но width, height, margin и padding важат като при block.
HTML, CSS, JS

Двата етикета имат едни и същи свойства, а вторият е и inline-block. Първият е inline: width: 64px и margin-ът не важат, а червеното от padding-а излиза над и под реда, без да го раздалечи от другите. Вторият е inline-block: широк е 64 + 2 × 8 = 80px (тук още е content-box), и абзацът става по-висок, за да го побере.

Пробвай

Етикетите

Етикетите −20% и Ново трябва да са широки по 64px и да са един до друг на един ред. Сега ширината не важи.

HTML, CSS, JS

Идеята

Единиците

Досега всичко беше в px. CSS има и други единици, и всяка е за нещо:

  • px: фиксирана мярка. Добра за рамки и малки отстояния.
  • rem: размерът на шрифта на <html> (16px, ако никой не го е сменил). 1.5rem е 24px навсякъде на страницата. Ако човек е увеличил шрифта в настройките на браузъра, rem расте с него, а px не. Затова шрифтовете и отстоянията са добре да са в rem.
  • em: размерът на шрифта на самия елемент; във font-size е този на родителя. Padding в em расте с текста на бутона.
  • %: от родителя. width: 50% е половината от ширината на съдържанието на родителя.
  • vw и vh: 1% от ширината и от височината на прозореца.
HTML, CSS, JS

Заглавието е 1,5rem = 24px. Картата е 50% от 320px = 160px. Шрифтът ѝ е 18px, затова padding: 1em е 18px. 1.25rem на заглавието в картата е 20px: rem не се интересува от 18px на картата. 0.75em на <small> е от шрифта на абзаца, който е наследил 18px: 13,5px.

Пробвай

Намери грешката

Менюто е 20px, подменюто в него е 25px, а следващото ниво е още по-голямо. Кой ред е виновен?

Избери реда с грешката.

По-точно, за любопитните

Процентите в padding и margin са от ширината на родителя, дори отгоре и отдолу. padding-top: 50% прави кутия, висока половината от ширината на родителя.

100vw и лентата за превъртане. vw включва лентата. Ако страницата има видима вертикална лента, елемент с width: 100vw е по-широк от страницата и дава хоризонтален скрол. За „цялата ширина“ width: 100% е по-сигурно.

vh на телефона. Адресната лента се показва и скрива. 100vh е височината, когато лентата е скрита, затова при показана лента долният край е под нея. 100dvh следва истинската височина в момента.

outline се рисува като рамка, но не заема място и не мести нищо. Затова фокусът на бутоните се показва с него.

min-width и max-width слагат граници: max-width: 100% не дава на картинка да излезе от колоната.

Формулярите. В Chromium бутоните са border-box по подразбиране, а полетата (input) са content-box. Правилото с * ги изравнява.

Как да спреш слепването. Margin-ът на детето не излиза през родителя, ако родителят има padding или border отгоре, или ако е display: flow-root. Така родителят прави нов контекст за подреждане (block formatting context).

Отрицателен margin дърпа кутията навън и я застъпва със съседите. Рядко трябва.

Упражнение

Каталогът на телефона

Каталогът на телефон (360px). Сложи border-box на всички елементи, за да влизат картите в екрана. Картата: padding 1rem, рамка 1px solid #ddd и 12px разстояние под всяка. Заглавията на продуктите са 1.25rem. Етикетите −20% и Ново са inline-block, с padding 2px 8px, бял текст и червен фон (#c0392b). Бутонът е широк колкото картата отвътре: width: 100%.

HTML, CSS, JS

Тест

Провери се

1. .box { width: 200px; padding: 10px; border: 2px solid; } без box-sizing. Колко широка е кутията на екрана?
2. Същото правило с box-sizing: border-box. Колко е съдържанието?
3. Един абзац има margin-bottom: 30px, а следващият margin-top: 20px. Колко е разстоянието между тях?
4. <html> е с 16px, родителят е с 20px. Колко е font-size: 1.5rem на детето?
5. Защо width: 80px не прави нищо на <span>?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·