Skip to content

Türchen 2 – Spezifität beherrschen

Published: at 07:00 AM

Spezifität ist ein Kernkonzept von CSS und einer der häufigsten Gründe dafür, warum Styles nicht so greifen, wie man es erwartet. Selbst erfahrene Teams stolpern im Alltag darüber, weil Spezifität oft als Nebenprodukt entsteht und nicht als bewusste Entscheidung. Wer Spezifität versteht, kontrolliert CSS. Wer sie ignoriert, kämpft gegen CSS.

In diesem Türchen geht es darum, wie Spezifität funktioniert, wo in realen Projekten Probleme entstehen und wie man sie langfristig in den Griff bekommt.

Was Spezifität eigentlich misst

Spezifität bewertet, wie präzise ein Selektor ein Element anspricht. Das System dahinter ist einfach, aber immens wirkungsvoll.

Dabei zählt CSS:

Die Werte werden von links nach rechts verglichen. Eine ID in Spalte A schlägt unendlich viele Klassen in Spalte B.

Ein paar Beispiele:

h1 {}                 /* Spezifität: (0, 0, 1) */
.title {}             /* Spezifität: (0, 1, 0) */
#main {}              /* Spezifität: (1, 0, 0) */
button[disabled] {}   /* Spezifität: (0, 1, 1) */

Und der Klassiker:

<button style="background: red"></button>

Ein Inline-Style gilt als spezifischste Quelle im Author Origin und wird in Browsern in einer separaten Spalte (Spalte A, wenn man Inline als A betrachtet, oder Spalte 0) geführt. Vereinfachend wird er oft als Spezifität (1, 0, 0, 0) dargestellt.

Warum Spezifität oft zu Problemen führt

Spezifität wird selten bewusst gestaltet. Sie entsteht zufällig, indem man Komponenten immer weiter verschachtelt oder zusätzliche Klassen ergänzt.

Ein typisches Beispiel aus einem meiner Projekte:

/* Designsystem */
.card .title {
  font-size: 1.25rem;
}

/* Eine spätere Anpassung */
.title {
  font-size: 1.5rem;
}

Auf den ersten Blick würde man denken, dass die zweite Regel gewinnt, weil sie später kommt. Aber: .card .title hat eine höhere Spezifität ((0, 2, 0) vs. (0, 1, 0)). Ein Bug der schnell mal ein paar Stunden kosten kann.

Das eigentliche Problem

Das Problem ist nicht nur ein falsch verstandener Selektor. Es ist die Tatsache, dass die Spezifität jeder neuen Regel das gesamte System instabiler macht.

Kaskadierende Verschachtelung: Ein schleichendes Risiko

In vielen Codebases findet man übermäßig verschachtelte Selektoren:

.layout .header .nav .item .link {
  color: var(--gray-700);
}

Spezifität: (0, 5, 0)

Ein solcher Selektor macht es nahezu unmöglich, spätere Overrides sauber durchzuführen, ohne die eigene Spezifität ebenfalls zu erhöhen.

Das Ergebnis ist ein Teufelskreis:

  1. Selektor A ist zu spezifisch
  2. Selektor B muss noch spezifischer werden
  3. Selektor C muss komplexer sein als B
  4. Am Ende gewinnt !important

So entsteht technischer CSS-Schuldenberg.

!important: Symptom, nicht Lösung

Viele greifen im Alltag zu !important, weil eine Regel „einfach nicht greifen will“.

Die Ursache liegt fast immer in Spezifität. !important löst das akute Problem, verschärft aber langfristig die Instabilität des Systems.

Ein realer Fall:

.button {
  background: var(--primary);
}

.header .button {
  background: var(--brand-blue);
}

.button[disabled] {
  background: var(--gray-200);
}

/* Ein:e Entwickler:in setzt das */
.button[disabled] {
  background: var(--gray-300) !important;
}

