CSS :has() ist der lang ersehnte Parent Selector, du kannst jetzt endlich Eltern-Elemente basierend auf ihren Kindern stylen. Kein JavaScript mehr nötig, kein Workaround, einfach pures CSS. Und ja, es funktioniert in allen modernen Browsern.

đŸ€” Warum gab es nie einen Parent Selector?

Wenn du schon lĂ€nger CSS schreibst, kennst du das Problem. Du willst ein Element stylen, aber die Bedingung hĂ€ngt von einem Kind-Element ab. Ein Formular rot umranden, wenn ein Input ungĂŒltig ist? Ein Card-Layout anpassen, wenn ein Bild vorhanden ist? Klassisches Szenario, und klassisch unmöglich mit CSS.

Der Grund war simpel: Performance. Browser rendern CSS von rechts nach links. Ein Parent Selector hĂ€tte bedeutet, dass der Browser bei jedem Element den gesamten DOM-Baum nach oben traversieren mĂŒsste. Das war lange ein No-Go.

Aber die Browser-Engines sind besser geworden. Viel besser. Und so wurde :has() geboren.

🚀 Was ist :has() und wie funktioniert es?

Mit :has() kannst du ein Element auswÀhlen, das ein bestimmtes Kind-Element enthÀlt. Du schreibst quasi: "WÀhle mir das Element, das dieses Kind hat."

/* WÀhle jeden div, der ein p-Element enthÀlt */
div:has(p) {
  border: 2px solid blue;
}

/* WÀhle jeden article, der ein img enthÀlt */
article:has(img) {
  grid-template-columns: 1fr 1fr;
}

Das Geniale: :has() ist nicht nur ein Parent Selector. Es ist ein relativer Selektor. Du kannst jede beliebige Selektor-Logik darin verwenden.

/* WÀhle ein label, dessen nÀchstes Geschwister-Element ein :checked Input ist */
label:has(+ input:checked) {
  font-weight: bold;
  color: green;
}

đŸ’» Praktische Beispiele

Formular-Validierung stylen

Statt JavaScript zu nutzen, um Klassen zu togglen, kannst du jetzt direkt mit CSS auf den Zustand deiner Inputs reagieren.

/* Fieldset rot umranden, wenn ein ungĂŒltiger Input drin ist */
fieldset:has(input:invalid) {
  border-color: red;
  background: #fff0f0;
}

/* Fieldset grĂŒn, wenn alle Inputs gĂŒltig sind */
fieldset:has(input:valid):not(:has(input:invalid)) {
  border-color: green;
  background: #f0fff0;
}

/* Submit-Button visuell deaktivieren */
form:has(input:invalid) button[type="submit"] {
  opacity: 0.5;
  pointer-events: none;
}

Card Layouts dynamisch anpassen

Stell dir ein Card-Component vor. Manchmal hat es ein Bild, manchmal nicht. Mit :has() passt du das Layout automatisch an.

.card:has(img) {
  display: grid;
  grid-template-columns: 200px 1fr;
}

.card:not(:has(img)) {
  padding: 2rem;
}

/* Card mit Video bekommt mehr Platz */
.card:has(video) {
  grid-column: span 2;
}
/* Nav-Item hervorheben, wenn es den aktiven Link enthÀlt */
nav li:has(a.active) {
  background: #e0e7ff;
  border-radius: 8px;
}

/* Dropdown-Pfeil drehen, wenn UntermenĂŒ offen ist */
nav li:has(.submenu:not([hidden])) > .arrow {
  transform: rotate(180deg);
}
Pseudo Selector :nth-child() interaktiv erklÀrt
Lerne den mĂ€chtigen :nth-child() Selektor interaktiv kennen – mit praktischen Beispielen und ErklĂ€rungen.

⚙ Kombinieren mit anderen Selektoren

Die wahre Power von :has() entfaltet sich in Kombination mit :not(), :is() und :where().

/* Element, das KEIN Bild enthÀlt */
.card:not(:has(img)) {
  min-height: 200px;
}

