is() und where() zur gezielten Spezifitätskontrolle
AI generated
{ }
@
CSS · Selektoren · Architektur
is() und where() zur gezielten Spezifitätskontrolle
Null-Spezifität, Migrationsmuster und das Ende von !important

is und where werden meist als reine Abkürzung für lange Selektorlisten vorgestellt, dabei liegt ihr größter Wert in der Spezifitätskontrolle. where() erzeugt Null-Spezifität für Resets und Design-Systeme, is() erbt die höchste Spezifität seines Arguments, ohne dass Entwickler jede Kombination manuell ausschreiben müssen.

18 Min. Lesezeit is-Selektor · where-Selektor · Spezifität Alle modernen Browser

1. Warum is und where Spezifitätswerkzeuge sind, keine Abkürzungen

In den meisten Einführungen werden is und where als praktische Abkürzung vorgestellt: :is(h1, h2, h3) ersetzt drei separate Regeln durch eine einzige. Das ist zwar korrekt, verdeckt aber den eigentlichen strategischen Wert der beiden Pseudoklassen. Der entscheidende Unterschied zwischen ihnen liegt nicht in der Syntax, sondern in der Spezifität: :is() übernimmt die Spezifität des spezifischsten Arguments, während :where() immer eine Spezifität von exakt null hat, unabhängig davon, wie komplex das Argument ist.

Diese Eigenschaft macht is und where zu gezielten Werkzeugen für Spezifitätsarchitektur, nicht nur zur Vermeidung von Wiederholung. Wer verstanden hat, dass :where() Spezifität aktiv auf null zurücksetzt, kann damit Basisstile schreiben, die von praktisch jeder anderen Regel im Projekt überschrieben werden können, ganz ohne !important. Die folgenden Abschnitte zeigen, wie diese gezielte Spezifitätskontrolle in der Praxis eingesetzt wird, von Resets über Design-Systeme bis zu Migrationen aus BEM-lastigem Legacy-CSS.

2. Das Spezifitätsmodell kurz aufgefrischt

CSS-Spezifität wird üblicherweise als Tripel aus drei Zahlen dargestellt: ID-Selektoren zählen in der ersten Kategorie, Klassen, Attribut-Selektoren und Pseudoklassen in der zweiten, Element- und Pseudoelement-Selektoren in der dritten. Ein Selektor wie #header .nav a:hover hätte demnach die Spezifität eins, zwei, eins. Bei einem Konflikt zwischen zwei Regeln gewinnt diejenige mit der höheren Kategorie, unabhängig von der Reihenfolge im Stylesheet, und erst bei völliger Gleichheit entscheidet die Reihenfolge.

