Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 11 Min.

Das C in CSS

Damit Sie die Kaskade beherrschen und nicht umgekehrt die Kaskade Sie, gibt es einige nützliche Techniken.
© Foto: mStudioVector / Shutterstock
Das C in CSS steht für Cascading, also kaskadierend. Es ist sicher eine der prägendsten Eigenschaften von CSS – und gleichzeitig eines der am häufigsten missverstandenen Features. Die Kaskade bestimmt, wie mehrere Angaben interpretiert werden, die auf ein Element wirken, und welche sich bei widersprechenden Angaben durchsetzen.Um nicht gegen die Kaskade zu arbeiten, sondern mit ihr, ist es wichtig, ihre grundlegende Funktionsweise zu verstehen sowie die relevanten Faktoren zu kennen. Eine der wichtigsten dabei ist die Spezifität. Durch bestimmte Tricks lässt sich die Spezifität von einzelnen Selektoren erhöhen oder vermindern, wobei sich neue Selektoren wie :where() als praktisch erweisen. Um Probleme mit der Spezifität von Anfang an zu vermeiden, helfen Methodologien wie BEM oder ITCSS.In naher Zukunft werden wir ein weiteres, sehr nützliches Tool für die Beherrschung der Kaskade an die Hand bekommen – die Kaskade-Layer. Was es damit auf sich hat, erfahren Sie am Ende des Artikels. Sehen wir uns aber zuerst an, welche Faktoren bei der Kaskade relevant sind und wie man mit der Spezifität umgeht. Bei der Kaskade spielen verschiedene Faktoren eine Rolle:Beachten Sie, dass die Faktoren in dieser Liste nach ihrer Rangfolge geordnet sind: ein weiter oben stehender Faktor zählt mehr als ein weiter unten stehender. Ein Beispiel: Die Spezifität hat ein größeres Gewicht als die Quellcodereihenfolge. Es setzt sich also ein Selektor mit höherer Spezifität durch, auch wenn er im Quellcode weiter unten steht. Sehen wir uns ausgewählte Faktoren genauer an.
Cascading and InheritanceLevel 5 führt die Kaskade-Layer ein(Bild 1) © Maurice
Bei der Ermittlung, ob eine Regel gilt, spielt es eine Rolle, ob sie mit !important versehen ist. Das folgende Beispiel demonstriert dies:

.wichtig {
  color: red !important;
}
#einmalig {
  color: blue;
}
 
 
Diese Regeln wirken auf einen Absatz, der die entsprechende id und die entsprechende Klasse hat:

<p class="wichtig" id="einmalig">formatiert</p>
 
 
Obwohl an sich die Spezifität des id-Selektors #einmalig höher ist als die Spezifität des Klassenselektors .wichtig, setzt sich die rote Schriftfarbe durch, da diese Angabe mit !important versehen ist.

Stylesheet-Varianten

Zudem ist der Ursprung der Angaben relevant: Formatierungsregeln können vom Browser stammen (User Agent Stylesheet), vom Benutzer (User Stylesheet) oder durch den Autor des Stylesheets definiert sein (Author Stylesheet). Was sich wann durchsetzt, hängt davon ab, ob die Angabe mit !important versehen ist. Dabei gilt folgende Rangfolge:Deklarationen, die weiter oben in der Liste stehen, setzen sich durch gegenüber weiter unten stehenden. Bemerkenswert ist dabei, dass sich durch !important die Reihenfolge ändert: Während eine normale Autordeklaration größeres Gewicht hat als eine normale Browserdeklaration, ist es bei der Ergänzung von !important umgekehrt. Prinzipiell gilt das, was die Autoren eines Stylesheets definieren – außer es ist wirklich wichtig, dann setzt sich der Benutzer durch, aber das allerletzte Wort hat der Browser. Das bedeutet konkret: Sie können eine Deklaration, die im Browser mit !important versehen ist, nicht in Ihrem Stylesheet überschreiben. Im Firefox-Browser-Stylesheet findet sich beispielsweise folgende Regel mit !important, die sich nicht aushebeln lässt:

iframe:fullscreen { 
  border: none !important; 
  padding: unset !important; 
}
 
 
Umgekehrt können Sie problemlos normale Browserdeklarationen überschreiben, ohne sich um die Spezifität kümmern zu müssen.

Spezifität von Selektoren

