Вложенные правила CSS
Когда правила компонента разбросаны по файлу, их связь не всегда видна. Нативная вложенность CSS позволяет записать правило дочернего элемента внутри правила родителя, сохраняя эту связь в исходнике. Однако вложенность меняет не только удобство записи: важно понимать получающийся селектор и его специфичность. Иначе сокращённый текст неожиданно окажется сильнее прежнего.
Продолжим каталог с диалогом и подсказкой. В этом уроке не добавляем новое взаимодействие и не меняем данные. Результат — две группы правил карточки записаны вложенно, а их связь с прежними плоскими селекторами разобрана явно. Дальше подготовим отдельный совместимый CSS-вариант, поскольку неизвестная вложенность не должна считаться автоматически развёрнутой старым движком.
Амперсанд обозначает родительский селектор
В слое components найдите прежнее отдельное правило .layout-lab .course-title, задающее font-size и line-height. Удалите именно это правило с этими двумя объявлениями. Позднее правило заголовка с min-inline-size и overflow-wrap из урока внутренних размеров сохраняется: оно имеет другую обязанность. На месте удалённого правила запишите следующий блок.
.layout-lab .course-card {
& .course-title {
font-size: 1.25rem;
line-height: 1.3;
}
}
Знак & ссылается на родительский селектор. Пробел перед .course-title означает потомка карточки. Для данного простого случая результат соответствует плоскому .layout-lab .course-card .course-title. Браузер не создаёт нового HTML-элемента и не требует, чтобы заголовок был непосредственным ребёнком; это определяется выбранным комбинатором, как и в обычном CSS.
Ожидается прежний размер названия курса внутри карточек. Однако результирующий селектор имеет три класса вместо двух у исходного .layout-lab .course-title. Его специфичность выросла. В текущем компоненте на те же свойства не претендует другой равнослойный вариант, поэтому вид остаётся согласованным. Сам факт визуального совпадения не доказывает, что перенос не изменил силу правила.
Если нужно выбрать только непосредственного ребёнка, можно написать & > .course-title. Это будет другая связь с HTML, которая перестанет работать после добавления промежуточной обёртки. Мы сохраняем потомка, потому что в данном упражнении группируем ответственность карточки, а не вводим новый контракт глубины дерева. Нативная вложенность не выбирает подходящий комбинатор за автора.
Модификатор относится к той же коробке
В слое variants замените прежнее правило .layout-lab .course-card--featured следующим полным блоком. Оно остаётся именно в этом слое, чтобы цвет выделения сохранял установленный приоритет перед общим компонентом.
.layout-lab .course-card {
&:where(.course-card--featured) {
border-color: var(--lab-accent);
}
}
Здесь между & и псевдоклассом нет пробела. Выбирается та же коробка карточки, которая дополнительно соответствует условию featured. Если поставить пробел, условие станет относиться к потомку, и наш article не получит ожидаемого цвета. Вложенность делает маленький знак пробела частью существенного смысла, поэтому короткую запись нужно читать так же внимательно, как плоскую.
Содержимое :where не добавляет специфичности. В данном случае остаются два класса родительского селектора, как у прежнего правила выделенного варианта. Если написать &.course-card--featured напрямую, получится ещё один класс и более сильный селектор. Оба варианта могут сейчас рисовать ту же границу, но задают разные условия будущего соперничества в одном слое.
Псевдокласс не перемещает правило между слоями и не отменяет их порядок. Поэтому мы сохранили variants и отдельно объяснили силу селектора. Сначала определяем группу объявления, затем сравниваем подходящие селекторы внутри неё. Нельзя ожидать, что использование :where позволит обычному раннему слою перекрыть поздний только за счёт своей краткости.
Список родителей и неожиданно сильное правило
Для отдельного диагностического чтения рассмотрим такой фрагмент. Не добавляйте его в основной файл: идентификатор здесь нужен только для разбора правила списка.
.layout-lab .course-card, #external-card {
& .course-title { color: #e4e4e4; }
}
Вложенный селектор учитывает наибольшую специфичность родительского списка, подобно механизму :is. Наличие идентификатора в одной ветке делает результирующую силу больше даже для карточки, подошедшей по классам другой ветки. Поэтому это не обязательно эквивалент двух независимо развёрнутых правил с разной специфичностью каждой ветки. Краткое объединение изменило модель сравнения.
В нашем основном переносе родитель не является смешанным списком, поэтому такая неожиданность не возникает. Тем не менее при рефакторинге общего файла полезно проверять именно списки с разной силой, а не только читать наиболее короткую ветку. Механизм раскрывает MDN о вложенности и специфичности.
Нативный CSS и препроцессор
Нативная вложенность разбирается браузером в самом stylesheet. Она не означает наличие Sass или другого инструмента, который предварительно создаёт отдельный файл. В нашем проекте нет сборки CSS и новой зависимости. Поэтому применяем именно синтаксис, поддерживаемый браузером, а не похожие удобства внешнего языка.
Например, склеивание имён в стиле &__title нельзя считать универсальным способом получить .course-card__title в нативном CSS. Амперсанд представляет родительский селектор, а не строку для конкатенации названия класса. Выберите реальный селектор существующего ребёнка. Для нашего HTML это .course-title, чьё имя остаётся прежним во всех снимках.
Внутри компонентов можно располагать и условные правила, но глубокая вложенность не является целью сама по себе. Если для понимания связи приходится проходить пять уровней, плоский селектор может быть яснее. Особенно полезно держать отдельными правила, отвечающие за независимые состояния вроде общей печати или скрытого HTML-атрибута. Их область шире одной карточки.
Правила записи и допустимые формы приведены в MDN об использовании CSS nesting. Поддержку выбранной формы следует смотреть отдельно для целевых движков. Наличие некоторой поддержки вложенности не доказывает автоматически, что каждый новый синтаксический вариант одинаково работает в более ранних версиях.
Проверяем сохранённую обязанность
После переноса сравните ожидаемые свойства: заголовки карточек имеют прежний размер и высоту строки, первая граница остаётся фиолетовой, остальные — обычными. Механизмы dialog и popover не должны зависеть от выбранной формы записи этих правил. Временные диагностические селекторы не входят в сохранённый stylesheet. В браузере эти эффекты ещё предстоит наблюдать; подготовка статьи не включала исполнение страницы.
Для чтения каскада удобно мысленно развернуть каждое вложенное правило в обычный селектор, затем посчитать его существенные части. Такой приём помогает обнаружить пробел вместо совпадения той же коробки и лишний уровень специфичности. Он не требует всегда возвращать плоскую запись, а сохраняет понятность выбранного сокращения.
Браузер, который не понимает вложенность, не обязан автоматически применить вложенные объявления как самостоятельные правила. Поэтому современный основной файл и совместимая основа имеют разные требования. В следующем завершающем уроке дадим отдельный плоский stylesheet и сравним условия улучшений без обещания одинакового оформления во всех существующих браузерах.
Полное состояние этого шага находится в layout-lab/lesson-31 внутри исходников продолжения. Каждый снимок открывается отдельно; подключать CSS разных уроков одновременно не нужно.