Каскад и блочная модель
Что это такое
Каскад — алгоритм, который решает, какое из ваших правил побеждает, когда к одному элементу применимо сразу несколько. Блочная модель — набор правил о том, насколько большим окажется этот элемент. Вместе они объясняют почти каждый момент «почему оно так себя ведёт» в 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 показывает все правила, которые применились, в том порядке, в каком их разрешил каскад.