Эта статья — перевод оригинальной статьи Pelumi Akintokun "Deep dive into the CSS :where() function"

Также я веду телеграм канал “Frontend по-флотски”, где рассказываю про интересные вещи из мира разработки интерфейсов.

Вступление

Функция CSS :where() — новейшее детище в блоке псевдоклассов. Она принимает список селекторов в качестве аргументов и минимизирует их, позволяя вам писать меньше кода и в то же время стилизовать их все вместе.

В этом уроке мы узнаем про функцию псевдокласса :where() и покажем, как ее можно использовать в продакшене. Мы обсудим наложение, специфичность в отношении функции :where(), а также рассмотрим некоторые конкретные варианты использования.

Поехали!

Что такое CSS :where()?

Согласно MDN, :where() — это функциональный селектор CSS, который принимает список селекторов в качестве аргумента и применяет заданные стили к любому элементу из этого списка. :where() очень полезен для сокращения длинного списка селекторов.

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

Вот пример, в котором мы применяем один и тот же стиль ко всем тегам <a> внутри header, main и footer:

header a:hover,
main a:hover,
footer a:hover {
  color: green;
  text-decoration: underline;
}

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

Здесь в игру вступает функция :where().

Вот как вышеприведенный пример будет выглядеть с использованием функции :where():

:where(header, main, footer) a:hover {
  color: red;
  text-decoration: underline;
}

Давайте подробнее рассмотрим, как работает этот код.

Когда браузер доходит до фрагмента кода, код указывает браузеру искать селекторы header, main и footer. Затем, когда пользователь наводит курсор на любой из этих тегов, браузер должен применить указанные стили (в данном случае красный цвет и подчёркивание).

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

Объединение, разделение и наложение функции :where()

С помощью функции :where() мы можем группировать элементы несколькими способами и комбинациями. Мы можем поместить функцию :where() в начало, середину или конец селектора.

Вот пример с несколькими селекторами и стилями:

header a:hover,
main a:hover,
footer a:hover {
  color: green;
  text-decoration: underline;
}

article header > p,
article footer > p{
  color: gray;
}

.dark-theme button,
.dark-theme a,
.dim-theme button,
.dim-theme a{
  color: purple;
}

Вот тот же код, переписанный с помощью функции :where():

:where(header, main, footer) a:hover {
  color: red;
  text-decoration: underline;
}

article :where(header, footer) > p {
  color: gray;
}

.dark-theme :where(button, a) {
  color: purple;
}

В первом списке мы указываем, что красный цвет и подчёркивание должны применяться к элементам header, main и footer при наведении.

Во втором списке мы указываем, что article, header и footer должны быть окрашены в серый цвет.

Мы разделили третий список на две функции :where() для большей ясности. В этом списке мы указываем, что кнопка и элемент должны иметь стили .dark-theme, .dim-theme и color: purple.

Теперь давайте еще больше упростим функции в приведенном выше списке:

.dim-theme :where(button, a) {
  color: purple;
}

Далее мы еще больше уменьшим эти функции, превратив их в одну функцию :where():

:where(.dark-theme, .dim-theme) :where(button, a) {
  color: purple;
}

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

Специфичность и функция :where()

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

Специфичность функции CSS :where() всегда равна нулю. Таким образом, любой элемент, на который нацелена эта функция, также автоматически получает нулевую специфичность. Это дает нам возможность легко аннулировать стиль любого элемента, который мы хотим, сводя его специфичность к нулю.

Вот пример с упорядоченными списками HTML:

<div>
  <h2>First list no class</h2>
  <ol>
    <li>List Item 1</li>
    <li>List Item 2</li>
  </ol>
</div>
<div>
  <h2>Second list with class</h2>
  <ol class="second-list">
    <li>List Item 1</li>
    <li>List Item 2</li>
  </ol>
</div>

<div>
  <h2>Third list with class</h2>
  <ol class="third-list">
    <li>List Item 1</li>
    <li>List Item 2</li>
  </ol>
</div>

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

Без каких-либо стилей мы можем видеть, что каждый список упорядочен по номерам:

Теперь давайте добавим стили:

:where(ol[class]) {
  list-style-type: none;
}

В приведенном выше фрагменте мы используем функцию :where() для выбора всех тегов <ol> к которым применен класс.

Ниже мы видим, что второй и третий списки, у которых есть класс, были нацелены на функцию :where() и у них был удален list-style-type:

Теперь добавим дополнительные стили:

:where(ol[class]) {
  list-style-type: none;
}

.second-list {
  list-style-type: disc;
}

Ориентируясь только на второй список, используя имя его класса, мы видим, что теперь он отображается с маркерами, в то время как третий список по-прежнему имеет list-style-type: none

