Zum Inhalt springen

Kaskade, Spezifität und Vererbung

Bisher hatte jedes Element höchstens eine passende Regel. In einem echten Stylesheet gelten für dasselbe Element oft mehrere. Dieses Kapitel klärt, wie der Browser dann entscheidet. Wer das versteht, muss bei CSS nicht mehr raten.

  • erkennen, wann zwei Regeln in Konflikt stehen und wann nicht
  • bei gleich genauen Selektoren vorhersagen, welche Regel gewinnt
  • die Spezifität eines Selektors bestimmen und vergleichen
  • erklären, welche Eigenschaften vererbt werden
  • begründen, warum !important selten die richtige Lösung ist

Live-Coding zum Mitmachen

⤓ demo-07-kaskade.zip herunterladen

Der Ordner starter ist der Ausgangsstand, fertig der Stand nach dem Live-Coding.

Enthält: bilder/laufstrecke.svg, fest.html, index.html, kontakt.html, stil.css, termine.html

Für jede Eigenschaft eines Elements kann am Ende nur ein Wert gelten. Sagen zwei Regeln Verschiedenes, muss der Browser sich entscheiden. Das ist kein Fehler, sondern der Normalfall in CSS.

Wichtig ist: Der Konflikt betrifft immer nur einzelne Eigenschaften, nicht ganze Regeln.

Nur die Farbe ist umstritten
p {
color: gray;
font-style: italic;
}
.hinweis {
color: teal;
}
<p>Ein gewöhnlicher Absatz: grau und kursiv.</p>
<p class="hinweis">Die Farbe ist umstritten und wird türkis. Die Kursivschrift ist unumstritten und bleibt.</p>

Beide Regeln treffen den zweiten Absatz. .hinweis setzt sich bei der Farbe durch. Die Kursivschrift kommt weiterhin aus der Regel für p, denn dazu sagt .hinweis nichts. Die Regel für p hat also nicht verloren, sondern nur bei einer Eigenschaft.

Warum aber gewinnt .hinweis? Dafür gibt es eine feste Rangfolge. Das Wort Cascading in CSS beschreibt genau diesen Ablauf. Wir sehen uns die Kriterien vom einfachsten zum anspruchsvollsten an.

Bei zwei gleich genauen Selektoren gewinnt die Regel, die später steht.

Gleich genau: Die spätere Regel gewinnt
.hinweis {
color: teal;
}
.hinweis {
color: purple;
}
<p class="hinweis">Beide Selektoren sind gleich genau. Die spätere Regel gewinnt: lila.</p>

Das gilt auch über mehrere Dateien hinweg: Was später eingebunden wird, steht später. Ein allgemeines Stylesheet bindet man deshalb vor einem besonderen ein.

Die Reihenfolge entscheidet nur bei gleich genauen Selektoren. Vorher zählt, wie genau ein Selektor ist. Diesen Wert nennt man Spezifität.

Genauer schlägt später
p.hinweis {
color: teal;
}
.hinweis {
color: purple;
}
<p class="hinweis">p.hinweis ist genauer als .hinweis und gewinnt, obwohl die Regel früher steht: türkis.</p>

Du zählst drei Dinge im Selektor und schreibst sie als drei Zahlen:

Stelle Was gezählt wird
1. Stelle IDs
2. Stelle Klassen, Attribute und Pseudoklassen
3. Stelle Elemente und Pseudoelemente

Ein paar Beispiele, Zeile für Zeile:

Selektor IDs Klassen usw. Elemente Spezifität
p 0 0 1 0,0,1
nav a 0 0 2 0,0,2
.hinweis 0 1 0 0,1,0
p.hinweis 0 1 1 0,1,1
a:hover 0 1 1 0,1,1
[type="email"] 0 1 0 0,1,0
#inhalt 1 0 0 1,0,0

Du vergleichst von links nach rechts. Die erste Stelle, die sich unterscheidet, entscheidet.

  • .hinweis (0,1,0) schlägt nav ul li a (0,0,4). Eine Klasse wiegt mehr als beliebig viele Elemente.
  • #inhalt (1,0,0) schlägt .a .b .c (0,3,0). Eine ID wiegt mehr als beliebig viele Klassen.
  • a:hover und p.hinweis sind gleich (0,1,1). Dann entscheidet wieder die Reihenfolge.

Damit erklärt sich auch der Rat aus dem letzten Kapitel, IDs im CSS zu meiden: Eine einmal mit ID geschriebene Regel lässt sich nur schwer überschreiben. Der universelle Selektor * zählt gar nicht.

Ein style-Attribut am Element schlägt jeden Selektor.

Im Styles-Bereich stehen die wirksamsten Regeln oben, und unterlegene Deklarationen sind durchgestrichen. Fährst du mit der Maus über einen Selektor, zeigt der Browser dessen Spezifität an. Das ist der schnellste Weg, einen Konflikt zu verstehen.

