TL;DR: 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.

Artikel teilen:Share article:

Mehr Artikel entdecken

htmx: Interactive Websites Without JavaScript Frameworks 🔄
Vorheriger Artikel

htmx: Interactive Websites Without JavaScript Frameworks 🔄

TL;DR: htmx makes your websites interactive without React, Angular, or Vue. A few HTML attributes replace tons of JavaScript while still delivering dynamic UIs. Here's how it works and when it makes sense. 🤔 What Is htmx, Anyway? Imagine making your website interactive without writing a single line

7 min read 25. Apr. 2026
CSS :has() – The Parent Selector Everyone’s Been Waiting For 🎯
Nächster Artikel

CSS :has() – The Parent Selector Everyone’s Been Waiting For 🎯

TL;DR: CSS :has() is the long-awaited parent selector – you can finally style parent elements based on their children. No more JavaScript hacks, no workarounds, just pure CSS. And yes, it works in all modern browsers. 🤔 Why CSS Never Had a Parent Selector If you've been writing CSS

4 min read 27. Apr. 2026
CSS Scroll-Driven Animations – Animationen ohne JavaScript 🎬
Ähnlicher Artikel

CSS Scroll-Driven Animations – Animationen ohne JavaScript 🎬

TL;DR: CSS Scroll-Driven Animations ermöglichen es dir, Elemente basierend auf der Scroll-Position zu animieren – ganz ohne JavaScript. Mit animation-timeline: scroll() und animation-timeline: view() kannst du Fortschrittsbalken, Fade-Ins und Parallax-Effekte rein deklarativ umsetzen. Performant, elegant und zukunftssicher. 🤔 Was sind Scroll-Driven Animations? Stell dir vor, du scrollst durch eine Website und

5 min read 17. Mai 2026
CSS Grid: Das fehlende Gegenstück zu Flexbox 🧩
Ähnlicher Artikel

CSS Grid: Das fehlende Gegenstück zu Flexbox 🧩

TL;DR: CSS Grid ist das fehlende Puzzlestück zu Flexbox. Während Flexbox eindimensionale Layouts meistert, gibt dir Grid die volle Kontrolle über Zeilen UND Spalten gleichzeitig. In diesem Artikel lernst du alles von den Basics bis Subgrid – mit praktischen Beispielen, die du sofort einsetzen kannst. Du kennst Flexbox? Gut. Dann

4 min read 15. Mai 2026
View Transitions API: Smooth Page Transitions ohne Framework ✨
Ähnlicher Artikel

View Transitions API: Smooth Page Transitions ohne Framework ✨

TL;DR: Die View Transitions API bringt butterweiche Seitenuebergaenge direkt in den Browser - ganz ohne Framework, ganz ohne JavaScript-Bibliothek. Einfach CSS und ein paar Zeilen JS. Hier erfaehrst du, wie das funktioniert und warum du es sofort ausprobieren solltest. 🤔 Was sind View Transitions ueberhaupt? Kennst du das? Du klickst

6 min read 13. Mai 2026
Container Queries in CSS: Das Ende von Media Queries? 📦
Ähnlicher Artikel

Container Queries in CSS: Das Ende von Media Queries? 📦

Container Queries machen Schluss mit dem Viewport-Chaos! 📦 Wie du deine Komponenten endlich wirklich responsive machst – und warum Media Queries trotzdem nicht tot sind.

4 min read 14. März 2026