Selectorii Nivel 4: Revoluția :has(), :is() și :where()
Timp de peste două decenii, „sfântul graal” al CSS-ului a fost găsirea unei modalități de a stiliza un element părinte în funcție de copiii săi fără a apela la JavaScript. Odată cu adoptarea specificației W3C Selectors Level 4, acest vis a devenit realitate prin :has(), alături de optimizarea specificității oferită de :is() și :where().
:has() acționează ca o condiție logică retroactivă (IF / THEN direct în CSS). Îți permite să spui: „Stilizează acest formular DACĂ vreun câmp intern este invalid” sau „Stilizează acest card într-un mod special DACĂ are o imagine atașată”.
1. Selectorul Părinte: Cum funcționează :has()
Sintaxa acceptă ca argument o listă de selectori relativi la elementul țintă:
/* 1. Stilizeaza cardul DOAR daca contine o imagine */
.card:has(img) {
grid-template-columns: 200px 1fr;
}
/* 2. Schimba fundalul formularului daca un input obligatoriu a esuat validarea */
.form-group:has(input:invalid:touched) {
border-left: 3px solid #d6472e;
}
/* 3. Schimba aspectul paginii intregi cand un modal sau meniu este deschis */
body:has(#nav-toggle:checked) {
overflow: hidden; /* Blocheaza scroll-ul fara o linie de JavaScript! */
}
2. Scenarii Concrete din Lumea Reală
A. Stilizează un titlu doar dacă este urmat imediat de un subtitlu
Înainte, foloseai selectorul frate h2 + p, dar acesta stiliza paragraful, nu titlul. Cu :has(), poți micșora marginea de jos a titlului doar dacă are subtitlu:
/* Reduce marginea h2 doar daca este urmat direct de .subtitlu */
h2:has(+ .subtitlu) {
margin-bottom: 4px;
}
B. Butoane de tip „Radio Card” interactive pur CSS
Când utilizatorul bifează o opțiune dintr-o listă de abonamente, putem stiliza întregul card părinte:
.plan-card:has(input[type="radio"]:checked) {
border-color: var(--accent);
background: var(--paper-dim);
box-shadow: 0 4px 12px rgba(214, 71, 46, .15);
}
3. Bătălia Specificității: :is() vs. :where()
Atât :is() cât și :where() rezolvă problema scrierii repetate de selectori lungi. Însă diferența dintre ele în ceea ce privește specificitatea este monumentală:
| Pseudo-clasă | Calculul Specificității | Caz Ideal de Utilizare |
|---|---|---|
:is(h1, h2, #header) |
Ia specificitatea celui mai puternic selector din paranteză (în acest caz ID-ul, deci 1-0-0). | Grupare de reguli standard unde vrei să păstrezi prioritatea selectorilor. |
:where(h1, h2, #header) |
Are SPECIFICITATE ZERO (0-0-0) absolut întotdeauna, indiferent ce conține paranteza! | Design Systems & CSS Resets: stiluri de bază care pot fi suprascrise extrem de ușor ulterior. |
Exemplu practic cu :where() pentru Design Systems:
/* Reset de baza definit in librarie cu specificitate 0 */
:where(button, .btn) {
padding: 10px 16px;
border-radius: 4px;
background: #0e2a47;
}
/* Oricine poate suprascrie butonul cu o clasa simpla, fara sa foloseasca !important! */
.btn-anulare {
background: transparent; /* Castiga instant, pentru ca :where() are specificitate 0! */
}
4. Pseudo-clasa de Negare :not() (Versiunea extinsă)
În specificațiile vechi CSS3, :not() accepta doar un singur argument simplu (de ex. :not(.disabled)). În CSS Selectors Level 4, poți pasa o listă separată prin virgulă și chiar combinații cu :has():
/* Selecteaza toate link-urile care NU sunt butoane SI NU au clasa logo */
a:not(.btn, .logo, [download]) {
text-decoration: underline;
}
/* Selecteaza cardurile care NU au imagine */
.card:not(:has(img)) {
padding: 32px;
}
5. Performanță în Browser și Bune Practici
- Nu folosi
:has()pe selectori universali pe scară largă: Evită reguli precum*:has(*), deoarece motoarele de navigare trebuie să verifice întregul sub-arbore DOM la fiecare mutație; - Ancorează selectorul
:has()pe o clasă specifică: Scrie.card:has(...)în loc dediv:has(...)pentru a minimiza numărul de noduri evaluate; - Compatibilitate: Toate browserele moderne (Chrome, Edge, Safari, Firefox) suportă complet
:has(),:is()și:where()începând cu 2023–2024.
Specificații W3C și Referințe Consultate:
- W3C Selectors Level 4 (The Relational Pseudo-class :has): w3.org/TR/selectors-4/#has-pseudo
- W3C Selectors Level 4 (Matches-any Pseudo-class :is and :where): w3.org/TR/selectors-4/#matches
- MDN Web Docs — :has() relational selector: developer.mozilla.org/CSS/:has