Eine wichtige Rolle bei der Ermittlung, welche Regel gilt, spielt die Spezifität von Selektoren. Je spezifischer ein Selektor ist, desto eher setzt er sich durch. Die Spezifität wird durch eine dreistellige Zahl angegeben, die sich so ermitteln lässt:Zu beachten ist, dass Pseudoklassen wie Klassen behandelt werden und Pseudoelemente wie Typselektoren – denn beide werden manchmal falsch eingeordnet. :not(), :where() und :is() sind außerdem Spezialfälle. Wenn ein Selektor aus mehreren Teilen besteht, werden die Angaben addiert. Bei einem einfachen Typselektor wie li ist die Spezifität 001:

li {} /* a=0 b=0 c=1, Spezifität 001 */
 
 
Bei dem Selektor ul li ist sie hingegen 002. Da 002 größer ist als 001, setzt sich im folgenden Beispiel die rote Schrift durch:

ul li { color: red; } 
li { color: blue; } 
 
 
Andere Zeichen wie zum Beispiel + oder ~ verändern die Spezifität nicht, deswegen hat ul ol + li dieselbe Spezifität wie ul ol li, nämlich 003:

ul ol+li {} /* a=0 b=0 c=3, Spezifität 003 */
 
 
Bei Klassenselektoren wird die zweite Stelle belegt – der Selektor ul ol li.red besitzt die Spezifität von 013 (drei Typselektoren, ein Klassenselektor):

ul ol li.red {} /* a=0 b=1 c=3, Spezifität 013 */
 
 
Bei der Ermittlung der Spezifität helfen Tools wie der CSS Specifity Calculator von Polypane (Bild 2). Dieser versteht auch neuere Selektoren wie :where(), :is() und :not() und behandelt sie korrekt.
Spezifitätsrechnerhelfen bei der Ermittlung der Spezifität(Bild 2) © Maurice
Hilfreich bei Spezifitätsfragen sind ebenfalls die Browserentwicklertools. Sie zeigen bei ausgewählten Elementen, welche Formatierung konkret gilt – eine Regel mit geringerer Spezifität ist durchgestrichen (Bild 3).
Welche Regelsich durchsetzt, zeigen Browser-Entwicklertools(Bild 3) © Maurice
Ein paar neuere Pseudoklassen, nämlich :where(), :is() und :not() verhalten sich bei der Spezifität anders als andere Pseudoklassen. Diese Tatsache verdient eine weitergehende Betrachtung.

Die Spezifität von :not()

Prinzipiell haben Pseudoklassen dieselbe Spezifität wie Klassenselektoren. Eine Ausnahme von dieser Regel ist :not(). Die Pseudoklasse :not() dient zur Verneinung. Sie wählen damit ein Element aus, auf das der in runden Klammern bei :not() angegebene Selektor nicht zutrifft:

:not(.foo) {}
 
 
Damit werden alle Elemente ausgewählt, die nicht die Klasse foo besitzen – darunter auch html, body etc. Sie können mehrere Selektorausdrücke mit Komma getrennt in runden Klammern angeben:

:not(.foo, .bar) {}
 
 
Prinzipiell trägt :not() nichts zur Spezifität bei, es zählt nur der Selektor in Klammern. Bei mehreren Selektoren legt der spezifischste Selektor die Spezifität fest.