/* Element, das entweder ein img ODER ein video enthÀlt */
.card:has(:is(img, video)) {
  aspect-ratio: 16 / 9;
}

/* SpezifitÀt-freie Variante mit :where() */
.card:has(:where(img, video)) {
  overflow: hidden;
}

Du kannst :has() auch verschachteln, ja, wirklich:

/* Section, die eine Card enthÀlt, die ein Bild enthÀlt */
section:has(.card:has(img)) {
  padding: 2rem;
}

🎹 Real-World Use Cases

Hat eine aktivierte Checkbox

/* Zeile hervorheben, wenn Checkbox gecheckt */
tr:has(input[type="checkbox"]:checked) {
  background: #e0f2fe;
}

/* Dark Mode Toggle ohne JavaScript */
body:has(#dark-mode:checked) {
  --bg: #1a1a2e;
  --text: #e0e0e0;
  background: var(--bg);
  color: var(--text);
}

Hat einen leeren Input

/* Placeholder-Styling, wenn Input leer ist */
.form-group:has(input:placeholder-shown) label {
  color: #999;
  transform: translateY(0);
}

/* Floating Label Effekt */
.form-group:has(input:not(:placeholder-shown)) label {
  transform: translateY(-1.5rem);
  font-size: 0.75rem;
  color: #3b82f6;
}

Hat ein bestimmtes Kind-Element

/* Sidebar nur anzeigen, wenn Content vorhanden */
.layout:has(.sidebar:not(:empty)) {
  grid-template-columns: 1fr 300px;
}

.layout:not(:has(.sidebar:not(:empty))) {
  grid-template-columns: 1fr;
}

/* Figcaption-Abstand nur wenn vorhanden */
figure:has(figcaption) img {
  margin-bottom: 0;
  border-radius: 8px 8px 0 0;
}

⚠ Performance, Worauf du achten solltest

Ja, :has() ist mĂ€chtig. Aber mit großer Macht kommt große Verantwortung.

Ein paar Regeln:

  • Vermeide zu breite Selektoren. :has(div) auf dem body zwingt den Browser, den gesamten DOM zu durchsuchen.
  • Sei spezifisch. .card:has(> img) (direktes Kind) ist performanter als .card:has(img) (beliebige Tiefe).
  • Nutze den direkten Kind-Kombinator > wo möglich.
  • Teste mit großen DOMs. Was bei 50 Elementen flĂŒssig lĂ€uft, kann bei 5000 ruckeln.
/* ❌ Zu breit – potenziell langsam */
div:has(span) { ... }

/* ✅ Spezifisch und performant */
.card:has(> .card-image) { ... }

đŸ›Ąïž Browser Support

:has() wird von allen modernen Browsern unterstĂŒtzt:

BrowserVersionSeit
Chrome105+August 2022
Firefox121+Dezember 2023
Safari15.4+MĂ€rz 2022
Edge105+August 2022

Safari war hier tatsĂ€chlich der Vorreiter, ungewöhnlich, aber willkommen. Firefox hat etwas lĂ€nger gebraucht, aber seit Ende 2023 sind alle großen Browser an Bord.

Can I Use – CSS :has()
Browser support tables for modern web technologies. Check the current support for CSS :has() across all major browsers.
:has() – CSS | MDN
Die CSS-Pseudoklasse :has() reprĂ€sentiert ein Element, wenn einer der als Argument ĂŒbergebenen relativen Selektoren mindestens ein Element matched.

💡 Fazit

CSS :has() ist kein Hype, es ist ein Game Changer. Jahrelang haben wir JavaScript-Workarounds gebaut, Klassen hin- und hertoggled und uns mit dem Kaskadenmodell herumgeschlagen. Das ist jetzt vorbei.

Du kannst Parent-Elemente stylen, auf ZustĂ€nde von Kind-Elementen reagieren und komplexe UI-Logik direkt in CSS abbilden. Die Browser-UnterstĂŒtzung ist da, die Performance ist gut (wenn du es richtig machst), und die Möglichkeiten sind endlos.

Also: Fang an, :has() zu nutzen. Dein CSS wird es dir danken.