Урок 4 из 49 · Месяц 1. HTML5 и CSS3

Позиционирование, единицы измерения и блочная модель

Содержание урока

Каждый элемент на странице — это прямоугольная коробка, и умение управлять её размерами, отступами и положением отличает аккуратную вёрстку от «поехавшей». В этом уроке разберём блочную модель (margin, padding, border, border-radius), единицы измерения px, %, em и rem и свойство position, которое позволяет точно ставить элементы туда, куда нужно.

Блочная модель

Браузер рисует любой элемент как прямоугольник из четырёх слоёв — от центра к краю:

┌──────────────── margin (внешний отступ) ────────────────┐
│  ┌──────────────── border (рамка) ──────────────────┐   │
│  │  ┌──────────── padding (внутренний отступ) ───┐  │   │
│  │  │                                            │  │   │
│  │  │            content (содержимое)            │  │   │
│  │  │                                            │  │   │
│  │  └────────────────────────────────────────────┘  │   │
│  └──────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘

Аналогия — картина в раме на стене:

  • content — сама картина;
  • padding — паспарту, белое поле между картиной и рамой;
  • border — рамка;
  • margin — расстояние от рамы до соседних картин.

Посмотреть блочную модель любого элемента можно в DevTools: Elements → справа вкладка Computed — там нарисована именно такая схема с реальными числами.

padding — внутренний отступ

padding отодвигает содержимое от краёв элемента внутрь. Фон элемента закрашивает и padding.

.button {
  background-color: #2b6cb0;
  color: white;
  padding: 12px 24px;
}

У padding (и у margin) есть четыре стороны, и записать их можно по-разному:

.box {
  padding: 20px;                 /* все четыре стороны */
  padding: 10px 20px;            /* верх-низ 10, лево-право 20 */
  padding: 10px 20px 30px;       /* верх 10, лево-право 20, низ 30 */
  padding: 10px 20px 30px 40px;  /* верх, право, низ, лево */
}

Четыре значения идут по часовой стрелке, начиная сверху: верх → право → низ → лево. Можно задать и одну сторону:

.box {
  padding-top: 10px;
  padding-right: 20px;
  padding-bottom: 10px;
  padding-left: 20px;
}

margin — внешний отступ

margin отталкивает элемент от соседей. Он всегда прозрачный — фон на него не распространяется. Сокращения те же, что у padding.

.card {
  margin-bottom: 24px;    /* отступ до следующей карточки */
}

.container {
  width: 960px;
  margin: 0 auto;         /* центрирование блока по горизонтали */
}

margin: 0 auto — классический способ поставить блок с заданной шириной по центру: сверху и снизу 0, а слева и справа браузер сам распределяет свободное место поровну.

Особенности margin

  • Отрицательные значения допустимы: margin-top: -20px подтягивает элемент вверх.
  • Схлопывание: вертикальные margin соседних блоков не складываются, а берётся больший. Если у одного абзаца margin-bottom: 20px, а у следующего margin-top: 30px, расстояние между ними будет 30px, а не 50px.
  • Браузер сам добавляет отступы некоторым тегам (body, h1, p, ul). Поэтому вёрстку часто начинают со сброса:
body {
  margin: 0;
}

h1, h2, p, ul {
  margin-top: 0;
}

border — рамка

.card {
  border: 1px solid #ddd;
}

Сокращение border принимает три значения: толщина, стиль, цвет. Стиль обязателен — без него рамки не будет.

Стиль Вид
solid Сплошная линия
dashed Штрихи
dotted Точки
double Двойная линия
none Нет рамки

Можно задать рамку только с одной стороны:

.tab {
  border-bottom: 3px solid tomato;   /* подчёркивание активной вкладки */
}

.quote {
  border-left: 4px solid #2b6cb0;    /* полоска слева у цитаты */
  padding-left: 16px;
}

border-radius — скругление углов

