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

Применение псевдокласса :is

Рассмотрим следующий пример:

:is(a, button) {
  text-transform: uppercase;  /* свойство применится для <a> и для <button> */
}

Читается запись как: "Если элемент - это ссылка или кнопка, делаем текст внутри заглавными буквами". Смысл применения псевдокласса - объединить в одну сжатую форму повторяющиеся селекторы.

section p,
article p {
  margin: 1em 0;
}

/* аналогично */

:is(section, article) p {
  margin: 1em 0;
}

Пример, когда псевдокласс в середине селектора:

article b,
article strong {
  font-weight: 500;
}

/* аналогично */

article :is(b, strong) {
  font-weight: 500;
}

Заметьте, что :is не привязан к элементу и пишется через пробел. Иначе селектор article:is(b) читался бы: "Если article - это b, то примени такие-то стили", что уже вызывает несостыковку.

Возможно для простых селекторов применение :is не кажется слишком профитным, но что, если у нас выборка массивней.

article h1 a,
article h1 button,
article h2 a,
article h2 button,
section h1 a,
section h1 button,
section h2 a,
section h2 button {
  font: inherit;
  color: inherit;
}

/* аналогично */

:is(article, section) :is(h1, h2) :is(a, button) {
  font: inherit;
  color: inherit;
}

Привязка :is к самому себе

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

.wrapper :is(.circle, .rect):is(:first-child, :last-child) {
  background-color: red;
}

Вот, какой результат мы получим на живом примере:

Игнорирование неверных селекторов

Как известно, наличие неверного селектора в стилях приводит к тому, что CSS-правило не отработает полностью. Например, данный селектор не закрасит текст абзаца красным:

p, input/ {
  color: red;
}

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

Так вот, если в выражении превдокласса :is есть такой селектор, оно его будет игнорировать и правило отработает для  остальных правильных селекторов.

/* Правило внутри любого из этих селекторов не выполнится */
p, art!cle { color: red; }
p, 1h1 { color: red; }
p, type] { color: red; }
p, *span { color: red; }
p, .#main { color: red; }
p, :pseudoselector { color: red; }

/* В этом случае текст внутри абзаца закрасится */
:is(p, art!cle, 1h1, type], *span, .#main, :pseudoselector) {
  color: red;
}

Отличие псевдокласса :is от :where

Как уже упоминалось, псевдокласс :where работает точно так же как и :is. Все вышеперечисленные примеры одинаково отработают для обоих псевдоклассов. Однако между ними есть одно отличие и вместе с этим интересная особенность - это вес селектора.

Как обычно считается вес селектора? Каждый тег в селекторе добавит к его весу 1, каждый класс или атрибут - 10, идентификатор 100 и инлайновая запись - 1000.

article { color: red; }  /* 1 */
.text { color: red; }  /* 10 */
#main { color: red; }  /* 100 */
ul > li .button .icon { color: red; }  /* 22 */
header label[for] #name { color: red; }  /* 112 */

Вес псевдокласса :is будет равен наибольшему из весов внутренних селекторов. А вес псевдокласса :where будет всегда равен 0.

:is(article, .text) { color: red; }  /* 10 */
:is(.text, #main) { color: red; }  /* 100 */

:where(article, .text) { color: red; }  /* 0 */
:where(.text, #main) { color: red; }  /* 0 */

Учитывая эту интересную особенность, будьте внимательны при написании стилей, чтобы не озадачиться подобным примером:

:is(.icon, #icon-arrow) {
  fill: green;
}

ul > li .button svg.icon {
  fill: red;
}

/* Заливка .icon будет зеленая */