is und where greifen genau in diese Berechnung ein, allerdings auf entgegengesetzte Weise. :is(.card, #special) würde die Spezifität von #special übernehmen, weil das die höhere der beiden Alternativen ist, komplett unabhängig davon, welcher der beiden Teile tatsächlich zutrifft. :where(.card, #special) hingegen hat in jedem Fall die Spezifität null, selbst wenn eine ID im Argument steht. Dieses Verständnis ist die Grundlage für jede gezielte Anwendung der beiden Pseudoklassen.


/* :is() inherits the highest specificity among its arguments */
:is(.card, #special) {
  /* Specificity here equals #special: (1,0,0) */
  padding: 1rem;
}

/* :where() always has zero specificity, regardless of the argument */
:where(.card, #special) {
  /* Specificity here is (0,0,0), no matter what */
  padding: 1rem;
}

/* Practical consequence: :where() is trivially overridable */
.card { padding: 2rem; } /* wins over :where(.card, #special) */

3. where(): Null-Spezifität für Resets und Basisstile

Der praktischste Anwendungsfall für :where() ist das Schreiben von Basisstilen, die absichtlich leicht überschreibbar bleiben sollen. Ein klassisches CSS-Reset setzt beispielsweise margin: 0 auf Überschriften, Absätze und Listen. Geschieht das über normale Selektoren wie h1, h2, h3 { margin: 0; }, addiert sich diese Spezifität zu allem, was danach im Projekt noch überschrieben werden muss. Mit :where(h1, h2, h3) { margin: 0; } bleibt die Spezifität exakt null, und jede einzelne Klassenregel im restlichen Projekt kann den Reset ohne Weiteres übersteuern.

Dieses Muster ist besonders wertvoll für Design-System-Bibliotheken oder Basis-Stylesheets, die von anderen Teams oder in anderen Projekten weiterverwendet werden. Eine Bibliothek, die ihre Basisstile mit :where() schreibt, garantiert, dass Konsumenten der Bibliothek niemals gegen unerwartet hohe Spezifität ankämpfen müssen. Ohne :where() müssten Bibliotheksautoren entweder sehr niedrige, oft zu generische Selektoren verwenden oder ihre Nutzer dazu zwingen, selbst mit noch höherer Spezifität oder !important zu arbeiten, um Basisstile zu übersteuern.


/* Reset base styles with zero specificity, trivially overridable */
:where(h1, h2, h3, h4, h5, h6) {
  margin: 0;
  font-weight: inherit;
}

:where(ul, ol) {
  list-style: none;
  padding-inline-start: 0;
}

/* Any single class anywhere in the project wins automatically */
.article-title {
  margin-block: 1rem; /* overrides the :where() reset without effort */
}

4. is(): geerbte Spezifität ohne Wiederholung

Während :where() Spezifität aktiv neutralisiert, verhält sich :is() anders: es spart Schreibarbeit, ohne die Spezifitätsrechnung zu verändern. :is(.sidebar, .footer) a:hover hat exakt dieselbe Spezifität, als hätte man .sidebar a:hover, .footer a:hover separat ausgeschrieben, weil :is() die Spezifität des jeweils höchsten Arguments übernimmt und in diesem Fall beide Argumente dieselbe Kategorie haben.

Der Vorteil von :is() zeigt sich vor allem bei tief verschachtelten Selektor-Kombinationen, bei denen das manuelle Ausschreiben aller Kombinationen schnell unübersichtlich wird. :is(.card, .panel, .widget) :is(h2, h3) { color: var(--heading-color); } deckt sechs Kombinationen mit einer einzigen, gut lesbaren Regel ab. Wichtig ist dabei: :is() verändert nicht die Spezifitätsstrategie eines Projekts, sondern macht lediglich das Ausschreiben bestehender Spezifität kompakter und wartbarer.

5. Rezept: Design-System-Layer ohne Spezifitätskonflikte

Ein wachsendes Design-System hat typischerweise mehrere Schichten: Basis-Tokens, Komponenten-Grundstile und projektspezifische Anpassungen. Ohne bewusste Spezifitätskontrolle konkurrieren diese Schichten schnell miteinander, besonders wenn Komponenten-Bibliotheken versehentlich mit hoher Spezifität geschrieben wurden. Mit :where() lässt sich die Komponenten-Grundschicht bewusst auf Spezifität null halten: :where(.ds-button) { padding: 0.5rem 1rem; border-radius: 0.375rem; } stellt sicher, dass jede projektspezifische Anpassung, egal wie einfach sie geschrieben ist, automatisch gewinnt.

Gleichzeitig lässt sich :is() nutzen, um innerhalb derselben Design-System-Schicht Varianten kompakt zu definieren, ohne die Null-Spezifität-Garantie zu verlieren: :where(.ds-button):is(.ds-button--primary, .ds-button--secondary) { font-weight: 600; } behält die Spezifität null bei, weil die äußere :where()-Hülle die Gesamtspezifität dominiert. Diese Kombination erlaubt komplexe, gut strukturierte Design-System-Selektoren, die trotzdem niemals mit projektspezifischem CSS konkurrieren.


/* Design system base layer: zero specificity, always overridable */
:where(.ds-button) {
  padding: 0.5rem 1rem;
  border-radius: 0.375rem;
  border: 1px solid transparent;
}

/* Variants nested inside :where() keep the zero-specificity guarantee */
:where(.ds-button):is(.ds-button--primary, .ds-button--secondary) {
  font-weight: 600;
}

/* Any project-level class overrides the design system without !important */
.checkout-button {
  padding: 0.75rem 1.5rem; /* wins automatically */
}

6. Rezept: BEM-Overrides ohne !important migrieren

Viele gewachsene Projekte kämpfen mit BEM-Selektoren, die über Jahre zu Ketten wie .block__element--modifier.is-active .child angewachsen sind. Jede neue Anforderung führte zu noch spezifischeren Selektoren oder direkt zu !important, weil die bestehende Spezifität sonst nicht zu überwinden war. Eine Migration hin zu :where()-basierten Basisstilen erlaubt es, diese Spirale schrittweise zu durchbrechen, ohne das gesamte Stylesheet auf einmal umschreiben zu müssen.

Der praktikable Weg: neue Komponenten von Anfang an mit :where() für Basisstile schreiben, während bestehende BEM-Ketten unangetastet bleiben. Da :where()-Regeln praktisch von jeder Klassen-Regel überschrieben werden, entstehen keine neuen Spezifitätskonflikte mit dem Altbestand, selbst wenn beide Systeme parallel im selben Projekt existieren. Diese Koexistenz macht :where() zu einem risikoarmen Einstiegspunkt für eine langfristige Spezifitäts-Sanierung, ganz ohne einen kompletten Rewrite zu erzwingen.

7. Kombination mit Cascade Layers

Cascade Layers über @layer und is und where lösen verwandte, aber unterschiedliche Probleme: Layers kontrollieren, welche Regelgruppe bei einem Konflikt gewinnt, unabhängig von der Spezifität innerhalb der Gruppe, während :where() die Spezifität innerhalb einer einzelnen Regel steuert. Kombiniert ergibt sich eine zweistufige Kontrolle: @layer reset { :where(h1, h2, h3) { margin: 0; } } platziert den Reset in der niedrigsten Layer-Priorität und hält gleichzeitig die interne Spezifität bei null.

Diese Kombination ist besonders robust, weil sie zwei unabhängige Mechanismen nutzt, statt sich auf einen einzigen zu verlassen. Selbst wenn ein Entwickler versehentlich eine ID-lastige Regel in die Reset-Layer schreibt, sorgt die Layer-Reihenfolge dafür, dass diese Regel gegenüber allen späteren Layers verliert. :where() und Cascade Layers ergänzen sich damit, statt sich zu duplizieren, und bilden zusammen ein deutlich robusteres System als Spezifität allein.

8. Fallstricke: wo where() die Spezifität nicht wirklich senkt

Ein häufiges Missverständnis ist, dass :where() die Spezifität von Elementen außerhalb seines eigenen Arguments beeinflusst. :where(.card) .title hat die Spezifität von .title allein, also eine Klasse, nicht null, weil nur der Teil innerhalb der Klammern auf null gesetzt wird, der Rest des Selektors zählt normal weiter. Wer :where() nur um einen Teilausdruck herum schreibt, in der Annahme, damit die Gesamtspezifität zu senken, wird von diesem Verhalten überrascht.

Ein zweiter Fallstrick betrifft verschachtelte Kombination mit :is(): :where(.card):is(.featured, #special) behält zwar die äußere Null-Spezifität der :where()-Hülle, aber nur, weil :where() tatsächlich außen liegt. Vertauscht man die Reihenfolge zu :is(.card):where(.featured, #special), wird die Spezifität stattdessen von :is(.card) bestimmt, nicht null. Die Position der Pseudoklassen im Selektor entscheidet also direkt darüber, welcher Teil tatsächlich neutralisiert wird.

9. is() und where() im direkten Vergleich

Die folgende Tabelle fasst zusammen, wann welche der beiden Pseudoklassen die richtige Wahl für gezielte Spezifitätskontrolle ist.

Ziel :is() :where() Empfehlung
Wiederholung vermeiden Passend Auch möglich, ändert Spezifität is() bei unveränderter Spezifität
Immer überschreibbare Basisstile Übernimmt bestehende Spezifität Passend where() für Null-Spezifität
Design-System-Bibliothek Riskant bei hoher Argument-Spezifität Passend where() garantiert Überschreibbarkeit
CSS-Reset Erhöht Spezifität wie normale Regel Passend where() ist Standardwahl für Resets
Kompakte Kombinatorik ohne Spezifitätsänderung Passend Setzt Spezifität ungewollt auf null is() bewahrt bestehende Struktur

Die Faustregel lautet: :where() immer dann, wenn eine Regel absichtlich leicht überschreibbar bleiben soll, :is() immer dann, wenn nur Schreibarbeit gespart werden soll, ohne die bestehende Spezifitätslogik zu verändern. Beide zusammen ergeben ein präzises Werkzeug-Paar für bewusste Spezifitätsarchitektur, statt nachträglicher Reparatur mit !important.

Mironsoft

CSS-Architektur, Design-Systeme und Legacy-Migration

Stylesheets ohne !important-Kaskaden und Spezifitätskämpfe?

Wir analysieren gewachsene Spezifitätsprobleme in bestehenden Projekten und migrieren Basisstile gezielt auf where()-basierte Architektur, für wartbare, konfliktfreie Stylesheets.

Spezifitäts-Audit

!important-Häufungen und Spezifitätskonflikte im Bestand identifizieren

Design-System-Aufbau

where()-basierte Basisschicht für neue oder wachsende Komponenten-Bibliotheken

Cascade-Layer-Strategie

@layer und where() kombinieren für robuste, zweistufige Kaskade-Kontrolle

10. Zusammenfassung

is und where lösen ein gemeinsames Problem der Selektorwiederholung, aber mit einem entscheidenden Unterschied in der Spezifitätswirkung. :is() übernimmt die höchste Spezifität seines Arguments und spart Schreibarbeit, ohne die bestehende Spezifitätslogik zu verändern. :where() setzt die Spezifität aktiv auf null zurück, unabhängig vom Argument, und eignet sich damit gezielt für Resets, Design-System-Basisschichten und jede Regel, die absichtlich leicht überschreibbar bleiben soll.

Kombiniert mit Cascade Layers entsteht eine zweistufige Kontrolle über die Kaskade: Layer-Reihenfolge für Regelgruppen, :where() für die interne Spezifität einzelner Regeln. Wer diese Werkzeuge gezielt einsetzt, kann Basisstile schreiben, die niemals gegen projektspezifisches CSS ankämpfen, und bestehende BEM-lastige Projekte schrittweise migrieren, ohne auf !important zurückgreifen zu müssen.

is() und where() Spezifitätskontrolle: Das Wichtigste auf einen Blick

is()

Übernimmt die höchste Spezifität seines Arguments, spart Schreibarbeit ohne Spezifität zu verändern.

where()

Hat immer Spezifität null, ideal für Resets, Design-Systeme und leicht überschreibbare Basisstile.

Positionierung im Selektor

Nur der Teil innerhalb von where() wird auf null gesetzt, der Rest des Selektors zählt normal weiter.

Cascade Layers

@layer plus where() ergibt zweistufige Kontrolle, robuster als Spezifität allein.

11. FAQ: is() und where() Spezifitätskontrolle

1Hauptunterschied bei der Spezifität?
is() übernimmt die höchste Argument-Spezifität, where() hat immer Spezifität null.
2Wann where() statt is()?
Bei Resets, Design-System-Basisschichten oder Bibliotheken, die leicht überschreibbar bleiben sollen.
3Wirkt where() auch außerhalb der Klammern?
Nein, nur der Teil innerhalb der Klammern wird auf null gesetzt, der Rest zählt normal.
4Können beide kombiniert werden?
Ja, aber die Reihenfolge entscheidet, welche Pseudoklasse die Gesamtspezifität dominiert.
5Ersetzt where() !important?
In vielen Fällen ja, weil Null-Spezifität automatisch von jeder Klassen-Regel überschrieben wird.
6Kombination mit Cascade Layers?
@layer für Regelgruppen-Priorität, where() für interne Spezifität, zusammen zweistufige Kontrolle.
7Gut für BEM-Migration?
Ja, neue Komponenten mit where() schreiben, bestehende BEM-Ketten bleiben unangetastet.
8Erhöht is() die Spezifität?
Nein, es übernimmt exakt die Spezifität, die separate Selektoren ohnehin hätten.
9Typischer Fehler bei where()?
Anzunehmen, es senke die Gesamtspezifität eines langen Selektors statt nur des eigenen Klammerteils.
10Browser-Support?
Beide werden von allen aktuellen Evergreen-Browsern unterstützt und gelten als produktionssicher.