Jetzt lässt sich der ganze Ablauf zusammensetzen. Der Browser prüft der Reihe nach, bis nur noch ein Wert übrig ist:

  1. Herkunft: Dein Stylesheet schlägt das Standard-Stylesheet des Browsers. Darum kannst du es überschreiben.
  2. Spezifität: Der genauere Selektor gewinnt.
  3. Reihenfolge: Bei Gleichstand gewinnt die spätere Regel.

Zwei Sonderfälle ergänzen diesen Ablauf, !important und Ebenen. Beide folgen am Ende des Kapitels.

Die Rangfolge klärt, was bei mehreren passenden Regeln geschieht. Was aber, wenn für eine Eigenschaft keine Regel das Element trifft? Dann wird manche Eigenschaft vom Elternelement übernommen. Das heißt Vererbung.

So kannst du die Schrift einmal am body setzen, und sie gilt für die ganze Seite.

Was wird vererbt?
.karte {
color: #0f766e;
font-family: Georgia, serif;
border: 2px solid #0f766e;
padding: 0.5rem;
}
<div class="karte">
<p>Farbe und Schrift erbt dieser Absatz. Rahmen und Abstand erbt er nicht.</p>
<button>Formularelemente erben die Schrift nicht von selbst</button>
</div>
Vererbt werden vor allem Nicht vererbt werden vor allem
Texteigenschaften: color, font-family, font-size, font-weight, line-height, text-align Eigenschaften der Box: margin, padding, border, width, background

Das ist sinnvoll: Text soll einheitlich sein, aber nicht jedes Kind soll einen eigenen Rahmen bekommen.

Eine direkte Regel schlägt immer die Vererbung, ganz gleich wie spezifisch die Regel am Elternelement ist. Der vererbte Wert ist nur der Rückfall.

Formularelemente wie button und input erben die Schrift nicht von selbst. Mit dem Wert inherit holst du das nach: font: inherit.

!important hebt eine Deklaration über alle normalen Regeln:

.warnung {
color: red !important;
}

Das löst ein Problem nur scheinbar: Beim nächsten Konflikt braucht es wieder ein !important, und das Stylesheet wird immer schwerer zu ändern. Besser sind diese beiden Gewohnheiten:

  • Selektoren kurz und flach halten, meist genügt eine Klasse.
  • Eine zu spezifische Regel reparieren, statt sie zu überbieten.

Ebenen geben dir eine feste Rangfolge, die sogar über der Spezifität steht:

Spätere Ebene schlägt höhere Spezifität
@layer basis, komponenten;
@layer komponenten {
p { color: green; }
}
@layer basis {
#sehr.spezifisch p { color: red; }
}
<div id="sehr" class="spezifisch">
<p>Grün: Die spätere Ebene gewinnt, egal wie spezifisch die frühere ist.</p>
</div>

Das ist vor allem nützlich, wenn du ein fremdes Stylesheet einbindest: Es kommt in eine frühe Ebene, und dein eigenes CSS gewinnt ohne Wettlauf um die Spezifität. Regeln außerhalb jeder Ebene gewinnen gegen alle Ebenen. Für kleine Projekte brauchst du das noch nicht.

Im Starter stehen am Ende von stil.css zwei Probleme: Der Menülink wird nicht türkis, und der Hinweis auf der Kontaktseite wird nicht rot.

stil.css
/* Neu: Warum wird der Menülink nicht türkis? */
header nav .hauptmenue li a {
color: #55606e;
}
.hauptmenue a {
color: #0f766e;
}
/* Warum ist der Hinweis auf der Kontaktseite nicht rot? */
main p.hinweis {
color: #55606e;
}
.warnung {
color: #b91c1c;
}

Wir gehen so vor:

  1. Das Element in den DevTools untersuchen und die durchgestrichene Regel finden.
  2. Die Spezifität beider Regeln bestimmen und vergleichen.
  3. Die überspezifische Regel entfernen, statt ein !important zu ergänzen.

Starter für die Übung

⤓ uebung-07-kaskade.zip herunterladen

ZIP-Datei entpacken und den Ordner in VS Code öffnen (Datei → Ordner öffnen).

Enthält: bilder/laufstrecke.svg, fest.html, index.html, kontakt.html, raetsel.css, raetsel.html, stil.css, termine.html

Öffne raetsel.html noch nicht im Browser. Lies zuerst raetsel.css.

Schreibe zu jedem Absatz auf, welche Farbe er bekommt und warum.

Öffne die Seite und vergleiche. Wenn deine Vorhersage falsch war, finde im Styles-Bereich die gewinnende und die durchgestrichenen Regeln und vergleiche deren Spezifität.

Absatz 5 soll die Farbe von .sechs bekommen. Schaffe das mit einer neuen Regel, ohne !important und ohne die Regel p zu ändern.

Ergänze zwei Ebenen basis und thema. In thema setzt du .eins auf Gold, in basis setzt du #besonders auf Schwarz. Warum ändert sich bei beiden Absätzen nichts?