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:
- Spalte A: Anzahl der IDs
- Spalte B: Anzahl der Klassen, Attribute und Pseudo-Klassen
- Spalte C: Anzahl der Elemente und Pseudo-Elemente
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:
- Selektor A ist zu spezifisch
- Selektor B muss noch spezifischer werden
- Selektor C muss komplexer sein als B
- 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:
- Varianten-Zerstörung: Ein
.button.ghost(der eigentlich transparent sein soll) bekommt nun zwangsweise den grauen Hintergrund aufgezwungen, sobald er disabled ist. - Kontext-Blockade: Wenn ein Button auf einem dunklen Hintergrund (z. B. im Footer) eine andere Disabled-Farbe bräuchte, wird diese ignoriert. Das globale
!importantgewinnt immer. - Eskalation: Um diesen Style jemals wieder zu überschreiben, müssen die nächsten Entwickler:innen ebenfalls
!importantverwenden. 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.
Kommentare