:not(.foo, .bar, .xyz) {} /* Spezifität 010 */
:not(.foo, #bar) {} /* Spezifität 100 */
 
 
Bei mehreren übergebenen Klassenselektoren ist die Spezifität 010. Wenn ein Klassenselektor und ein id-Selektor übergeben werden, ist die Spezifität hingegen 100, weil die id als spezifischster Selektor entscheidend ist.Wie Sie an diesem Beispiel sehen, können Sie mehrere Selektoren in Klammern bei :not() angeben. Sollten Sie noch den Internet Explorer unterstützen wollen, müssen Sie stattdessen mehrere :not()-Ausdrücke kombinieren. Die beiden folgenden Angaben wählen dieselben Elemente aus:

:not(.foo, .bar) {}
:not(.foo):not(.bar) {}
 
 
Diese beiden Angaben unterscheiden sich aber wiederum in ihrer Spezifität:

:not(.foo):not(.bar) {} /* Spezifität 020 */
:not(.foo, .bar) {} /* Spezifität 010 */
 
 
:where() und :is() werden wie :not() in Level 4 der Selektor-Spezifikation definiert. Sie erlauben es, Selektoren kompakter zu notieren. Beim folgenden Code gelten Formatierungen für p-Elemente beim Hovern, wenn sie innerhalb von header, main oder footer stehen:

header p:hover,
main p:hover,
footer p:hover {
  color: red;
  cursor: pointer;
}
 
 
Diese Schreibweise ist jedoch umständlich, weil p:hover jedes Mal wiederholt werden muss. Mit :where() geht es kompakter. Der folgende Code bewirkt dasselbe wie der gerade gezeigte:

:where(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}
 
 
Statt :where() können Sie auch :is() benutzen:

:is(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}
 
 
Der Unterschied zwischen :where() und :is() liegt in der Spezifität. :is() ist so spezifisch wie der spezifischste Selektor, der als Argument übergeben wird – es verhält sich in dieser Hinsicht also wie :not(). Der folgende Selektorausdruck hat eine Spezifität von 002 (1 Mal zählt der Typselektor in Klammern und 1 Mal zählt das p):

:is(header, main) p {} /* Spezifität 002 */
 
 
Der nun folgende Ausdruck besitzt hingegen die Spezifität 101. Von den an :is() übergebenen Selektoren hat #id die höchste Spezifität:

:is(header, main, #id) p {} /* Spezifität 101 */
 
 
Anders verhält es sich bei :where(). Dies trägt – unabhängig von den übergebenen Argumenten – nichts zur Spezifität dazu. Im folgenden Beispiel wird nur p als Typselektor bei der Spezifität berücksichtigt:

:where(header, main, #id) p {} /* 001 */
 
 
Mojtaba Seyedi zeigt bei CSS Tricks eine praktische Einsatzmöglichkeit von :where(). Browser versehen Listen automatisch mit Aufzählungszeichen und Abständen. Wenn Sie dieses Verhalten elegant aushebeln wollen, falls ein Element eine Klasse besitzt, so erledigt das der folgende Ausdruck:

:where(ul[class]) {
  list-style: none;
}
 
 
Weil dieser Selektor dank :where() keine Spezifität hat, können Sie ihn jederzeit durch einen Klassenselektor überschreiben:

.list {
  list-style: square; 
}
 
 
Ein recht nützlicher Trick und da der Ursprung einer Deklaration (also ob sie vom Browser, vom Benutzer oder vom Autor stammt) wichtiger ist als die Spezifität, brauchen Sie sich nicht um die Spezifität von Ausdrücken zu kümmern, die die Browserstyles überschreiben sollen.id-Selektoren haben die höchste Spezifität. Damit setzen sie sich immer gegenüber Klassen- und Typselektoren durch. Angenommen, folgendes Element soll ausgewählt werden:

<p id="einmalig"> ...</p>
 
 
Dann besteht die erste Möglichkeit darin, das Element mit dem id-Selektor auszuwählen:

#einmalig { color: red; } /* Spezifität 100*/
 
 
Das ist aber eventuell problematisch aufgrund der hohen Spezifität des id-Selektors. Sie können jedoch stattdessen einen Attributselektor benutzen:

[id=einmalig] { color: red; } /* Spezifität 010*/
 
 
Auch dieser Code wählt ein Element mit einer id mit dem Wert einmalig aus, aber nun hat der Selektor nur noch die Spezifität eines Attributselektors. Um die Spezifität ganz auf 0 zu setzen, können Sie :where() verwenden:

:where(#einmalig) {} /* Spezifität 000 */
 
 
Umgekehrt kann es sinnvoll sein, die Spezifität zu erhöhen. Nehmen wir als Beispiel folgenden Selektor, der überschrieben werden soll:

.beispiel .wichtig {} /* Spezifität 020 */
 
 
Dann benötigen Sie zum Überschreiben einen Selektor, der mindestens aus zwei Klassen besteht. Was aber, wenn diese in einem bestimmten Fall nicht zur Verfügung stehen? Dann können Sie sich behelfen, indem Sie dieselbe Klasse zwei Mal angeben:

.wichtig.wichtig {} /* Spezifität 020 */
 
 
So sorgen Sie für die gewünschte Spezifität und können bei Bedarf die Klasse auch häufiger wiederholen. Die Pseudoklasse :not() eignet sich ebenfalls für Tricksereien mit der Spezifität. Die beiden folgenden Selektorausdrücke wählen dasselbe Element aus:

#foo:not(#bar) {} /* Spezifität 200 */
#foo {}  /* Spezifität 100 */
 
 
Damit wird beides Mal ein Element mit id=foo selektiert, aber der Ausdruck mit :not() zeichnet sich durch eine höhere Spezifität aus.

Methodologien zur Hilfe

Bei größeren Projekten oder wenn Komponenten aus verschiedenen Quellen stammen, kommt es immer wieder zu Problemen mit der Kaskade und insbesondere mit der Spezifität. Als Abhilfe wurden Methodologien wie BEM, ITCSS, SMACSS oder OOCSS entwickelt. Oder aber man benutzt gleich Atomic CSS (Utility-First CSS) (Bild 4).
Zufriedenheitmit einzelnen CSS-Methodologien gemäß https://2020.stateofcss.com/(Bild 4) © Maurice
Bei Atomic CSS werden durchgehend einfache Klassen verwendet, die jeweils nur einen Zweck erfüllen. Der Name der Klasse zeigt schon die Funktionalität. Typisch ist beispielsweise ein Klassenname wie bg-white für die Zuweisung einer weißen Hintergrundfarbe. Atomic CSS führt zu sehr vielen Klassenverwendungen, weshalb manche dann kritisieren, dass wichtige Grundprinzipien von CSS ignoriert würden.Eine sehr beliebte Methodologie ist BEM, wobei die Abkürzung für Block Element Modifier steht. BEM hilft Ihnen, Klassennamen konsistent zu wählen und wenn Sie durchgängig Klassen als Selektoren nutzen, haben Sie auch keine Spezifitätsprobleme. Ein Beispiel mit mehreren Buttons zeigt ein BEM-Grundprinzip:

<button class="button">Normaler Button</button>
<button class="button button--state-success">Erfolg
</button>
 
 
Der Standardbutton hat die Klasse button. Außerdem gibt es einen weiteren Button mit besonderen Formatierungen. Dieser wird in speziellen Situationen verwendet und zeigt einen eigenen Zustand, in der BEM-Sprache handelt es sich um einen Modifizierer (modifier). Für Zustände verwendet man zusätzliche Klassen, deren Namen sich aus dem ursprünglichen Namen, zwei Minus-Zeichen und dem Zustandsnamen zusammensetzen. Der besondere Button hat also zum einen die button-Klasse und zum anderen die button--state-success-Klasse.Darüber hinaus unterscheidet BEM zwischen Blöcken und Elementen. Blöcke bestehen üblicherweise aus mehreren Elementen. Im folgenden Beispiel haben wir ein Formular mit einem Textfeld und einem Absendebutton. Das Formular ist in der BEM-Methodologie ein Block; Textfeld und Absendebuttons sind hingegen Elemente, die zu diesem Block gehören. Das Formular erhält die Klasse form, die Elemente heißen form__input und form__submit. Das heißt, der Elementname wird gebildet aus block__element:

<form class="form">
  <input class="form__input" type="text">
  <input class="form__submit form__submit--disabled" 
  type="submit">
</form>
 
 
Im Beispiel oben sehen Sie außerdem noch, dass der Button einen besonderen Zustand (Modifier) hat, was zu der Klasse form__submit--disabled führt. Das ist das Grundprinzip der Benennung bei BEM: block__element--modifier.
Das umgekehrte Dreieckvon ITCSS(Bild 5) © xfive.co
ITCSS ist eine weitere beliebte Methodologie; sie wurde von Harry Roberts entwickelt. ITCSS steht für Inverted Triangle CSS (Bild 5) und ermöglicht es, CSS-Dateien zu strukturieren, um besser mit der Kaskade und der Spezifität umgehen zu können. Bei ITCSS wird der CSS-Code in verschiedene Bereiche unterteilt, die Layer genannt werden. Unter anderem sind folgende Layer vorgesehen:Der Name ITCSS steht ja für Inverted Triangle CSS. Die verschiedenen Layer bilden dieses Dreieck. Oben befinden sich die allgemeinen Formatierungen, über die unspezifisch recht viele HTML-Elemente angesprochen werden, unten stehen die speziellen Formatierungen für die gezielte Gestaltung einzelner Komponenten.Dieser Ansatz zeigt, in welche logischen Bereiche sich Stylesheets aufteilen lassen und hilft bei der Organisation großer Stylesheets. Bemerkenswert ist außerdem, dass dabei der Begriff layer auftaucht. Denn das ist der zentrale Begriff bei den Kaskade-Layer.

Layer für die Kaskade

Die Auswirkungen der Kaskade sind schwer zu händeln, wenn mehrere Personen am Stylesheet arbeiten und insbesondere, wenn die Formatierungen aus verschiedenen Quellen stammen. Dann kann es passieren, dass Angaben überschrieben werden, die nicht überschrieben werden sollen oder umgekehrt nicht wirken, obwohl sie es sollen. Abhilfe kommt durch die sogenannten CSS-Kaskade-Layer, ein neues Feature in CSS, das im Working Draft CSS Cascading and Inheritance Level 5 beschrieben wird. Es funktioniert derzeit in den Browsern hinter einem Flag:Dass diese drei Browser-Engines eine experimentelle Unterstützung für die Kaskade-Layer bereithalten, zeigt, dass die Browserhersteller die Bedeutung dieses Features erkannt haben. Das gibt Grund zu der Hoffnung, dass die Kaskade-Layer relativ rasch in den Browser ankommen, sobald die offenen Fragen geklärt und die Tests abgeschlossen sind.
CSS Cascade-Layerin Chrome aktivieren(Bild 6) © Maurice
Mit den neuen Kaskade-Layer können Sie eigene Schichten/Ebenen für CSS-Regeln erstellen, die sich benennen lassen. Welche Formatierung sich durchsetzt, entscheidet dabei nicht die Spezifität der einzelnen Angaben, sondern die Reihenfolge, in der die Layer deklariert werden. Die Angaben aus dem zuletzt deklarierten Layer überschreiben immer die aus den vorher deklarierten.Layer können Sie auf verschiedene Arten definieren: Bei der neu eingeführten @layer-Direktive können Sie direkt die gewünschten Formatierungen angeben. Das folgende Beispiel definiert einen Layer mit dem Namen reset und legt eine Formatierung fest:

@layer reset {
  audio[controls] {
    display: block;
  }
}
 
 
Bei Bedarf können Sie auch die @import-Direktive nutzen – was Sie aber normalerweise aus Performance-Gründen vermeiden sollten:

@import url(default.css) layer(default);
 
 
Sie können ebenfalls zuerst die Layer deklarieren und erst danach die Regeln zuweisen. Im folgenden Code werden drei Layer benannt und danach zwei Layern Regeln zugewiesen:

@layer default, theme, components;
@layer theme {
  body {
    background: yellow;
  }
}
@layer default {
  body {
    background: white;
    color: black;
  }
}
 
 
Die Reihenfolge, in der die Layer definiert werden, spielt hier die entscheidende Rolle. Im Beispiel steht in der ersten Zeile das Folgende:

@layer default, theme, components;
 
 
Damit setzen sich die Formatierungen aus dem Layer theme gegenüber denen aus default durch. Es gilt immer das, was zuletzt steht. Dass dann im weiteren Stylesheet zuerst die Regeln für theme stehen und dann erst die für default, ist hingegen irrelevant. Im oberen Beispiel hat body einen gelben Hintergrund. Ein weiteres kleines Beispiel:

@layer default, theme, components;
@layer theme {
  body .warning {
    background: red;
  }
}
@layer components {
  .warning {
    background: pink;
  }
}
 
 
In diesem Beispiel gibt es Formatierungen für den Layer theme und für den Layer components. Da der Layer components erst nach dem Layer theme definiert wird, setzt sich diese Formatierung durch. Dass im Beispiel der beim Layer theme angegebene Selektor body .warning eine höhere Spezifität hat als .warning innerhalb des Layers components, ist hingegen irrelevant.Neben den normalen Layern können Sie auch anonyme Layer erstellen, das heißt Layer ohne Namen. Zu diesen können allerdings im Nachhinein keine weiteren Regeln ergänzt werden. Neben den anonymen Layern gibt es auch implizite Layer. Diese beinhalten alle Regeln, die keinem Layer zugeordnet sind, und setzen sich gegenüber den expliziten Layern durch. Zudem können Layer verschachtelt werden.Das Entscheidende ist, dass zwischen den Layern die Spezifität keine Rolle mehr spielt, die Spezifität ist nur noch innerhalb der Layer relevant. Das macht es wesentlich einfacher, fremden CSS-Code zu integrieren. Wenn man ihn in einem eigenen Layer platziert, kann man durch die Reihenfolge der Layer-Deklarationen festlegen, was sich in Konfliktfällen durchsetzt und muss sich um die Spezifität keine Gedanken machen.

Fazit

Die Kaskade ist ein mächtiges und gleichzeitig komplexes Konstrukt in CSS. Ein Kernbereich der Kaskade ist die Spezifität. Unerwünschte Spezifitätsprobleme vermeidet man heute oft, indem man nach Möglichkeit auf Klassen setzt und dabei Methodologien wie BEM nutzt. Eine erweiterte Kontrolle über die CSS-Regeln und ihren Wirkungsbereich versprechen die Kaskade-Layer, die sich gerade für den Umgang mit Fremdcode als sehr nützlich erweisen können.

Neueste Beiträge

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Evolutionäres Prototyping von Business-Apps - Low Code/No Code und KI mit Power Apps
Microsoft baut Power Apps zunehmend mit Features aus, um die Low-Code-/No-Code-Welt mit der KI und der professionellen Programmierung zu verbinden.
19 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige