CSS :has(): стилизовать родителя по дочернему элементу
:has() — функциональный псевдокласс, который выбирает элемент, если внутри него совпал относительный селектор. Он позволяет стилизовать родителя по состоянию дочернего поля без JavaScript. Поддержка есть в Chrome 105+, Safari 15.4+ и Firefox 121+, поэтому базовые стили должны работать и без него.
Подсветить поле с ошибкой и фокусом
.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, и может неожиданно переопределить правило из другого места. Держите такие правила рядом с базовыми стилями компонента.
Добавить поддержку постепенно
@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.