CSS :has(): стилизовать родителя по дочернему элементу

Категория: CSS · Язык: CSS · Обновлено:

:has() — функциональный псевдокласс, который выбирает элемент, если внутри него совпал относительный селектор. Он позволяет стилизовать родителя по состоянию дочернего поля без JavaScript. Поддержка есть в Chrome 105+, Safari 15.4+ и Firefox 121+, поэтому базовые стили должны работать и без него.

Подсветить поле с ошибкой и фокусом

CSS
.field {
    border: 1px solid #d0d5dd;
    border-radius: 8px;
    padding: 12px;
}

.field:has(input:focus-visible) {
    outline: 2px solid #2563eb;
    outline-offset: 2px;
}

.field:has(input:user-invalid) {
    border-color: #b42318;
}

.card:has(> img) {
    padding-block-start: 0;
}

:has(input:focus-visible) срабатывает, когда внутри есть поле с клавиатурным фокусом. :user-invalid появляется после того, как пользователь ввёл значение и оно не прошло проверку, — в отличие от :invalid, который подсвечивает пустое обязательное поле сразу. Комбинатор > в .card:has(> img) ограничивает выборку прямыми дочерними элементами.

Специфичность :has() равна специфичности самого сильного аргумента, как у :is(). Поэтому селектор .field:has(input:focus-visible) весит больше, чем .field, и может неожиданно переопределить правило из другого места. Держите такие правила рядом с базовыми стилями компонента.

Добавить поддержку постепенно

CSS
@supports selector(:has(*)) {
    .field:has(input:focus-visible) {
        outline: 2px solid #2563eb;
    }
}

Основное поведение — границу, отступы и текст ошибки — задавайте без :has(), а улучшение включайте внутри @supports. Так старый браузер не потеряет доступность формы.

Не вкладывайте :has() в другой :has() и не используйте псевдоэлементы внутри аргумента: это запрещено синтаксисом. Широкие селекторы вроде body:has(.modal) заставляют браузер проверять больше узлов при изменениях DOM; для больших списков предпочитайте класс-состояние на контейнере, если профилирование показывает просадку.

Документация: MDN :has(), Can I use.