Lektion 1 von 9
Wie der Browser CSS liest: Kaskade, Spezifität, Vererbung
Du verstehst, warum eine Regel gewinnt, berechnest Spezifität und findest Konflikte in den DevTools in Sekunden.
Worum es geht
Leonie ist im 1. Lehrjahr als Mediamatikerin bei der Wohnwerk Möbel AG in Wil SG. Sie soll die neue Kampagnenseite für die Herbstkollektion umsetzen. Sie bindet ihr Stylesheet ein, schreibt .button { background: #c96a42; } für die terrakottafarbenen Buttons und lädt die Seite neu. Ergebnis: Die Buttons in der Seitenleiste sind rot. Sie kopiert die Regel ans Ende der Datei. Immer noch rot. Sie hängt !important an. Jetzt sind sie terrakotta, aber eine Woche später funktioniert dafür etwas anderes nicht mehr.
Fast jede Person, die CSS lernt, erlebt genau diese Situation. Dahinter steckt kein Zufall, sondern ein klares Regelwerk: die Kaskade. Wer sie versteht, rät nicht mehr herum, sondern sieht in den DevTools in wenigen Sekunden, warum eine Regel gewinnt, und löst das Problem sauber. In dieser Lektion lernst du, wie der Browser CSS liest, wie du die Stärke eines Selektors berechnest und welche Werte ein Element von seinen Eltern erbt.
Eine CSS-Regel lesen
Jede CSS-Regel besteht aus zwei Teilen: dem Selektor (wen betrifft es?) und dem Deklarationsblock (was soll passieren?). Eine Deklaration ist ein Paar aus Eigenschaft und Wert.
/* Selektor Deklarationsblock */
.produkt-karte {
padding: 1.5rem; /* Eigenschaft: padding, Wert: 1.5rem */
background: #f3e9d9; /* Sand aus dem Entwurf */
border-radius: 0.5rem;
}Mit den folgenden Grundselektoren kommst du im Alltag sehr weit:
| Selektor | Beispiel | trifft |
|---|---|---|
| Typselektor | p | alle <p>-Elemente |
| Klassenselektor | .button | alle Elemente mit class="button" |
| ID-Selektor | #sidebar | das eine Element mit id="sidebar" |
| Attributselektor | a[href^="tel:"] | Links, deren href mit tel: beginnt |
| Pseudoklasse | a:hover | Links, über denen die Maus steht |
| Pseudoelement | p::first-line | die erste Zeile jedes Absatzes |
| Universalselektor | * | jedes Element |
Selektoren lassen sich mit Kombinatoren verbinden:
.nav a { } /* Nachfahre: jedes a irgendwo innerhalb von .nav */
.nav > a { } /* Kind: nur a, die direkt in .nav liegen */
h2 + p { } /* direkter Nachbar: das p unmittelbar nach einem h2 */
h2 ~ p { } /* alle folgenden Geschwister-p nach einem h2 */Ein Element hat oft mehrere Klassen, zum Beispiel <a class="button button--primary">. Ein Selektor wie .button.button--primary (ohne Leerzeichen) trifft nur Elemente, die beide Klassen gleichzeitig haben. Mit Leerzeichen wäre es ein Nachfahren-Selektor. Dieser kleine Unterschied ist eine häufige Fehlerquelle.
Die Kaskade: Wer gewinnt?
Auf ein Element passen fast immer mehrere Regeln. Solange sie verschiedene Eigenschaften setzen, ist das kein Problem: Die eine Regel setzt die Farbe, die andere den Abstand. Ein Konflikt entsteht erst, wenn zwei Regeln dieselbe Eigenschaft für dasselbe Element setzen. Dann prüft der Browser der Reihe nach:
- Herkunft und Wichtigkeit. Es gibt Browser-Standardstile (deshalb ist ein
<h1>ohne dein CSS fett und gross), Stile der Nutzerin und deine Autorenstile. Deine Stile schlagen die Browser-Standards. Deklarationen mit!importantschlagen normale Deklarationen. Inline-Stile imstyle-Attribut schlagen jede normale Regel aus einem Stylesheet. - Spezifität. Bei gleicher Herkunft gewinnt der spezifischere Selektor.
- Reihenfolge. Bei gleicher Spezifität gewinnt die Regel, die später kommt, auch über mehrere Dateien hinweg: Was zuletzt geladen wird, steht später.
In Lektion 2 kommt mit den Cascade Layers noch eine Stufe zwischen Herkunft und Spezifität dazu. Für den Anfang reichen diese drei.
Leonies Button steht in der Seitenleiste:
<aside id="sidebar">
<a class="button" href="/herbst">Kollektion ansehen</a>
</aside>#sidebar a { background: #c0392b; } /* theme.css, wird zuerst geladen */
.button { background: #c96a42; } /* kampagne.css, wird danach geladen */Welche Hintergrundfarbe hat der Button und warum?
Spezifität berechnen
Die Spezifität ist ein Gewicht mit drei Stellen, die man als (a, b, c) schreibt:
| Stelle | zählt | Beispiele |
|---|---|---|
| a | ID-Selektoren | #sidebar |
| b | Klassen, Attributselektoren, Pseudoklassen | .button, [type="email"], :hover |
| c | Typselektoren und Pseudoelemente | a, li, ::before |
Der Universalselektor * und die Kombinatoren (Leerzeichen, >, +, ~) zählen nicht.
Verglichen wird stellenweise von links nach rechts, wie bei einer Versionsnummer. Sobald eine Stelle grösser ist, ist der Vergleich entschieden. Eine einzige ID (1,0,0) schlägt also auch zwanzig Klassen (0,20,0). Es gibt keinen Übertrag, man darf die Stellen nicht zu einer Zahl zusammenzählen.
| Selektor | Rechnung | Spezifität |
|---|---|---|
p | 1 Element | (0,0,1) |
.button | 1 Klasse | (0,1,0) |
.nav a | 1 Klasse, 1 Element | (0,1,1) |
nav ul li a | 4 Elemente | (0,0,4) |
.card .card__title:hover | 2 Klassen, 1 Pseudoklasse | (0,3,0) |
#sidebar a | 1 ID, 1 Element | (1,0,1) |
a[href^="tel:"]::after | 1 Attribut, 1 Element, 1 Pseudoelement | (0,1,2) |
Ein Inline-Stil (style="...") steht über allen Selektoren. !important ist kein Teil der Spezifität, sondern hebt die Deklaration auf die höhere Wichtigkeitsstufe aus Schritt 1. Genau darum ist es so gefährlich: Gegen eine !important-Regel hilft nur eine weitere !important-Regel mit höherer Spezifität. So entsteht ein Wettrüsten.
Ordne jedem Selektor seine Spezifität (a, b, c) zu.
Vererbung: Was Kinder von den Eltern übernehmen
Setzt du color auf <body>, ist danach auch jeder Absatz, jede Liste und jede Tabelle in dieser Farbe, obwohl du dort nichts geschrieben hast. Das ist Vererbung: Manche Eigenschaften gibt ein Element an seine Kinder weiter.
- Vererbt werden vor allem Texteigenschaften:
color,font-family,font-size,font-weight,line-height,text-align,letter-spacing,list-style,visibility,cursor. - Nicht vererbt werden Box- und Layouteigenschaften:
margin,padding,border,background,width,height,display,position.
Das ist sinnvoll: Wenn eine Karte einen Rahmen hat, sollen nicht alle Absätze darin ebenfalls einen Rahmen bekommen.
Wichtig ist die Rangfolge: Vererbung greift nur, wenn für das Element selbst keine Regel die Eigenschaft setzt. Ein vererbter Wert ist immer schwächer als jede Regel, die das Element direkt trifft, egal wie spezifisch die Regel beim Elternelement war. Gibt es weder eine Regel noch Vererbung, gilt der Initialwert der Eigenschaft.
#kampagne { color: #1f2a24; } /* (1,0,0), gilt für den Container */
p { color: #555; } /* (0,0,1), trifft das p direkt */Der Absatz in #kampagne wird grau (#555). Die ID-Regel ist zwar stärker, sie trifft aber nur den Container. Beim Absatz kommt ihr Wert nur über Vererbung an, und Vererbung verliert gegen jede direkte Regel.
Mit Schlüsselwörtern steuerst du die Vererbung gezielt: inherit übernimmt den Wert des Elternelements, initial setzt auf den Initialwert, unset wirkt bei vererbten Eigenschaften wie inherit und sonst wie initial. Ein klassischer Einsatz: Buttons erben die Schrift standardmässig nicht, deshalb schreibt man oft button { font: inherit; }.
Leonie setzt auf einer Karte .karte { ... } die folgenden Eigenschaften. Welche davon wirken durch Vererbung auch auf die Absätze in der Karte, ohne dass diese eine eigene Regel haben?
Wähle alle zutreffenden Antworten.
Die DevTools als wichtigstes Werkzeug
Rechtsklick auf ein Element, «Untersuchen»: Im Bereich Styles (Firefox: Regeln) siehst du alle Regeln, die auf das Element passen, sortiert von der stärksten oben zur schwächsten unten. Deklarationen, die verloren haben, sind durchgestrichen. Weiter unten folgen Abschnitte wie «Inherited from body» mit den vererbten Werten.
Im Bereich Computed (Berechnet) steht für jede Eigenschaft der Wert, der am Ende wirklich gilt. Ein Klick auf den Pfeil daneben springt zur Regel, die ihn liefert. Mit den Checkboxen neben jeder Deklaration schaltest du Regeln testweise aus und siehst sofort, was passiert.
Du untersuchst einen Link in den Chrome-DevTools. Im Bereich «Styles» ist bei der Regel .button die Zeile background: #c96a42; durchgestrichen. Was bedeutet das?
Schritt für Schritt: Warum ist der Button rot?
Leonie untersucht den Button in der Seitenleiste. Das HTML:
<aside id="sidebar">
<a class="button" href="/herbst">Kollektion ansehen</a>
</aside>Schritt 1: Element untersuchen. Rechtsklick auf den Button, «Untersuchen». Im Styles-Bereich stehen zwei Regeln:
/* altes Theme, theme.css, Zeile 812 */
#sidebar a {
background: #c0392b; /* rot */
color: #fff;
}
/* Leonies Datei, kampagne.css, Zeile 14 */
.button {
background: #c96a42; /* terrakotta, durchgestrichen */
color: #fff;
padding: 0.75rem 1.25rem;
}Schritt 2: Spezifität berechnen. #sidebar a hat 1 ID und 1 Element, also (1,0,1). .button hat 1 Klasse, also (0,1,0). Erste Stelle: 1 gegen 0. Die alte Regel gewinnt, die Reihenfolge spielt keine Rolle mehr.
Schritt 3: Ursache benennen. Nicht Leonies Regel ist falsch, sondern die alte Regel ist zu breit: Sie gestaltet jeden Link in der Seitenleiste, nur weil er dort steht.
Schritt 4: Sauber lösen statt kontern. Nach Absprache mit dem Team ersetzt Leonie die ID-Regel durch eine Klasse, die nur die Textlinks der Seitenleiste meint:
<aside id="sidebar" class="sidebar">
<a class="sidebar__link" href="/filialen">Filialen</a>
<a class="button button--primary" href="/herbst">Kollektion ansehen</a>
</aside>/* theme.css: neu nur noch Klassen, Spezifität (0,1,0) */
.sidebar__link {
color: #c0392b;
}
/* kampagne.css */
.button {
display: inline-block;
padding: 0.75rem 1.25rem;
color: #fff;
background: #2f5d46; /* Grundvariante: Waldgrün */
}
.button--primary {
background: #9e4628; /* (0,1,0) wie .button, steht später und gewinnt */
}Schritt 5: Kontrolle. Neu laden, Button untersuchen: Die ID-Regel taucht nicht mehr auf, weil sie nicht mehr existiert. Durchgestrichen ist nur noch der Hintergrund von .button, weil .button--primary ihn überschreibt, und das ist gewollt: gleiche Spezifität, die Modifier-Regel steht später. Im Computed-Bereich steht background-color: rgb(158, 70, 40), also das dunkle Terrakotta.
Schritt 6: Nebenwirkungen prüfen. Leonie klickt durch die anderen Seiten mit Seitenleiste. Die Textlinks sind weiterhin rot, weil sie jetzt die Klasse sidebar__link tragen. Kein !important war nötig, und eine spätere Regel mit einer Klasse kann diese Buttons jederzeit wieder überschreiben.
Code-Labor: Spezifität berechnen und vergleichen. Leonie will nicht mehr raten, welche Regel gewinnt. Schreibe zwei Funktionen:
spezifitaet(selektor)liefert ein Array[a, b, c]: a = IDs, b = Klassen, Attributselektoren und Pseudoklassen, c = Elemente und Pseudoelemente.*und Kombinatoren (Leerzeichen,>,+,~) zählen nicht.vergleiche(selA, selB)liefert1, wennselAspezifischer ist,-1, wennselBspezifischer ist, und0bei Gleichstand (dann entscheidet die Reihenfolge im Code).
Funktionale Pseudoklassen wie :is() kommen erst in Lektion 2 vor und sind hier nicht getestet.
Typische Fehler
- Mit
!importantkontern. Das Problem verschwindet, kommt aber verstärkt zurück: Die nächste Änderung braucht wieder!important. Lösung: Spezifität des Gegners in den DevTools ablesen und den zu starken Selektor entschärfen. - Regel ans Ende verschieben und auf Erfolg hoffen. Die Reihenfolge zählt nur bei gleicher Spezifität. Gegen (1,0,1) hilft keine Position.
- Spezifität als eine Zahl zusammenzählen. (0,11,0) ist nicht «mehr» als (1,0,0). Die erste unterschiedliche Stelle entscheidet.
- Vererbung bei Boxeigenschaften erwarten.
paddingauf der Karte gibt den Absätzen darin kein Padding. Und umgekehrt: Ein vererbter Wert verliert gegen jede direkte Regel, auch gegen einen schlichtenp-Selektor. - Leerzeichen verwechseln.
.button.button--primary(beide Klassen am selben Element) ist etwas anderes als.button .button--primary(ein Element innerhalb eines anderen).
Zusammenfassung
- Ein Konflikt entsteht, wenn zwei Regeln dieselbe Eigenschaft für dasselbe Element setzen.
- Die Kaskade prüft der Reihe nach: Herkunft und
!important, dann Spezifität, dann Reihenfolge im Code. - Spezifität (a, b, c): IDs, dann Klassen, Attribute und Pseudoklassen, dann Elemente und Pseudoelemente. Verglichen wird stellenweise von links.
- Vererbung gilt nur für bestimmte Eigenschaften (vor allem Text) und nur, wenn keine Regel das Element direkt trifft.
- Die DevTools zeigen gewinnende und durchgestrichene Deklarationen sowie den berechneten Endwert.
- Gestalte mit Klassen statt IDs und löse Konflikte, indem du zu starke Selektoren entschärfst, nicht mit
!important.
Mit deiner eigenen KI vertiefen
Kopiere einen Prompt in Claude, ChatGPT oder Claude Code. Er macht die KI zur Lernbegleitung statt zum Lösungsautomaten.
Kaskade verstehen mit Gegenfragen Chat-KI
Du lässt dir die Kaskade nicht einfach erklären, sondern kommst mit Fragen selbst auf die Lösung. Ideal nach Lektion 1.