CSS: как страница получает вид
- Зачем это нужно
- Правило: кому и что
- Селекторы: кого выбираем
- Кто побеждает, когда правил несколько
- Наследование: что достаётся потомкам даром
- Коробка: почему 200 не равно 200
- Единицы: от чего считается число
- Схлопывание полей: 40 плюс 30 равно 40
- Сброс: с чего начинают файл
- Типичные ошибки
- Слова главы
- Тренажёр: набрать вслепую
- На чемпионате
- Куда углубиться
- Куда дальше: проверенные ресурсы
Зачем это нужно
Разметка описала, что на странице есть. Браузер уже показал это — своими стилями: Times, поля восемь точек, заголовок тридцать два пикселя. Выглядит как документ из девяностых.
Язык оформления отвечает на второй вопрос: как это должно выглядеть. Размеры, цвета, расположение, поведение при сужении экрана.
Учить его целиком не нужно — свойств несколько сотен, и большинство ищут по мере надобности. Нужно понять четыре механизма, из-за которых оформление ведёт себя «непонятно»: приоритет правил, наследование, счёт ширины и единицы измерения. Все четыре разобраны здесь прогонами.
Правило: кому и что
.card {
padding: 16px;
border-radius: 12px;
background: #16181d;
}
Подключают оформление тремя способами, и они не равны:
<link rel="stylesheet" href="style.css"> <!-- отдельный файл: так и делают -->
<style> .card { … } </style> <!-- внутри страницы: для мелочей -->
<div style="padding: 16px"> <!-- прямо в теге: почти никогда -->
Третий способ выглядит удобным и создаёт проблему: такое правило почти нечем перебить. Через две секции будет видно, чем именно.
Селекторы: кого выбираем
| запись | кого выбирает |
|---|---|
p | все абзацы |
.card | все элементы с классом card |
#total | элемент с таким идентификатором |
.card p | абзацы внутри карточки, на любой глубине |
.card > p | абзацы, лежащие прямо в карточке |
.card, .panel | и то, и другое: два независимых селектора |
input[type="number"] | поля с таким атрибутом |
button:hover | кнопка под курсором |
button:disabled | выключенная кнопка |
li:nth-child(2n) | каждый второй пункт списка |
Практика простая: основной инструмент — класс. Теги выбирают для общих правил вроде сброса, идентификаторы в оформлении почти не используют (причина — в следующей секции), а остальное берут по мере надобности.
Кто побеждает, когда правил несколько
К одному элементу почти всегда подходит больше одного правила. Прогон меняет только признаки элемента и смотрит, какой цвет победил:
── что победило при разном наборе признаков ──
подходит только p {…} rgb(1, 1, 1) p {…}
подходят p, .c и p.c rgb(3, 3, 3) p.c — два признака против одного
плюс #i rgb(4, 4, 4) #i — идентификатор весомее классов
плюс style="" и !important rgb(9, 9, 9) !important — сильнее даже style
Правило веса читается так: считают сколько чего в селекторе.
- Больше идентификаторов — весомее.
- При равенстве смотрят классы, атрибуты и состояния вроде «под курсором».
- При равенстве и тут — имена тегов.
- Запись прямо в теге весомее любого селектора.
- Пометка «важно» сильнее всего.
При полном равенстве побеждает то, что записано ниже. Порядок решает только ничью — не более того. Отсюда частая ошибка: «я дописал правило в конец, а оно не применилось». Значит, вес прежнего выше, и порядок ни при чём.
Идентификаторы и пометку «важно» в оформлении стараются не использовать вовсе. Причина простая: перебить их потом нечем, кроме такого же. Один раз написанная пометка тянет за собой вторую, третью — и через день никто не может поменять цвет кнопки.
Наследование: что достаётся потомкам даром
Родителю задано семь свойств. Потомку — ни одного. Прогон смотрит, что у потомка получилось:
── что потомок получил сам, без единого правила ──
color rgb(7, 7, 7) ← унаследовал
fontFamily Georgia ← унаследовал
fontSize 20px ← унаследовал
textAlign center ← унаследовал
borderTopWidth 0px ← нет
paddingTop 0px ← нет
Правило простое и не требует запоминания списка: наследуется то, что про текст — цвет, шрифт, размер, выравнивание, межстрочное расстояние. Не наследуется то, что про коробку — рамки, отступы, фон, размеры.
Логика очевидна, если задуматься: рамка вокруг карточки, повторённая у каждого потомка, выглядела бы дико. А цвет текста, наоборот, должен распространяться вглубь.
Из этого следует приём, которым пользуются постоянно: шрифт и цвет задают один раз на теле документа, и дальше они действуют везде.
Коробка: почему 200 не равно 200
Самая частая причина «вёрстка съехала» — в том, как считается ширина.
.b { width: 200px; padding: 20px; border: 5px solid; margin: 10px; }
── одна и та же запись, два способа считать ──
свойство content-box border-box
width в CSS 200px 200px
место под текст 200px 150px
видимая ширина 250px 200px
занято с полями 270px 220px
По умолчанию запись width: 200px означает «двести точек под содержимое». Отступы по двадцать с каждой стороны и рамка по пять прибавляются сверху — получается 250. Внешние поля добавляют ещё двадцать: элемент занимает 270.
Отсюда классическая беда: две колонки по 50 % с отступами не помещаются в строку, потому что каждая на самом деле шире половины.
Второй способ счёта меняет смысл: 200 становится шириной всего элемента, а отступы и рамка съедают место у содержимого. Именно это обычно и имеют в виду.
*, *::before, *::after { box-sizing: border-box; }
Эта строка стоит первой почти в любом файле оформления последних десяти лет. Дальше width: 50% действительно означает половину, и колонки складываются как ожидается.
Единицы: от чего считается число
── от чего считаются единицы ──
родитель: font-size 20px, ширина 400px; корень документа: 16px
1.5rem font-size 24px width 200px
1.5em font-size 30px width 200px
25% font-size 20px width 100px
Точки (px) — абсолютный размер. Подходит для рамок, мелких отступов, теней.
rem — от размера шрифта корня документа, по умолчанию 16. 1.5rem = 24 всегда, где бы элемент ни лежал.
em — от размера шрифта родителя. Здесь 1.5 × 20 = 30. И это ловушка: em внутри em умножается. Три уровня вложенности по 1.5 дают не полтора, а 3,375 от исходного — текст разрастается непонятно почему.
Проценты — от родителя, причём для ширины от ширины, а для отступов тоже от ширины (не от высоты, как ожидают).
vh и vw — сотые доли высоты и ширины окна. 100vh — ровно экран.
Практическое правило: размеры шрифта и крупные отступы — в rem, мелкие детали — в точках, ширины — в процентах или долях сетки.
Схлопывание полей: 40 плюс 30 равно 40
── два поля между блоками: 40px снизу и 30px сверху ──
сумма на бумаге : 70px
реальный зазор : 40px
Вертикальные внешние поля соседних блоков не складываются: берётся большее. Это не ошибка браузера, а правило, придуманное для текста — чтобы абзацы не расползались вдвое.
Знать про него надо по одной причине: иначе зазор между блоками невозможно объяснить. Вы поставили 30, получили 40 — и ищете лишнее правило, которого нет.
Три способа не встретиться с этим:
- задавать отступ только с одной стороны — например, всегда снизу;
- использовать расстояние между элементами сетки — там схлопывания нет вовсе;
- изменить способ размещения родителя так, чтобы поля потомков не выходили наружу.
Первый способ проще всего и решает девять случаев из десяти.
Сброс: с чего начинают файл
── что меняет сброс ──
без сброса: высота блока с заголовком и абзацем 77px
со сбросом: 56px
поля body при этом остаются: 8px — их сбрасывают отдельно
Двадцать одна точка разницы на двух элементах — это те самые отступы браузера из прошлой главы. На настоящей странице их десятки, и все они спорят с вашей сеткой.
Начало файла оформления, которое имеет смысл набрать наизусть:
*, *::before, *::after { box-sizing: border-box; }
body {
margin: 0;
font-family: system-ui, sans-serif;
font-size: 16px;
line-height: 1.5;
color: #e8e8ea;
background: #0e1013;
}
h1, h2, h3, p, ul { margin: 0 0 1rem; }
ul { padding-left: 1.25rem; }
img { max-width: 100%; height: auto; display: block; }
Типичные ошибки
Забыли второй способ счёта ширины. Колонки по 50 % не помещаются в строку. Лечится одной строкой в начале файла.
Правило не применилось, дописали «важно». Работает ровно один раз, дальше начинается война пометок. Правильно — посмотреть, какое правило победило, и исправить вес.
Между блоками зазор больше заданного. Схлопывание вертикальных полей.
Размер шрифта в em на нескольких уровнях. Текст растёт сам по себе.
Селектор в пять уровней вложенности. Вес получается огромный, и перебить его потом невозможно. Класс на элементе почти всегда лучше.
Потеряли точку с запятой. Строка и следующая за ней пропадают молча — сообщения об ошибке в оформлении не бывает вовсе.
Оформление прямо в теге. Перебивается только пометкой «важно». В интерфейсе, который меняется, это тупик.
Слова главы
- Правило — селектор и блок объявлений «свойство: значение».
- Селектор — кому применить; основной инструмент — класс.
- Вес селектора — сколько в нём идентификаторов, классов и тегов; порядок решает только ничью.
- Наследование — свойства про текст переходят потомкам, свойства про коробку — нет.
- Модель коробки — по умолчанию ширина считается по содержимому, а не по элементу.
remиem— от корня документа и от родителя; второе умножается при вложенности.- Схлопывание полей — вертикальные поля соседей не складываются, берётся большее.
- Сброс — обнуление стилей браузера и единый способ счёта ширины в начале файла.
Тренажёр: набрать вслепую
Цель: скорость ≥ 95 зн/мин, точность ≥ 88%
*, *::before, *::after { box-sizing: border-box; }
На чемпионате
Сброс набирают первым, до всего остального. Пять строк, которые снимают половину будущих вопросов «почему съехало».
Классы, а не идентификаторы. Интерфейс на чемпионате переделывают по ходу, и оформление должно поддаваться правке.
Цвета и размеры — в переменных. Одна строка сверху, дальше они по имени; менять тему потом придётся, и лучше в одном месте:
:root { --accent: #7cf; --bg: #0e1013; --radius: 12px; }
.card { background: var(--bg); border-radius: var(--radius); }
Проверяйте на узком экране сразу. Не в конце: переделывать готовую вёрстку под телефон дороже, чем сразу не ломать.
И про время: оформление — та часть, где легко потерять час на мелочи. Сначала пусть всё работает и читается, потом красота.
Куда углубиться
- Переменные оформления — объявление, область действия, смена темы одной строкой.
- Состояния элементов — под курсором, в фокусе, выключен, с ошибкой; для формы это обязательное.
- Единицы, зависящие от окна — и почему
100vhна телефоне ведёт себя не так, как ожидается. - Слои каскада — современный способ управлять весом без пометок «важно».
- Инструменты браузера — панель, в которой видно, какое правило победило и почему.
Сводная проверка по всей главе: 8 вопросов, 5:00 на всё, без подсказок по ходу. Вопросы показываются по одному, ответ изменить нельзя. Если время выйдет — экзамен завершится с тем, что успел ответить. Пересдавать можно сколько угодно раз.
Куда дальше: проверенные ресурсы
- MDN: основы CSS — на русском, с примерами.
- MDN: каскад, вес и наследование — почему побеждает то, а не это.
- MDN: модель коробки — content-box против border-box.
- MDN: значения и единицы — px, rem, em, проценты.
- CSS Specificity Calculator — считает вес селектора за вас.
Комментарии
Обсуждение живёт в GitHub Discussions и грузится с чужого домена — поэтому только по нажатию.