Jeśli interesujesz się frontendem, to pewnie zauważyłeś, że CSS w ostatnich latach przechodzi małą rewolucję. Pojawiają się nowe funkcje, które jeszcze kilka lat temu wydawały się niemożliwe. Jedną z nich jest selektor :has(). Dla wielu osób to „pierwszy prawdziwy selektor rodzica”, który pozwala robić rzeczy wcześniej dostępne tylko w JavaScript. Brzmi dobrze? To dopiero początek.
W tym artykule pokażę Ci, do czego służy :has(), jak działa, jakie problemy rozwiązuje i jak możesz wykorzystać go w praktyce. Będzie dużo przykładów, trochę kodu i sporo inspiracji.
Co to jest selektor :has()?
:has() to tzw. relacyjny pseudoklasa selektora, która pozwala stylować element na podstawie tego, co znajduje się w jego wnętrzu lub obok niego. To ogromna zmiana, bo wcześniej CSS działał tylko „w dół” – mogliśmy stylować dzieci, ale nie rodziców.
Dzięki :has() możemy w końcu powiedzieć:
„Zmień styl elementu A, jeśli w środku znajduje się element B”.
Albo:
„Zmień wygląd elementu X, jeśli następny element spełnia warunek Y”.
To otwiera drzwi do wielu rzeczy, które wcześniej wymagały JavaScriptu.
Dlaczego :has() jest takie przełomowe?
Bo pozwala na coś, czego CSS nie potrafił przez 25 lat: stylowanie rodzica na podstawie dziecka.
Przykład? Chcesz podświetlić <form>, jeśli któryś z inputów ma błąd? Kiedyś musiałeś pisać JS. Teraz wystarczy:
form:has(input:invalid) {
border: 2px solid red;
}
I gotowe.
Jak działa :has()?
Możesz myśleć o nim jak o odwrotności :has() w jQuery. W CSS działa podobnie: selektor wybiera element, jeśli w środku lub w relacji znajduje się inny element pasujący do warunku.
Składnia:
element:has(selektor)
Przykłady:
div:has(img)– wybiera każdydiv, który zawiera obrazek.li:has(ul)– wybiera elementy listy, które mają podlistę.section:has(h2)– wybiera sekcje z nagłówkiem H2.
Przykłady użycia selektora :has()
Poniżej znajdziesz praktyczne przykłady, które możesz wykorzystać w realnych projektach.
1. Stylowanie rodzica, jeśli dziecko spełnia warunek
Załóżmy, że masz formularz i chcesz podświetlić go na czerwono, jeśli któryś input jest niepoprawny.
form:has(input:invalid) {
background: #ffe6e6;
border: 2px solid red;
}
To działa natychmiast – bez JS, bez klas, bez kombinowania.
2. Pokazywanie/ukrywanie elementów na podstawie stanu innego elementu
Chcesz pokazać komunikat dopiero wtedy, gdy checkbox jest zaznaczony?
HTML:
<label> <input type="checkbox" id="agree"> Akceptuję regulamin </label> <p class="info">Dziękujemy za akceptację!</p>
CSS:
.info {
display: none;
}
label:has(input:checked) + .info {
display: block;
}
Czyli: jeśli label zawiera zaznaczony checkbox, pokaż .info.
3. Stylowanie elementu na podstawie tego, co znajduje się obok
Możesz stylować elementy w relacji rodzeństwa.
Przykład: podświetl przycisk „Usuń”, jeśli obok jest checkbox zaznaczony.
.item:has(input:checked) .delete-btn {
opacity: 1;
pointer-events: auto;
}
4. Tworzenie zaawansowanych efektów hover
Możesz zmieniać wygląd elementu, gdy użytkownik najedzie na jego dziecko.
.card:has(.btn:hover) {
transform: scale(1.03);
box-shadow: 0 0 20px rgba(0,0,0,0.2);
}
Efekt? Karta reaguje, gdy najedziesz na przycisk w środku.
5. Walidacja formularzy bez JavaScript
Chcesz podświetlić label, jeśli input jest pusty?
label:has(input:placeholder-shown) {
color: gray;
}
Chcesz podświetlić label na zielono, jeśli input jest poprawny?
label:has(input:valid) {
color: green;
}
6. Tworzenie akordeonów bez JavaScript
HTML:
<div class="accordion"> <h3>FAQ 1</h3> <div class="content">Treść 1</div> <h3>FAQ 2</h3> <div class="content">Treść 2</div> </div>
CSS:
.content {
display: none;
}
h3:has(+ .content:hover),
h3:hover + .content {
display: block;
}
Możesz też użyć checkboxów i zrobić pełny akordeon bez JS.
7. Stylowanie elementów listy, które mają podlisty
li:has(ul) {
font-weight: bold;
color: #333;
}
Przydatne np. w menu wielopoziomowym.
8. Wyszukiwanie elementów, które mają określone potomki
Chcesz znaleźć wszystkie artykuły, które mają obrazek?
article:has(img) {
border-left: 4px solid #007bff;
}
9. Zmiana wyglądu kontenera w zależności od liczby elementów
Możesz sprawdzić, czy w środku jest np. więcej niż jeden element.
.gallery:has(img + img) {
grid-template-columns: repeat(2, 1fr);
}
Czyli: jeśli galeria ma co najmniej dwa obrazki, ustaw układ dwukolumnowy.
Przykład praktyczny: interaktywna karta produktu
HTML:
<div class="product"> <img src="phone.jpg" alt="Telefon"> <p class="price">1999 zł</p> <button class="buy">Kup teraz</button> </div>
CSS:
.product:has(.buy:hover) {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
}
Efekt? Karta reaguje, gdy użytkownik najedzie na przycisk „Kup teraz”.
Przykład z kodem JavaScript – ale opcjonalny
Czasem możesz chcieć połączyć :has() z JS, np. do dynamicznego dodawania klas.
document.querySelectorAll("section").forEach(section => {
if (section.matches(":has(h2)")) {
section.classList.add("has-title");
}
});
To pokazuje, że :has() działa także w metodach JS takich jak .matches() czy .querySelector().
Wsparcie przeglądarek
Dobra wiadomość: :has() działa już w:
- Chrome
- Edge
- Safari
- Opera
- Firefox (od 2024)
Czyli możesz używać go praktycznie wszędzie.
Dlaczego warto używać :has()?
Bo:
- upraszcza kod,
- eliminuje potrzebę pisania JS do prostych rzeczy,
- pozwala tworzyć bardziej interaktywne UI,
- daje większą kontrolę nad strukturą HTML,
- umożliwia stylowanie rodzica – coś, czego brakowało od lat.
To narzędzie, które naprawdę zmienia sposób myślenia o CSS.
Selektor :has() to jedna z najpotężniejszych funkcji, jakie trafiły do CSS. Pozwala stylować elementy na podstawie ich dzieci, rodzeństwa i relacji między nimi. Dzięki temu możesz tworzyć bardziej inteligentne, dynamiczne i interaktywne interfejsy – często bez ani jednej linijki JavaScriptu.
Jeśli jeszcze go nie używasz, to naprawdę warto zacząć. Gwarantuję, że gdy raz go poznasz, będziesz zastanawiać się, jak frontend radził sobie bez niego przez tyle lat.