Долгое время задача стилизовать родителя на основе его потомка или соседний элемент по состоянию соседа решалась только через JavaScript с ручным добавлением/удалением классов. Селектор
:has() навсегда закрывает этот пробел, позволяя описывать такие паттерны декларативно, прямо в CSS, устраняя классы-костыли и снижая риск визуальных артефактов.Как это работает и синтаксис
Селектор
:has() применяет стили к элементу, если внутри его DOM-дерева есть другой элемент, удовлетворяющий заданному условию. Классический пример — подсветка списка при наличии выполненной задачи:ul:has(li.done) {
border: 2px solid green;
}Всё, никаких дополнительных классов на
<ul> — браузер сам проверяет вложенность. Это радикально упрощает поддержку компонентов.Conditional соседей и респонсив
:has() работает и с соседними селекторами, что открывает сценарии управления отступами или визуальной иерархией без JavaScript. Пример:.card:has(+ .featured) {
margin-bottom: 24px;
}Здесь
margin-bottom у карточки добавляется только если сразу после неё идёт элемент с классом featured. Это идеально для списков в e-commerce или карточек в кабинетах, где порядок определяется бекендом, а визуальные отступы должны адаптироваться.Типичная ошибка: перегрузка сложными селекторами
Самая частая проблема — использовать
:has() для проверки глубоко вложенных условий внутри большой страницы, например:body:has(.sidebar.open) .main {
margin-left: 300px;
}Такой селектор заставляет браузер при каждом изменении проверять всех потомков
<body>. На статичных страницах это почти незаметно, но на динамических интерфейсах с частыми ре-рендерами (лендинги с анимациями, панели управления) может дать задержку. Практический совет: ограничивай глубину проверки до 1-2 уровней и избегай зацикливания через :has() внутри :has() — это гарантированно сломает layout.Production-ориентированный пример: формы и валидация
Один из самых полезных сценариев — подсветка всей формы при невалидном поле без JS:
form:has(input:invalid) {
border: 2px solid #e53e3e;
box-shadow: 0 0 0 1px rgba(229, 62, 62, 0.3);
}Это даёт пользователю мгновенный визуальный фидбек: если в форме есть ошибка, весь её контейнер выделяется красной рамкой. Для accessibility важно не забыть aria-атрибуты на самой форме, но это уже вопрос семантики. Основное trade-off:
:has(input:invalid) работает только в современных браузерах (Safari 15.4+), поэтому для поддержки старых версий нужна progressive enhancement.Вывод:
:has() — инженерный инструмент, который делает верстку более предсказуемой, убирая лишние JS-проверки, но требует строгого контроля над глубиной вложенности и браузерной поддержкой.