.card   { border-radius: 12px; }            /* все углы */
.pill   { border-radius: 999px; }           /* «таблетка» — кнопка с круглыми краями */
.avatar {
  width: 80px;
  height: 80px;
  border-radius: 50%;                       /* квадрат становится кругом */
}
.tag    { border-radius: 8px 0 8px 0; }     /* углы по часовой с левого верхнего */

border-radius работает даже без рамки — скругляются фон и картинка.

box-sizing: как считается ширина

Вопрос: какой ширины будет этот блок на экране?

.box {
  width: 200px;
  padding: 20px;
  border: 5px solid black;
}

По умолчанию (box-sizing: content-box) width — это ширина только содержимого. Padding и border прибавляются сверху: 200 + 20·2 + 5·2 = 250px. Это неудобно: задали 200, а получили 250.

Со значением border-box ширина включает padding и border — блок будет ровно 200px, а под содержимое останется 150px. Поэтому почти все проекты начинают с такого правила:

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

Единицы измерения

px — пиксели

Абсолютная единица: 16px — всегда 16 пикселей. Просто и предсказуемо. Хороши для рамок, мелких деталей и теней.

.card { border: 1px solid #ddd; }

% — проценты

Считаются от родителя. Для width — от ширины родителя, для font-size — от размера шрифта родителя.

.parent { width: 800px; }
.child  { width: 50%; }   /* 400px */

Обратите внимание: padding и margin в процентах считаются от ширины родителя — даже вертикальные.

em — относительно шрифта элемента

1em = текущий font-size элемента. Если у элемента 20px, то 1em = 20px, 0.5em = 10px. Удобно, когда отступ должен расти вместе с текстом:

.button {
  font-size: 18px;
  padding: 0.5em 1em;   /* 9px 18px — пропорционально шрифту */
}

Ловушка em — накопление. Если font-size указан в em, он считается от шрифта родителя, и при вложенности умножается:

<div class="level">Уровень 1
    <div class="level">Уровень 2
        <div class="level">Уровень 3</div>
    </div>
</div>
.level { font-size: 1.2em; }
/* при базовых 16px: 19.2px → 23.04px → 27.65px */

rem — относительно корня

rem (root em) считается от размера шрифта корневого элемента <html>. По умолчанию в браузерах это 16px, значит 1rem = 16px, 1.5rem = 24px, 0.75rem = 12px. Никакого накопления — везде одинаково.

h1 { font-size: 2.5rem; }    /* 40px */
p  { font-size: 1rem; }      /* 16px */
.section { margin-bottom: 3rem; }  /* 48px */

Главный плюс rem: если пользователь увеличит размер шрифта в настройках браузера, все размеры в rem увеличатся пропорционально. Это важно для людей со слабым зрением.

Когда что использовать

Единица От чего считается Где удобна
px Ни от чего, абсолютная Рамки, тени, мелкие детали
% Родитель Ширина блоков и колонок
em Шрифт самого элемента Отступы внутри кнопок и бейджей
rem Шрифт <html> Размеры шрифтов, общие отступы

Есть ещё vw и vh — 1% ширины и высоты окна браузера. Например, height: 100vh — блок на весь экран.

position — позиционирование

По умолчанию элементы идут в нормальном потоке: блочные — сверху вниз, строчные — слева направо. Свойство position позволяет вынуть элемент из этого порядка или сдвинуть его. Вместе с ним используют top, right, bottom, left — смещения от краёв.

static — по умолчанию

.box { position: static; }

Элемент стоит в обычном потоке, а top/left на него не действуют. Писать static явно почти никогда не нужно.

relative — сдвиг относительно своего места

.badge {
  position: relative;
  top: -5px;
  left: 10px;
}

Элемент визуально сдвигается на 5px вверх и на 10px вправо, но его место в потоке остаётся — соседи не двигаются, как будто он стоит на прежнем месте. Главное применение relative — служить точкой отсчёта для absolute.

absolute — точно в нужной точке родителя

<div class="card">
    <span class="card-badge">NEW</span>
    <h3>Капучино</h3>
</div>
.card {
  position: relative;      /* точка отсчёта */
  width: 300px;
  padding: 24px;
  border: 1px solid #ddd;
  border-radius: 12px;
}

.card-badge {
  position: absolute;
  top: 12px;
  right: 12px;
  padding: 4px 8px;
  background-color: tomato;
  color: white;
  border-radius: 6px;
}
  • absolute вынимает элемент из потока: остальные ведут себя так, будто его нет.
  • Координаты считаются от ближайшего предка, у которого position не static (обычно ставят relative). Здесь это .card, поэтому бейдж стоит в её правом верхнем углу.
  • Если такого предка нет, отсчёт идёт от всей страницы — и бейдж «улетит» в угол экрана.

fixed — приклеен к окну

.to-top {
  position: fixed;
  right: 24px;
  bottom: 24px;
}

Элемент отсчитывается от окна браузера и не двигается при прокрутке. Так делают кнопки «наверх», чаты поддержки, плавающие панели.

sticky — прилипает при прокрутке

.header {
  position: sticky;
  top: 0;
  background-color: white;
}

Пока страница не прокручена, шапка стоит на своём месте. Когда при прокрутке её верх доходит до top: 0, она «прилипает» к краю окна и едет вместе с ним — но только в пределах своего родителя.

z-index — кто сверху

Позиционированные элементы могут перекрывать друг друга. Порядок слоёв задаёт z-index: чем больше число, тем выше слой.

.modal   { position: fixed; z-index: 100; }
.header  { position: sticky; z-index: 10; }

z-index работает только у элементов с position, отличным от static (а также у элементов внутри flex-контейнера).

Сводная таблица

Значение В потоке? От чего отсчёт Пример
static Да — Обычный текст
relative Да (место сохраняется) Своё обычное место Родитель для absolute
absolute Нет Ближайший позиционированный предок Бейдж на карточке
fixed Нет Окно браузера Кнопка «наверх»
sticky Да, пока не прилип Окно при прокрутке Липкая шапка

Типичные ошибки

  • Ширина «не сходится», блоки вылезают — забыли box-sizing: border-box.
  • Нет рамки при border: 2px red — не указан стиль (solid).
  • Огромный текст во вложенных блоках — накопление em; для шрифтов берите rem.
  • margin: 0 auto не центрирует — у блока не задана ширина или это строчный элемент.
  • absolute-элемент улетел — у родителя нет position: relative.

Справочник по свойству — MDN: position.

Практика

  1. Кнопка. Сделайте ссылку-кнопку: фон, белый текст, padding: 0.75em 1.5em, border-radius: 8px, без подчёркивания. Результат: аккуратная кнопка; при увеличении font-size отступы растут пропорционально.
  2. Карточки. Сверстайте три карточки одна под другой: белый фон, padding: 1.5rem, рамка 1px solid #ddd, скругление 12px, расстояние между ними 1.5rem. Результат: карточки не слипаются, текст не прилипает к краям.
  3. Аватар. Квадратный блок 100×100px с фоном превратите в круг и поставьте по центру страницы через margin: 0 auto. Результат: круг по центру.
  4. Бейдж и кнопка «наверх». Добавьте карточке бейдж «SALE» в правом верхнем углу через absolute, а на страницу — кнопку «Наверх» с position: fixed в правом нижнем углу. Результат: бейдж держится за карточку, кнопка не уезжает при прокрутке.
  5. Липкая шапка. Сделайте длинную страницу (много абзацев) и шапку с position: sticky; top: 0. Результат: при прокрутке шапка остаётся вверху и не прячется под текстом (подберите z-index и фон).

Итоги

  • Каждый элемент — коробка: content → padding → border → margin.
  • Сокращения padding/margin идут по часовой стрелке: верх, право, низ, лево.
  • box-sizing: border-box делает width полной шириной элемента — ставьте его всегда.
  • px — абсолютно, % — от родителя, em — от шрифта элемента, rem — от шрифта <html>.
  • relative сдвигает и служит точкой отсчёта, absolute ставит точно внутри родителя.
  • fixed приклеивает к окну, sticky — прилипает при прокрутке, z-index управляет слоями.
Отзыв