CSS-вложение

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

Адам Аргайл
Adam Argyle

Перед вложением каждый селектор необходимо было явно объявить отдельно друг от друга. Это приводит к повторению, большому объему таблиц стилей и разбросанности опыта разработки.

До
.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

.nesting > .is > .awesome {
  color: deeppink;
}

После вложения селекторы можно продолжить, а связанные с ними правила стиля можно сгруппировать внутри.

После
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Попробуйте это в браузере .

Вложенность помогает разработчикам, уменьшая необходимость повторения селекторов, а также совмещая правила стиля для связанных элементов. Это также может помочь стилям соответствовать HTML, на который они нацелены. Если компонент .nesting в предыдущем примере был удален из проекта, вы можете удалить всю группу вместо поиска в файлах связанных экземпляров селектора.

Вложение может помочь в: - организации; - уменьшении размера файла; - рефакторинге.

Вложенность доступна в Chrome 112, а также доступна для тестирования в Safari Техническая предварительная версия 162 .

Начало работы с вложением CSS

В оставшейся части статьи используется следующая демо-песочница, которая поможет вам визуализировать выбор. В этом состоянии по умолчанию ничего не выбрано и все видно. Выбирая различные формы и размеры, вы можете попрактиковаться в синтаксисе и увидеть его в действии.

Красочная сетка из маленьких и больших кругов, треугольников и квадратов.

Внутри песочницы находятся круги, треугольники и квадраты. Некоторые из них маленькие, средние или большие. Другие — синие, розовые или фиолетовые. Все они находятся внутри элемента, содержащего .demo . Ниже приведен предварительный просмотр HTML-элементов, на которые вы будете ориентироваться.

<div class="demo">
  <div class="sm triangle pink"></div>
  <div class="sm triangle blue"></div>
  <div class="square blue"></div>
  <div class="sm square pink"></div>
  <div class="sm square blue"></div>
  <div class="circle pink"></div>
  …
</div>

Примеры вложения

Вложенность CSS позволяет определять стили для элемента в контексте другого селектора.

.parent {
  color: blue;

  .child {
    color: red;
  }
}

В этом примере селектор класса .child вложен в селектор класса .parent . Это означает, что вложенный селектор .child будет применяться только к элементам, которые являются дочерними элементами элементов с классом .parent .

В качестве альтернативы этот пример можно написать с использованием символа & , чтобы явно указать, где следует разместить родительский класс.

.parent {
  color: blue;

  & .child {
    color: red;
  }
}

Эти два примера функционально эквивалентны, и причина, по которой у вас есть варианты, станет яснее, когда в этой статье будут рассмотрены более сложные примеры.

Выбор кругов

В этом первом примере задача состоит в том, чтобы добавить стили для исчезновения и размытия только кругов внутри демо-версии.

Без вложенности CSS сегодня:

.demo .circle {
  opacity: .25;
  filter: blur(25px);
}

При вложении есть два допустимых способа:

/* & is explicitly placed in front of .circle */
.demo {
  & .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

или

/* & + " " space is added for you */
.demo {
  .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

В результате все элементы внутри .demo с классом .circle размыты и почти невидимы:

В красочной сетке фигур больше нет кругов, они очень тусклые на заднем плане.
Попробуйте демо

Выбор любых треугольников и квадратов

Эта задача требует выбора нескольких вложенных элементов, также называемых селектором группы .

Без вложенности в CSS сегодня есть два пути:

.demo .triangle,
.demo .square {
  opacity: .25;
  filter: blur(25px);
}

или, используя :is()

/* grouped with :is() */
.demo :is(.triangle, .square) {
  opacity: .25;
  filter: blur(25px);
}

При использовании вложенности есть два допустимых способа:

.demo {
  & .triangle,
  & .square {
    opacity: .25;
    filter: blur(25px);
  }
}

или

.demo {
  .triangle, .square {
    opacity: .25;
    filter: blur(25px);
  }
}

В результате внутри .demo остаются только элементы .circle :

В красочной сетке фигур остались только круги, все остальные фигуры почти невидимы.
Попробуйте демо

Выбор больших треугольников и кругов

Для этой задачи требуется составной селектор , в котором элементы должны иметь оба класса, чтобы их можно было выбрать.

Без вложенности CSS сегодня:

.demo .lg.triangle,
.demo .lg.square {
  opacity: .25;
  filter: blur(25px);
}

или

.demo .lg:is(.triangle, .circle) {
  opacity: .25;
  filter: blur(25px);
}

При использовании вложенности есть два допустимых способа:

.demo {
  .lg.triangle,
  .lg.circle {
    opacity: .25;
    filter: blur(25px);
  }
}

или

.demo {
  .lg {
    &.triangle,
    &.circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

В результате все большие треугольники и круги спрятаны внутри .demo :

В красочной сетке видны только мелкие и средние фигуры.
Попробуйте демо
Совет для профессионалов по составным селекторам и вложению

Символ & здесь ваш друг, поскольку он наглядно показывает, как соединять вложенные селекторы. Рассмотрим следующий пример:

.demo {
  .lg {
    .triangle,
    .circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

Хотя это допустимый способ вложения, результаты не будут соответствовать ожидаемым элементам. Причина в том, что без & для указания желаемого результата объединения .lg.triangle, .lg.circle фактическим результатом будет .lg .triangle, .lg .circle ; селекторы потомков .

Выделение всех фигур, кроме розовых.

Для этой задачи требуется псевдокласс функционального отрицания , где у элементов не должно быть указанного селектора.

Без вложенности CSS сегодня:

.demo :not(.pink) {
  opacity: .25;
  filter: blur(25px);
}

При использовании вложенности есть два допустимых способа:

.demo {
  :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

или

.demo {
  & :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

В результате все фигуры, которые не розовые, спрятаны внутри .demo :

Красочная сетка теперь монохромная, на ней видны только розовые фигуры.
Попробуйте демо
Точность и гибкость с &

Допустим, вы хотите настроить таргетинг на .demo с помощью селектора :not() . & для этого требуется :

.demo {
  &:not() {
    ...
  }
}

Это объединяет .demo и :not() в .demo:not() , в отличие от предыдущего примера, в котором требовался .demo :not() . Это напоминание очень важно, если вы хотите вложить взаимодействие :hover .

.demo {
  &:hover {
    /* .demo:hover */
  }

  :hover {
    /* .demo :hover */
  }
}

Больше примеров вложенности

Спецификация CSS для вложенности содержит больше примеров. Если вы хотите узнать больше о синтаксисе с помощью примеров, он охватывает широкий спектр допустимых и недопустимых примеров.

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

Вложение @media

Переход в другую область таблицы стилей в поисках условий медиа-запроса, изменяющих селектор и его стили, может сильно отвлекать. Это отвлечение исчезло благодаря возможности вкладывать условия прямо в контекст.

Для удобства синтаксиса, если вложенный медиа-запрос изменяет только стили для текущего контекста селектора, можно использовать минимальный синтаксис.

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    font-size: 1.25rem;
  }
}

Использование & явно также может использоваться:

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    &.large {
      font-size: 1.25rem;
    }
  }
}

В этом примере показан расширенный синтаксис с помощью & , а также таргетинг на карты .large , чтобы продемонстрировать, что дополнительные функции вложения продолжают работать.

Узнайте больше о вложении @rules .

Вложение где угодно

Все примеры до этого момента были продолжением или дополнением предыдущего контекста. При необходимости вы можете полностью изменить или переставить контекст.

.card {
  .featured & {
    /* .featured .card */
  }
}

Символ & представляет собой ссылку на объект селектора (не строку) и может быть размещен в любом месте вложенного селектора. Его даже можно разместить несколько раз:

.card {
  .featured & & & {
    /* .featured .card .card .card */
  }
}

Хотя этот пример выглядит немного бесполезным, безусловно, существуют сценарии, в которых возможность повторения контекста селектора полезна.

Неверные примеры вложенности

Существует несколько сценариев синтаксиса вложения, которые недопустимы и могут вас удивить, если вы использовали вложение в препроцессорах.

Вложение и конкатенация

Многие соглашения об именах классов CSS рассчитывают на вложенность, позволяющую объединять или добавлять селекторы, как если бы они были строками. Это не работает при вложении CSS, поскольку селекторы не являются строками, а являются ссылками на объекты.

.card {
  &--header {
    /* is not equal to ".card--header" */
  }
}

Более подробное объяснение можно найти в спецификации .

Сложный пример вложения

Вложение в списки селекторов и :is()

Рассмотрим следующий вложенный блок CSS:

.one, #two {
  .three {
    /* some styles */
  }
}

Это первый пример, который начинается со списка селекторов , а затем продолжает вложенность. Предыдущие примеры заканчивались только списком выбора. В этом примере вложения нет ничего недопустимого, но есть потенциально сложная деталь реализации вложения внутри списков селекторов, особенно тех, которые включают селектор идентификатора.

Чтобы цель вложенности работала, любой список селекторов, который не является самым внутренним вложением, будет заключен браузером в :is() . Эта упаковка поддерживает группировку списка селекторов в любых созданных контекстах. Побочным эффектом этой группировки :is(.one, #two) является то, что она принимает специфику наивысшего балла в селекторах внутри круглых скобок. Именно так всегда работает :is() , но использование синтаксиса вложенности может стать сюрпризом, поскольку он не совсем тот, который был создан. Суть трюка вкратце; вложение с идентификаторами и списками селекторов может привести к созданию селекторов с очень высокой специфичностью.

Чтобы наглядно проиллюстрировать сложный пример, предыдущий блок вложения будет применен к документу следующим образом:

:is(.one, #two) .three {
  /* some styles */
}

Будьте внимательны или научите свои линтеры предупреждать, что при вложении в список селекторов, использующий селектор идентификаторов, специфичность всех вложений в этом списке селекторов будет высокой.

Смешение вложенности и объявлений

Рассмотрим следующий вложенный блок CSS:

.card {
  color: green;
  & { color: blue; }
  color: red;
}

Цвет элементов .card будет blue .

Любые объявления смешанных стилей поднимаются наверх, как если бы они были созданы до того, как произошло какое-либо вложение. Более подробную информацию можно найти в спецификации .

Есть способы обойти это. Следующий пример оборачивает три цветовых стиля в & , который поддерживает каскадный порядок, как, возможно, и задумал автор. Цвет элементов .card будет красным.

.card {
  color: green;
  & { color: blue; }
  & { color: red; }
}

На самом деле, хорошей практикой является обертывание любых стилей, следующих за вложенностью, с помощью & .

.card {
  color: green;

  @media (prefers-color-scheme: dark) {
    color: lightgreen;
  }

  & {
    aspect-ratio: 4/3;
  }
}

Обнаружение функций

Есть два отличных способа обнаружения вложенности CSS: использовать вложение или использовать @supports для проверки возможности анализа селектора вложенности.

Скриншот демонстрации Codepen от Bramus, где спрашивается, поддерживает ли ваш браузер вложение CSS. Под этим вопросом находится зеленая рамка, сигнализирующая о поддержке.

Использование вложения:

html {
  .has-nesting {
    display: block;
  }

  .no-nesting {
    display: none;
  }
}

Использование @supports :

@supports (selector(&)) {
  /* nesting parsing available */
}

У моего коллеги Брамуса есть отличный Codepen , демонстрирующий эту стратегию.

Отладка с помощью Chrome DevTools

Текущая поддержка вложенности в DevTools минимальна. В настоящее время стили представлены на панели «Стили», как и ожидалось, но отслеживание вложения и его полного контекста селектора пока не поддерживается. У нас есть дизайн и планы сделать это прозрачным и понятным.

Скриншот синтаксиса вложенности Chrome DevTools.

В Chrome 113 планируется добавить дополнительную поддержку вложенности CSS. Следите за обновлениями.

Будущее

Вложенность CSS доступна только в версии 1. Версия 2 представит больше синтаксического сахара и потенциально меньше правил для запоминания. Существует большая потребность в том, чтобы анализ вложенности не был ограничен и не содержал сложных моментов.

Вложенность — это большое усовершенствование языка CSS. Это имеет значение для разработки почти всех архитектурных аспектов CSS. Это большое влияние необходимо глубоко изучить и понять, прежде чем можно будет эффективно определить версию 2.

И напоследок, вот демо , в котором используются @scope , вложение и @layer вместе. Это все очень увлекательно!

Светлая открытка на сером фоне. На карточке есть заголовок и текст, несколько кнопок действий и изображение в стиле киберпанк.