Dieser !important override fixte einen Bug, erzeugte dabei aber drei neue:

  1. Varianten-Zerstörung: Ein .button.ghost (der eigentlich transparent sein soll) bekommt nun zwangsweise den grauen Hintergrund aufgezwungen, sobald er disabled ist.
  2. Kontext-Blockade: Wenn ein Button auf einem dunklen Hintergrund (z. B. im Footer) eine andere Disabled-Farbe bräuchte, wird diese ignoriert. Das globale !important gewinnt immer.
  3. Eskalation: Um diesen Style jemals wieder zu überschreiben, müssen die nächsten Entwickler:innen ebenfalls !important verwenden. Das startet eine Spirale, die den Code unwartbar macht.

Wie man Spezifität aktiv reduziert

Ein wichtiges Werkzeug ist der Selektor :where().

:where() setzt Spezifität auf Null

Beispiel:

:where(.card .title) {
  font-weight: 500;
}

Egal wie tief verschachtelt, die Spezifität bleibt 0. Damit wird CSS deutlich stabiler, da spätere Overrides keine „Spezifitätsschlacht“ gewinnen müssen.

Ein realer Vergleich:

/* Früher */
.card .title {
  font-weight: 500;
}

/* Heute */
:where(.card .title) {
  font-weight: 500;
}

Mit der zweiten Variante kann jede spätere Regel mit einer Spezifität von (0, 0, 1) (z. B. h1) überschreiben. Das fühlt sich im ersten Moment ungewöhnlich an, führt aber zu spürbar saubererem CSS.

Der strategische Ansatz: Selektoren bewusst gestalten

In großen Projekten empfehle ich folgende Prinzipien:

1. Eine Klasse pro Komponente

Statt:

.page .content .box .title {}

Lieber:

.component-title {}

Hinweis: Das widerspricht dem Ansatz von Tailwind CSS. Tailwind setzt auf viele kleine Utility-Klassen, die das Spezifitätsproblem umgehen, indem sie alle die gleiche niedrige Spezifität haben, aber durch ihre Reihenfolge im generierten CSS (“Utilities Layer”) gewinnen. Wer klassisches CSS oder CSS Modules schreibt, fährt mit “einer Klasse pro Komponente” am sichersten.

2. Keine ID-Selektoren im CSS

IDs erzeugen unnötig hohe Spezifität und bieten wenig Mehrwert gegenüber Classes.

3. Inline-Styles nur für wirklich dynamische Werte

Also Werte, die aus Runtime-Logik kommen. Nicht für Farben. Nicht für Layout.

4. Vermeide Selektoren mit mehr als 2 Klassen

Alles darüber schafft schnell technisches Risiko.

Ein Beispiel aus der Praxis: Wie man Spezifität richtig plant

Ein früherer Kunde hatte folgendes Setup:

/* Komponente */
.button {
  padding: 0.75rem 1rem;
  background: var(--primary);
}

/* Variante */
.button.is-secondary {
  background: var(--secondary);
}

/* Dark-Mode Override */
.dark .button {
  background: var(--primary-dark);
}

Das Problem: .dark .button und .button.is-secondary haben beide die Spezifität (0, 2, 0). Hier entscheidet die Quellreihenfolge – und .dark .button stand später im CSS, wodurch es ungewollt gewann.

Die Lösung:

:where(.button) {
  padding: 0.75rem 1rem;
  background: var(--primary);
}

.button.is-secondary {
  background: var(--secondary);
}

.dark :where(.button) {
  background: var(--primary-dark);
}

Durch den Einsatz von :where() wurde die Basis-Spezifität auf 0 gesetzt und das gesamte System wurde stabiler.

Fazit

Spezifität entscheidet, welche Regel in CSS gewinnt. Wer sie ignoriert, wird unweigerlich Probleme bekommen und das besonders in wachsenden Projekten. Wer sie kontrolliert, schreibt CSS, das vorhersehbar, stabil und langfristig wartbar ist.

Spezifität ist kein theoretisches Konzept, sondern ein praktisches Werkzeug. Richtig eingesetzt, ist sie einer der stärksten Hebel für eine saubere CSS-Architektur.


☕ Buy me a coffee

Wenn Dir meine Beiträge gefallen und sie Dir bei Deiner Arbeit helfen, würde ich mich über einen "Kaffee" und ein paar nette Worte von Dir freuen.

Buy me a coffee

Previous Post
Türchen 3 – Moderne Selektoren nutzen
Next Post
Türchen 1 – Die Cascade verstehen

Kommentare