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

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

Play ITЗагрузка интерактивного демо…

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

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


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

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

Базовые псевдоклассы — в псевдоклассах и псевдоэлементах. Общий обзор функций в CSS (значения и селекторы) — Функции в CSS; справочник — справочник по CSS.


Атрибутные селекторы

Атрибут в HTML — пара «имя="значение"» на теге: type="text", href="…", disabled, data-status="draft". Справочник по атрибутам — HTML, основные теги.

Атрибутный селектор в CSS выбирает элементы по такому признаку. Вес селектора [type="text"] такой же, как у класса .field — одна «десятка» в специфичности.

Точное совпадение и наличие атрибута

/* value атрибута ровно "text" */
[type="text"] {
border: 1px solid #ccc;
}

/* любой <a>, у которого href начинается с https:// */
a[href^="https://"] {
color: #2563eb;
}

/* элемент с атрибутом disabled (значение не важно) */
[disabled] {
opacity: 0.5;
cursor: not-allowed;
}

Частичное совпадение значения

Когда в классе или data-* есть общий префикс или суффикс, удобны операторы в квадратных скобках:

ЗаписьЧто проверяетПример
[attr^="текст"]значение начинается с текст[class^="btn-"] — все классы вида btn-primary, btn-ghost
[attr$="текст"]значение заканчивается на текст[class$="-primary"] — вариант с суффиксом -primary
[attr*="текст"]значение содержит подстроку текст[class*="icon"] — класс с фрагментом icon
[attr|="код"]код или код-… (часто для lang)[lang|="ru"]ru, ru-RU

data-*пользовательские атрибуты для разметки без лишних классов. Пример: data-state="open" и селектор [data-state="open"].

[class^="btn-"] {
padding: 0.5rem 1rem;
border-radius: 6px;
cursor: pointer;
}

[class*="icon"] {
display: inline-block;
width: 1.25rem;
height: 1.25rem;
background-repeat: no-repeat;
background-size: contain;
}

[class$="-primary"] {
background: var(--brand, #2563eb);
color: #fff;
}

Сочетание с другими селекторами

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

  • input[type="text"][required] — обязательное текстовое поле;
  • a[href^="mailto:"]:hover — почтовая ссылка при наведении;
  • form:has([type="checkbox"]:checked) — форма, где отмечен чекбокс (см. раздел :has()).

Селекторы по [class*="…"] на всей странице сложнее читать в DevTools и чуть тяжелее для пересчёта стилей. В продакшене обычно заводят явный класс .btn или имя по BEM — методологии в §111.


: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() — родитель по потомку

Код ITЗагрузка примера кода…

С :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 в псевдоклассах.


Краткий итог

  • Атрибутные селекторы — стили по type, href, data-* и по фрагменту значения (^=, $=, *=);
  • :is() — короткая запись нескольких селекторов, специфичность от самого «тяжёлого» аргумента;
  • :where() — та же запись, но без добавления веса (удобно для базового слоя);
  • :has() — стиль родителя или соседа в зависимости от содержимого внутри.

Функции значений (calc(), var(), color-mix()) — в Функции в CSS. Таблицы свойств — справочник.