Каскад и блочная модель

Что это такое

Каскад — алгоритм, который решает, какое из ваших правил побеждает, когда к одному элементу применимо сразу несколько. Блочная модель — набор правил о том, насколько большим окажется этот элемент. Вместе они объясняют почти каждый момент «почему оно так себя ведёт» в CSS.

Почему это важно

Почти вся боль от CSS растёт из двух вопросов, на которые нет ответа: какое правило победило и какова на самом деле ширина элемента. Ответить на них — и таблица стилей становится предсказуемой: вы добавляете правило, знаете, применится ли оно, и знаете, сколько места оно займёт. Без этого остаются только !important и лишние обёртки, а каждое из двух делает следующее изменение труднее предыдущего.

Ключевые идеи

У каскада есть порядок приоритета

Когда правила сталкиваются, браузер разрешает их в таком порядке: важность (объявление с !important бьёт обычное), источник (таблица стилей автора бьёт умолчания браузера, но важные пользовательские стили бьют и их), затем специфичность и, наконец, порядок в исходнике. Специфичность — кортеж: id, затем классы, атрибуты и псевдоклассы, затем элементы и псевдоэлементы, — а сравнивается он слева направо, а не как единый балл.

/* специфичность: 0,1,1 */
nav a { color: navy; }

/* специфичность: 0,2,0 — это побеждает, хотя написано раньше */
.site-nav .link { color: teal; }

Встроенные стили бьют любой селектор без !important. Поэтому переопределить их потом так трудно.

Каждое объявление может победить или проиграть само по себе

Переопределяют редко целое правило: обычно дело в одном свойстве. Добавьте свойство в проигравшее правило — и оно применится сразу, а победившее сохранит свой цвет. Единообразно низкая специфичность избавляет от ситуации, когда одно новое объявление требует поднять три других.

Блочная модель складывает четыре прямоугольника

Содержимое — лишь самый внутренний из четырёх. Вокруг него идут padding, затем border, затем margin. По умолчанию width применяется только к содержимому, поэтому добавление padding расширяет элемент. box-sizing: border-box включает padding и border в width, и почти всегда вы имели в виду именно это.

.card {
  box-sizing: border-box;
  width: 20rem;
  padding: 1rem;
  border: 1px solid grey;
}

Сначала идёт нормальный поток

Пока вы его не измените, элементы находятся в нормальном потоке. Отступы схлопываются между соседями и между родителем и его первым или последним потомком — поэтому заголовок внутри контейнера словно отталкивает контейнер вниз. Обтекания, абсолютное позиционирование и flex-контейнеры выводят потомков из этого потока, каждый по-своему; прибегайте к ним, только когда поток действительно не подходит.

Наследование передаёт значения вниз по дереву

Наследуемые свойства вроде color, font-family и line-height переходят от родителя к потомку. Однако свойства прямоугольников не наследуются. Базовые font-size и line-height на body дают всей странице согласованный масштаб шрифта, и тогда em внутри компонента измеряется относительно собственного размера шрифта этого компонента, а не корневого.

Практика

Предскажите победителя до того, как посмотрите

Напишите страницу с тремя правилами, нацеленными на один и тот же абзац, с разной специфичностью, включая один встроенный атрибут style. Запишите, какой цвет победит, до открытия браузера, затем проверьте на панели Styles.

Готово, когда: ваше предсказание совпадает с отрисованным цветом, и вы можете назвать специфичность каждого селектора как кортеж.

Измерьте разницу, которую даёт box-sizing

Создайте два блока с одинаковыми объявленными width и padding: один с content-box, другой с border-box. Затем добавьте границу обоим.

Готово, когда: вы можете указать вычисленную ширину каждого в DevTools и объяснить разрыв одним предложением.

Частые ошибки

  • Наращивание специфичности ради победы — за следующим изменением придётся наращивать опять, и файл теряет управляемость; исправить: держите селекторы неглубокими и добавляйте вместо этого небольшой класс с понятным именем.
  • Обращение к !important, чтобы обойти унаследованный стиль — переопределить его позже уже ничем не удастся, и правило живёт дольше своей причины; исправить: починьте селектор, который фактически побеждает.
  • Добавление обёртки div ради отступа — разметка растёт, чтобы компенсировать margin, которому можно было дать имя; исправить: разберитесь с margin или с потоком, который создаёт разрыв.
  • Убеждение, что width включает padding — элемент шире набранного числа и раскладка переполняется; исправить: задайте box-sizing: border-box в начале таблицы стилей.
  • Борьба со схлопыванием отступов лишним padding — появляются два разрыва там, где вы хотели один; исправить: используйте gap у flex- или grid-контейнера либо обнулите соседний margin.

Что читать дальше

  • Язык гипертекстовой разметки (HTML) (02-frontend/03-html) — дерево, с которым сопоставляются ваши селекторы; специфичность селектора — мера того, насколько плотно он описывает это дерево.
  • Flexbox и grid (02-frontend/05-css/02-flexbox-and-grid) — шаг дальше: осознанно применять блочную модель для раскладки страницы.
  • Инструменты разработчика в браузере (02-frontend/27-browser-devtools) — панель Styles показывает все правила, которые применились, в том порядке, в каком их разрешил каскад.