Вы можете задаться вопросом: «Но разве не так должно быть, учитывая, что новый стиль написан ниже стиля функции :where()?» Нет, это не так, и мы увидим это через мгновение.

Давайте посмотрим, что произойдет, если мы переместим код, который мы только что добавили, в верхнюю часть блока кода, а часть функции :where() переместим в нижнюю часть:

.second-list {
  list-style-type: disc;
}

:where(ol[class]) {
  list-style-type: none;
}

Обратите внимание, что стиль по-прежнему не меняется:

Помните, что функция :where() имеет нулевую специфичность. Независимо от того, размещен ли новый код до или после фрагмента функции :where(), специфичность элемента, на который нацелена функция :where(), станет равной нулю, а его стили будут аннулированы.

Чтобы проиллюстрировать это далее, давайте добавим стили второго списка в третий список после функции :where() в коде.

.second-list {
  list-style-type: disc;
}

:where(ol[class]) {
  list-style-type: none;
}

.third-list{
  list-style-type: disc;
}

И второй, и третий список отображаются с маркерами, независимо от размещения кода:

Теперь давайте посмотрим, как функция CSS :where() будет реагировать, если один из элементов выбран с помощью недопустимого селектора.

Прощение и функция :where()

CSS обычно не прощает не валидные селекторы. Если браузер не распознает только один селектор в списке, весь список селекторов будет считаться недействительным, и их стиль не будет применен.

Но это не относится к функции :where().

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

В приведенном ниже примере :unsupported является недопустимым селектором для многих браузеров. Приведенный ниже код будет проанализирован правильно и по-прежнему будет соответствовать селектору :valid, даже в браузерах, которые не поддерживают селектор :unsupported:

:where(:valid, :unsupported) {
  ...
}

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

:valid, :unsupported {
  ...
}

Особые случаи использования функции :where()

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

Во-первых, давайте рассмотрим несколько вариантов использования, в которых :where() может быть особенно полезным.

Улучшение сброса CSS

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

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

Например, предположим, что мы настроили таргетинг на все теги на сайте и выделили их зеленым цветом. Затем мы решили сделать все теги footer серыми.

Новый стиль (серого цвета) не применяется из-за сложности его выбора при сбросе CSS. Селектор в сбросе имеет более высокий порядок специфичности, чем селектор, используемый позже в коде, для нацеливания только на теги footer, поэтому стиль серого цвета не применяется.

Теперь, если мы добавим функцию :where() в сброс CSS, это автоматически придаст всем элементам нулевую специфичность. Это облегчает нам изменение стилей позже, не беспокоясь о конфликтах специфичности.

Удаление стилей

Функция :where() может быть полезна, если мы хотим удалить или обнулить стили или уменьшить специфичность элемента или набора элементов. Изменение произойдет в тот момент, когда мы поместим его в наш код, почти как «мини-сброс».

Минификация

Более короткий код легче читать и отлаживать. Хорошим эмпирическим правилом является проверка любого кода, содержащего более двух запятых или трех элементов списка, на предмет возможности его минимизации с помощью функции :where(). Это также полезная стратегия для комбинаций двух или более селекторов (например, section > header > p > a).

Теперь давайте рассмотрим вариант использования, в котором следует избегать функции :where().

Поддержание стилей

Если важно гарантировать, что стиль или специфичность элемента или набора элементов не изменятся ни в какой момент в будущем, не используйте псевдокласс :where(). Стиль и специфичность любого элемента, на который нацелен этот селектор, будут аннулированы.

Заключение

Функция CSS :where() очень полезна для улучшения сброса CSS, удаления стилей в любой точке нашего кода и облегчения чтения и отладки нашего кода.

В этой статье мы продемонстрировали, как функцию :where() можно использовать в производственной среде, и рассмотрели несколько вариантов ее использования.

Что вы думаете об этом новом селекторе CSS? Можете ли вы назвать какие-либо другие случаи, когда это было бы полезно?

Комментарии (7)


  1. MDiMaI666
    13.04.2022 22:21
    +8

    Жесть, еще один миллион первый способ отстрелить себе верстку.


  1. alfixer
    13.04.2022 23:53
    +4

    1. qmzik Автор
      15.04.2022 13:00

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


  1. holodoz
    14.04.2022 00:52

    обфускация кода: теперь и в css!


  1. Aleksandr-JS-Developer
    14.04.2022 12:39

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


  1. SeryiBaran6
    15.04.2022 09:55

    Я бы в самом низу статьи дал ссылки на спецификацию и поддержку браузерами


    1. qmzik Автор
      15.04.2022 13:04

      Вначале статьи есть ссылка на MDN