Перейти к основному содержимому

Селекторы :is, :where и :has

Разработчику Архитектору

Перед чтением: Операторы — общие понятия оператора, операнда и приоритетов; в CSS роль «операций» выполняют селекторы и комбинаторы.

Псевдоклассы CSS - состояния без лишней разметки

Длинные списки селекторов через запятую трудно читать и сопровождать. :is() и :where() объединяют варианты в одну запись. :has() выбирает элемент по содержимому — «родитель, внутри которого есть …» — без классов, которые раньше добавлял только JavaScript.

Базовые псевдоклассы — в псевдоклассах и псевдоэлементах; справочник — в справочнике по CSS.


:is() — группировка с учётом специфичности

/* было */
article h1,
section h1,
aside h1 {
font-size: 1.5rem;
}

/* стало */
:is(article, section, aside) h1 {
font-size: 1.5rem;
}

Специфичность :is() равна самому «тяжёлому» селектору в списке. Если внутри есть #id, весь :is(...) станет таким же специфичным.

:is(#nav, .menu) a { color: blue; }
/* специфичность как у #nav — (1,0,1) */

Поддерживает вложенность и сложные селекторы:

:is(.card, .panel):is(:hover, :focus-within) {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

:where() — та же запись, специфичность ноль

Синтаксис как у :is(), но вклад в специфичность не считается (как 0,0,0).

:where(article, section, aside) h1 {
font-size: 1.5rem;
}

Удобно для базовых стилей в дизайн-системе: их проще перебить классом компонента без гонки с #id.

:where(.btn, button[type="submit"]) {
padding: 0.5rem 1rem;
border-radius: 6px;
}

.btn--primary {
background: #2563eb;
color: #fff;
}
:is():where()
Краткая записьдада
Специфичностьот самого сильного аргумента0
Когданужна «нормальная» силасброс / слой базовых правил

:has() — родитель по потомку

/* карточка с изображением — другой отступ заголовка */
.card:has(img) h2 {
margin-top: 0.5rem;
}

/* форма, где есть невалидное поле */
form:has(:user-invalid) {
border-left: 3px solid #c0392b;
}

/* ссылка с иконкой после текста */
a:has(+ svg) {
display: inline-flex;
align-items: center;
gap: 0.25rem;
}

С :not():

.nav:has(.nav__item:not(:last-child)) {
gap: 1rem;
}

Осторожно с производительностью: :has() на больших DOM-деревьях без узкого контекста может замедлить пересчёт стилей. Ограничивайте область (.list:has(.item--active) вместо body:has(...)).

Поддержка в актуальных браузерах широкая; для старых — запасной класс из JS.


Замена «селектор-листа мечты»

Раньше невозможно было выразить «label, за которым следует input» одним правилом без :has:

label:has(+ input:focus-visible) {
color: #2563eb;
}

Связка с валидацией форм и :user-invalid в псевдоклассах.


Краткий итог

:is() сокращает дублирование с обычной специфичностью. :where() — то же для «лёгких» базовых правил. :has() стилизует родителя по детям и соседям — мощный инструмент, но с умным выбором контекста.


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).