Box model
Накратко: Box model казва, че всеки елемент е кутия от съдържание, padding, border и margin; с box-sizing: border-box ширината включва padding и рамката.
Проблем
Картите излизат от екрана
Страницата с хлябовете на телефон, широк 360px. Всяка карта е с width: 100%: колкото колоната. А картите излизат вдясно, и цялата страница се плъзга встрани.
Конзола
Колоната е 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;}Стъпка по стъпка, с думи
- В средата е съдържанието: текстът на картата. По подразбиране width е ширината само на съдържанието: 280px.
- padding е мястото между съдържанието и рамката. То е от двете страни: 280 + 2 × 24 = 328px. Фонът на картата го запълва.
- border е рамката, пак от двете страни. Кутията вече е 344px, а не 280: width не казваше колко широка е картата на екрана.
- margin е мястото извън рамката, между тази кутия и следващата. Няма фон и не е част от картата, но заема място: 376px.
- box-sizing: border-box сменя какво значи width: вече е ширината до рамката. Картата е 280px, а съдържанието се свива до 280 − 48 − 16 = 216px.
- Един и същ CSS, две значения на width. При content-box картата е 344px, при border-box е 280px: колкото казва width. Затова почти всеки проект слага border-box на всички елементи.
Същите две карти в браузъра. Синьото е абзацът вътре: той е широк колкото съдържанието на картата. В инструментите за разработчици (F12) избери картата: панелът Computed рисува същата кутия с числата на всеки слой.
Конзола
Идеята
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) остават.
Конзола
Решение
index.html<!doctype html>
<html lang="bg">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Хлябове</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>Хлябове</h1>
<article class="card">
<h2>Хляб „Добруджа“</h2>
<p class="price">1,80 €</p>
<button type="button">Добави в кошницата</button>
</article>
<article class="card">
<h2>Ръжен хляб</h2>
<p class="price">2,10 €</p>
<button type="button">Добави в кошницата</button>
</article>
</main>
</body>
</html>
style.css*, *::before, *::after {
box-sizing: border-box;
}
body { font-family: system-ui, sans-serif; margin: 0; }
main { padding: 16px; }
.card {
width: 100%;
padding: 16px;
border: 1px solid #ccc;
margin-bottom: 12px;
}
С border-box width: 100% е 328px заедно с padding и рамката, а за съдържанието остават 328 − 32 − 2 = 294px. width: 90% случайно влиза на този екран, но на друг пак ще смяташ. Без padding картата влиза, но текстът опира в рамката.
Правилото, с което почти всеки проект започва:
*, *::before, *::after {
box-sizing: ;
}border-box: width и height са до рамката включително. Стойността по подразбиране е content-box.
Идеята
Margin-ите се слепват
Две кутии една под друга: долният margin на горната и горният margin на долната не се събират. Те се слепват, и остава по-големият.
Конзола
- Бележките са с 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.
Конзола
Двата етикета имат едни и същи свойства, а вторият е и inline-block. Първият е inline: width: 64px и margin-ът не важат, а червеното от padding-а излиза над и под реда, без да го раздалечи от другите. Вторият е inline-block: широк е 64 + 2 × 8 = 80px (тук още е content-box), и абзацът става по-висок, за да го побере.
Пробвай
Етикетите
Етикетите −20% и Ново трябва да са широки по 64px и да са един до друг на един ред. Сега ширината не важи.
Конзола
Решение
index.html<!doctype html>
<html lang="bg">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Прясно мляко 3%</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<article>
<h2>Прясно мляко 3%</h2>
<p><span class="badge">−20%</span> <span class="badge">Ново</span></p>
<p class="price">1,92 €</p>
</article>
</body>
</html>
style.cssbody { font-family: system-ui, sans-serif; margin: 16px; }
.badge {
display: inline-block;
width: 64px;
padding: 4px 0;
text-align: center;
color: #fff;
background: #c0392b;
}
display: inline-block дава на етикета размерите на кутия, а той остава в реда на текста. С block ширината важи, но всеки етикет е на нов ред. inline е стойността по подразбиране на <span>, затова не променя нищо.
Идеята
Единиците
Досега всичко беше в px. CSS има и други единици, и всяка е за нещо:
px: фиксирана мярка. Добра за рамки и малки отстояния.rem: размерът на шрифта на<html>(16px, ако никой не го е сменил).1.5remе 24px навсякъде на страницата. Ако човек е увеличил шрифта в настройките на браузъра, rem расте с него, а px не. Затова шрифтовете и отстоянията са добре да са в rem.em: размерът на шрифта на самия елемент; въвfont-sizeе този на родителя. Padding в em расте с текста на бутона.%: от родителя.width: 50%е половината от ширината на съдържанието на родителя.vwиvh: 1% от ширината и от височината на прозореца.
Конзола
Заглавието е 1,5rem = 24px. Картата е 50% от 320px = 160px. Шрифтът ѝ е 18px, затова padding: 1em е 18px. 1.25rem на заглавието в картата е 20px: rem не се интересува от 18px на картата. 0.75em на <small> е от шрифта на абзаца, който е наследил 18px: 13,5px.
Пробвай
Намери грешката
Менюто е 20px, подменюто в него е 25px, а следващото ниво е още по-голямо. Кой ред е виновен?
Избери реда с грешката.
em във font-size е от размера на родителя. Всяко вложено .menu умножава по 1,25: 16, 20, 25, 31,25. С font-size: 1.25rem всяко ниво е 20px.
По-точно, за любопитните
Процентите в 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%.
Конзола
Решение
index.html<!doctype html>
<html lang="bg">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Хлябове</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>Хлябове</h1>
<article class="card">
<h2>Хляб „Добруджа“</h2>
<span class="badge">−20%</span>
<p class="price">1,80 €</p>
<button type="button">Добави в кошницата</button>
</article>
<article class="card">
<h2>Ръжен хляб</h2>
<p class="price">2,10 €</p>
<button type="button">Добави в кошницата</button>
</article>
<article class="card">
<h2>Хляб от лимец</h2>
<span class="badge">Ново</span>
<p class="price">3,40 €</p>
<button type="button">Добави в кошницата</button>
</article>
</main>
</body>
</html>
style.css*, *::before, *::after {
box-sizing: border-box;
}
body { font-family: system-ui, sans-serif; margin: 0; }
main { padding: 16px; }
.card {
width: 100%;
padding: 1rem;
border: 1px solid #ddd;
margin-bottom: 12px;
}
.card h2 { font-size: 1.25rem; }
.badge {
display: inline-block;
padding: 2px 8px;
color: #fff;
background: #c0392b;
}
.card button { width: 100%; }
*, *::before, *::after { box-sizing: border-box; } е първото правило: без него картите са 362px. margin-bottom: 12px на картата дава разстоянието. 1rem е 16px, а 1.25rem е 20px, каквото и да е около тях. Бутонът с width: 100% е 294px: колоната е 328px, минус 2 × 16 padding и 2 × 1 рамка на картата.
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: HTML документът · Селектори и каскада
- Продължава в: Flexbox: ред и колона · Grid: редове и колони · Responsive дизайн
- Задачи по темата: CSS: как изглежда страницата