Leitfaden für Cascade Layers

Avatar of Miriam Suzanne
Miriam Suzanne am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Dies ist Ihr vollständiger Leitfaden zu CSS Cascade Layers, einer CSS-Funktion, die es uns ermöglicht, explizit abgegrenzte Ebenen der Spezifität zu definieren. So haben wir die volle Kontrolle darüber, welche Stile in einem Projekt Priorität haben, ohne auf Spezifitäts-Hacks oder !important angewiesen zu sein. Dieser Leitfaden soll Ihnen helfen, vollständig zu verstehen, wofür Cascade Layers gedacht sind, wie und warum Sie sie einsetzen könnten, wie der aktuelle Support aussieht und wie die Syntax für ihre Verwendung lautet.

Inhaltsverzeichnis

Kurzes Beispiel

/* establish a layer order up-front, from lowest to highest priority */
@layer reset, defaults, patterns, components, utilities, overrides;

/* import stylesheets into a layer (dot syntax represents nesting) */
@import url('framework.css') layer(components.framework);

/* add styles to layers */
@layer utilities {
  /* high layer priority, despite low specificity */
  [data-color='brand'] { 
    color: var(--brand, rebeccapurple);
  }
}

@layer defaults {
  /* higher specificity, but lower layer priority */
  a:any-link { color: maroon; }
}

/* un-layered styles have the highest priority */
a {
  color: mediumvioletred;
}

Einführung: Was sind Cascade Layers?

CSS Cascade Layers sind dazu gedacht, knifflige Probleme in CSS zu lösen. Werfen wir einen Blick auf das Hauptproblem und wie Cascade Layers dieses lösen wollen.

Problem: Spezifitätskonflikte eskalieren

Viele von uns waren schon in Situationen, in denen wir Stile von anderer Stelle in unserem Code (oder von einem Drittanbieter-Tool) aufgrund kollidierender Selektoren überschreiben wollten. Im Laufe der Jahre haben Autoren eine Reihe von „Methoden“ und „Best Practices“ entwickelt, um diese Situationen zu vermeiden – wie zum Beispiel „nur eine einzige Klasse“ für alle Selektoren zu verwenden. Diese Regeln dienen meist eher dazu, die Kaskade zu vermeiden, anstatt sie gezielt einzusetzen.

Die Verwaltung von Kaskadenkonflikten und Selektorspezifität wurde oft als einer der schwierigeren – oder zumindest verwirrenderen – Aspekte von CSS angesehen. Das mag zum Teil daran liegen, dass nur wenige andere Sprachen eine Kaskade als zentrales Merkmal nutzen. Es liegt aber auch daran, dass die ursprüngliche Kaskade stark auf Heuristiken basiert (eine Art begründete Vermutung oder Annahme, die in den Code eingebaut ist), anstatt Webautoren eine direkte und explizite Kontrolle zu ermöglichen.

Selektorspezifität zum Beispiel – unsere primäre Interaktion mit der Kaskade – basiert auf der Annahme, dass enger gefasste Stile (wie IDs, die nur einmal verwendet werden) wahrscheinlich wichtiger sind als generische und wiederverwendbare Stile (wie Klassen und Attribute). Das heißt: wie spezifisch der Selektor ist. Das ist eine gute Vermutung, aber keine absolut zuverlässige Regel, was zu Problemen führt.

  • Es kombiniert den Akt der Auswahl von Elementen mit dem Akt der Priorisierung von Regelsätzen.
  • Der einfachste Weg, einen Konflikt bei der Spezifität zu „lösen“, besteht darin, das Problem zu eskalieren, indem man ansonsten unnötige Selektoren hinzufügt oder (schluck) die !important-Handgranate wirft.
.overly#powerful .framework.widget {
  color: maroon;
}

.my-single_class { /* add some IDs to this ??? */
  color: rebeccapurple; /* add !important ??? */
}

Lösung: Cascade Layers bieten Kontrolle

Cascade Layers geben CSS-Autoren eine direktere Kontrolle über die Kaskade, sodass wir bewusst kaskadierende Systeme aufbauen können, ohne uns so sehr auf heuristische Annahmen verlassen zu müssen, die an die Auswahl gekoppelt sind.

Mit der @layer-Regel und gelayerten @import-Anweisungen können wir unsere eigenen Ebenen der Kaskade festlegen – angefangen bei Stilen mit niedriger Priorität wie Resets und Standardwerten, über Themes, Frameworks und Design-Systeme bis hin zu Stilen mit höchster Priorität wie Komponenten, Utilities und Overrides. Innerhalb jeder Ebene wird die Spezifität weiterhin auf Konflikte angewendet, aber Konflikte zwischen Ebenen werden immer zugunsten der Stile aus der Ebene mit der höheren Priorität gelöst.

@layer framework {
  .overly#powerful .framework.widget {
    color: maroon;
  }
}

@layer site {
  .my-single_class {
    color: rebeccapurple;
  }
}

Diese Ebenen sind so geordnet und gruppiert, dass sie nicht in der gleichen Weise eskalieren wie Spezifität und Wichtigkeit. Cascade Layers sind nicht kumulativ wie Selektoren. Das Hinzufügen von mehr Ebenen macht etwas nicht wichtiger. Sie sind auch nicht binär wie „Wichtigkeit“ – was plötzlich an die Spitze eines Stapels springt – oder nummeriert wie der z-index, bei dem wir eine riesige Zahl raten müssen (z-index: 9999999?). Tatsächlich sind gelayerte Stile standardmäßig weniger wichtig als ungelayerte Stile.

@layer defaults {
  a:any-link { color: maroon; }
}

/* un-layered styles have the highest priority */
a {
  color: mediumvioletred;
}

Wo ordnen sich Ebenen in der Kaskade ein?

Die Kaskade ist eine Abfolge von Schritten (ein Algorithmus) zur Lösung von Konflikten zwischen Stilen.

html { --button: teal; }
button { background: rebeccapurple !important; }
.warning { background: maroon; }
<button class="warning" style="background: var(--button);">
  what color background?
</button>

Mit der Einführung von Cascade Layers sind diese Schritte wie folgt:

Illustration of the various specificity levels of the CSS cascade and where CSS Cascade Layers fit in it.

Die Selektorspezifität ist nur ein kleiner Teil der Kaskade, aber es ist auch der Schritt, mit dem wir am meisten interagieren, und er wird oft verwendet, um allgemeiner auf die gesamte Kaskadenpriorität zu verweisen. Man sagt vielleicht, dass das !important-Flag oder das style-Attribut „Spezifität hinzufügt“ – eine Kurzform, um auszudrücken, dass der Stil in der Kaskade eine höhere Priorität erhält. Da Cascade Layers direkt oberhalb der Spezifität hinzugefügt wurden, ist es sinnvoll, sie auf ähnliche Weise zu betrachten: eine Stufe mächtiger als ID-Selektoren.

CSS Cascade Layers machen es jedoch auch unumgänglich, die Rolle von !important in der Kaskade vollständig zu verstehen – nicht nur als Werkzeug zur „Erhöhung der Spezifität“, sondern als System zum Ausgleich verschiedener Belange.

!important-Ursprünge, Kontext und Ebenen sind umgekehrt!

Als Webautoren betrachten wir !important oft als eine Möglichkeit, die Spezifität zu erhöhen, um Inline-Stile oder hochspezifische Selektoren zu überschreiben. Das funktioniert in den meisten Fällen gut (wenn man mit der Eskalation einverstanden ist), lässt aber den Hauptzweck der Wichtigkeit (Importance) als Merkmal in der Gesamtkaskade außer Acht.

„Importance“ ist nicht dazu da, einfach nur die Macht zu erhöhen – sondern um die Macht zwischen verschiedenen konkurrierenden Belangen auszubalancieren.

Wichtige Ursprünge

Alles beginnt mit den Ursprüngen (Origins), also der Herkunft eines Stils im Web-Ökosystem. In CSS gibt es drei grundlegende Ursprünge:

  • Der Browser (oder User Agent)
  • Der Benutzer (oft über Browser-Einstellungen)
  • Web-Autoren (das sind wir!)

Browser bieten lesbare Standardwerte für alle Elemente, Benutzer legen ihre Präferenzen fest, und wir (Autoren) liefern das beabsichtigte Design für unsere Webseiten. Standardmäßig haben Browser also die niedrigste Priorität, Benutzereinstellungen überschreiben die Browser-Standards, und wir können alles überschreiben.

Aber die Schöpfer von CSS waren sich sehr einig, dass wir eigentlich nicht das letzte Wort haben sollten:

Wenn Konflikte auftreten, sollte der Benutzer das letzte Wort haben, aber man sollte dem Autor auch erlauben, Stil-Hinweise anzubringen.

— Håkon Lie (Hervorhebung hinzugefügt)

Daher bietet Importance dem Browser und den Benutzern eine Möglichkeit, ihre Priorität zurückzufordern, wenn es am wichtigsten ist. Wenn ein Stil mit dem !important-Flag versehen wird, werden drei neue Ebenen erstellt – und die Reihenfolge wird umgekehrt!

  1. !important Browser-Stile (am mächtigsten)
  2. !important Benutzer-Präferenzen
  3. !important Autoren-Stile
  4. normale Autoren-Stile
  5. normale Benutzer-Präferenzen
  6. normale Browser-Stile (am wenigsten mächtig)

Für uns ändert das Hinzufügen von !important nicht viel – unsere wichtigen Stile liegen ziemlich nah an unseren normalen Stilen – aber für den Browser und den Benutzer ist es ein sehr mächtiges Werkzeug, um die Kontrolle zurückzugewinnen. Browser-Standard-Stylesheets enthalten eine Reihe wichtiger Stile, die wir unmöglich überschreiben können, wie zum Beispiel:

iframe:fullscreen {
  /* iframes in full-screen mode don't show a border. */
  border: none !important;
  padding: unset !important;
}

Während die meisten gängigen Browser das Hochladen tatsächlicher Benutzer-Stylesheets erschwert haben, bieten sie alle Benutzer-Präferenzen an: eine grafische Oberfläche zum Festlegen spezifischer Benutzerstile. In dieser Oberfläche gibt es immer ein Kontrollkästchen, mit dem Benutzer wählen können, ob eine Website ihre Präferenzen überschreiben darf oder nicht. Dies entspricht dem Setzen von !important in einem Benutzer-Stylesheet.

Screenshot of user font preferences.

Wichtiger Kontext

Dieselbe Grundlogik wird auf den Kontext in der Kaskade angewendet. Standardmäßig überschreiben Stile aus dem Host-Dokument (Light DOM) Stile aus einem eingebetteten Kontext (Shadow DOM). Das Hinzufügen von !important kehrt die Reihenfolge jedoch um:

  1. !important Shadow-Kontext (am mächtigsten)
  2. !important Host-Kontext
  3. normaler Host-Kontext
  4. normaler Shadow-Kontext (am wenigsten mächtig)

Wichtige Stile, die aus einem Shadow-Kontext stammen, überschreiben wichtige Stile, die im Host-Dokument definiert sind. Hier ist ein odd-bird Custom Element mit einigen Stilen im Element-Template (Shadow DOM) und einigen Stilen im Stylesheet der Host-Seite (Light DOM):

Beide color-Deklarationen haben eine normale Wichtigkeit, daher hat die Host-Seite mit mediumvioletred Vorrang. Aber die font-family-Deklarationen sind als !important markiert, was dem Shadow-Kontext den Vorteil gibt, in dem fantasy definiert ist.

Wichtige Ebenen

Cascade Layers funktionieren genauso wie Ursprünge und Kontexte, wobei die wichtigen Ebenen in umgekehrter Reihenfolge vorliegen. Der einzige Unterschied besteht darin, dass Ebenen dieses Verhalten viel deutlicher machen.

Sobald wir anfangen, Cascade Layers zu verwenden, müssen wir viel vorsichtiger und bewusster damit umgehen, wie wir !important einsetzen. Es ist nicht länger ein schneller Weg, um an die Spitze der Prioritäten zu springen, sondern ein integrierter Teil unserer Kaskadenschichtung; eine Möglichkeit für niedrigere Ebenen, darauf zu bestehen, dass einige ihrer Stile essenziell sind.

Da Cascade Layers anpassbar sind, gibt es keine vordefinierte Reihenfolge. Aber wir können uns vorstellen, mit drei Ebenen zu beginnen:

  1. Utilities (am mächtigsten)
  2. Komponenten
  3. Standardwerte (am wenigsten mächtig)

Wenn Stile in diesen Ebenen als wichtig markiert werden, würden sie drei neue, umgekehrte wichtige Ebenen erzeugen:

  1. !important Standardwerte (am mächtigsten)
  2. !important Komponenten
  3. !important Utilities
  4. normale Utilities
  5. normale Komponenten
  6. normale Standardwerte (am wenigsten mächtig)

In diesem Beispiel wird die Farbe durch alle drei normalen Ebenen definiert, und die utilities-Ebene gewinnt den Konflikt und wendet die Farbe maroon an, da die utilities-Ebene eine höhere Priorität in den @layers hat. Beachten Sie jedoch, dass die Eigenschaft text-decoration sowohl in der Ebene defaults als auch in components als !important markiert ist, wobei wichtige defaults Vorrang haben und das von defaults deklarierte Unterstreichen anwenden.

Festlegen einer Ebenen-Reihenfolge

Wir können eine beliebige Anzahl von Ebenen erstellen und sie auf verschiedene Weise benennen oder gruppieren. Das Wichtigste ist jedoch, sicherzustellen, dass unsere Ebenen in der richtigen Prioritätsreihenfolge angewendet werden.

Eine einzelne Ebene kann mehrfach in der Codebasis verwendet werden – Cascade Layers stapeln sich in der Reihenfolge ihres ersten Erscheinens. Die zuerst gefundene Ebene befindet sich ganz unten (am wenigsten mächtig), die letzte Ebene ganz oben (am mächtigsten). Darüber haben jedoch ungelayerte Stile die höchste Priorität:

@layer layer-1 { a { color: red; } }
@layer layer-2 { a { color: orange; } }
@layer layer-3 { a { color: yellow; } }
/* un-layered */ a { color: green; }
  1. ungelayerte Stile (am mächtigsten)
  2. Ebene-3
  3. Ebene-2
  4. Ebene-1 (am wenigsten mächtig)

Wie oben besprochen, werden dann alle wichtigen Stile in umgekehrter Reihenfolge angewendet:

@layer layer-1 { a { color: red !important; } }
@layer layer-2 { a { color: orange !important; } }
@layer layer-3 { a { color: yellow !important; } }
/* un-layered */ a { color: green !important; }
  1. !important Ebene-1 (am mächtigsten)
  2. !important Ebene-2
  3. !important Ebene-3
  4. !important ungelayerte Stile
  5. normale ungelayerte Stile
  6. normale Ebene-3
  7. normale Ebene-2
  8. normale Ebene-1 (am wenigsten mächtig)

Ebenen können auch gruppiert werden, was eine komplexere Sortierung von übergeordneten und verschachtelten Ebenen ermöglicht:

@layer layer-1 { a { color: red; } }
@layer layer-2 { a { color: orange; } }
@layer layer-3 {
  @layer sub-layer-1 { a { color: yellow; } }
  @layer sub-layer-2 { a { color: green; } }
  /* un-nested */ a { color: blue; }
}
/* un-layered */ a { color: indigo; }
  1. ungelayerte Stile (am mächtigsten)
  2. Ebene-3
    1. Ebene-3 unverschachtelt
    2. Ebene-3 Unterebene-2
    3. Ebene-3 Unterebene-1
  3. Ebene-2
  4. Ebene-1 (am wenigsten mächtig)

Gruppierte Ebenen bleiben in der endgültigen Ebenenreihenfolge immer zusammen (beispielsweise liegen alle Unterebenen von Ebene-3 nebeneinander), verhalten sich ansonsten aber so, als wäre die Liste „flach“ – was dies in eine einzige Liste mit sechs Elementen verwandelt. Wenn die !important-Reihenfolge umgekehrt wird, wird die gesamte flache Liste umgekehrt.

Ebenen müssen jedoch nicht nur einmal an einer einzigen Stelle definiert werden. Wir geben ihnen Namen, damit Ebenen an einer Stelle definiert werden können (um die Ebenenreihenfolge festzulegen), und wir ihnen dann von überall her Stile hinzufügen können:

/* describe the layer in one place */
@layer my-layer;

/* append styles to it from anywhere */
@layer my-layer { a { color: red; } }

Wir können sogar eine ganze geordnete Liste von Ebenen in einer einzigen Deklaration definieren:

@layer one, two, three, four, five, etc;

Dies ermöglicht es dem Autor einer Website, das letzte Wort über die Ebenenreihenfolge zu haben. Indem eine Ebenenreihenfolge vorab festgelegt wird, bevor Drittanbieter-Code importiert wird, kann die Reihenfolge an einer zentralen Stelle etabliert und neu angeordnet werden, ohne sich Gedanken darüber machen zu müssen, wie Ebenen in einem Drittanbieter-Tool verwendet werden.

Syntax: Arbeiten mit Cascade Layers

Schauen wir uns die Syntax an!

Ordnungssetzende @layer-Anweisungen

Da Ebenen in der Reihenfolge ihrer Definition gestapelt werden, ist es wichtig, dass wir ein Werkzeug haben, um diese Reihenfolge an einer einzigen Stelle festzulegen!

Dazu können wir @layer-Anweisungen verwenden. Die Syntax lautet:

@layer <layer-name>#;

Das Hash-Zeichen (#) bedeutet, dass wir beliebig viele Ebenennamen in einer durch Kommas getrennten Liste hinzufügen können:

@layer reset, defaults, framework, components, utilities;

Das legt die Ebenenreihenfolge fest:

  1. ungelayerte Stile (am mächtigsten)
  2. Utilities
  3. Komponenten
  4. Framework
  5. Standardwerte
  6. Reset (am wenigsten mächtig)

Wir können dies so oft tun, wie wir wollen, aber denken Sie daran: Es zählt die Reihenfolge, in der jeder Name zuerst erscheint. Das Folgende hat also dasselbe Ergebnis:

@layer reset, defaults, framework;
@layer components, defaults, framework, reset, utilities;

Die Sortierlogik ignoriert die Reihenfolge von reset, defaults und framework in der zweiten @layer-Regel, da diese Ebenen bereits etabliert wurden. Diese @layer-Listensyntax fügt der Ebenensortierlogik keine besondere Magie hinzu: Ebenen werden basierend auf der Reihenfolge gestapelt, in der sie zuerst in Ihrem Code erscheinen. In diesem Fall erscheint reset zuerst in der ersten @layer-Liste. Jede später folgende @layer-Anweisung kann nur Ebenennamen an die Liste anhängen, aber bereits existierende Ebenen nicht verschieben. Dies stellt sicher, dass Sie die endgültige Gesamtreihenfolge der Ebenen immer von einer Stelle aus kontrollieren können – ganz am Anfang Ihrer Styles.

Diese Anweisungen zur Ebenensortierung sind am Anfang eines Stylesheets erlaubt, vor der @import-Regel (aber nicht zwischen Imports). Wir empfehlen dringend, diese Funktion zu nutzen, um alle Ebenen vorab an einer einzigen Stelle festzulegen, damit Sie immer wissen, wo Sie suchen oder Änderungen vornehmen müssen.

Block-@layer-Regeln

Die Block-Version der @layer-Regel akzeptiert nur einen einzigen Ebenennamen, ermöglicht es Ihnen dann aber, dieser Ebene Stile hinzuzufügen:

@layer <layer-name> {
  /* styles added to the layer */
}

Sie können fast alles in einen @layer-Block packen – Media Queries, Selektoren und Stile, Support-Abfragen usw. Die einzigen Dinge, die Sie nicht in einen Ebenen-Block schreiben können, sind Charset, Imports und Namespaces. Aber keine Sorge, es gibt eine Syntax, um Stile in eine Ebene zu importieren.

Falls der Ebenenname zuvor noch nicht festgelegt wurde, fügt diese Ebenenregel ihn der Ebenenreihenfolge hinzu. Falls der Name jedoch bereits etabliert ist, ermöglicht dies das Hinzufügen von Stilen zu bestehenden Ebenen von überall im Dokument aus – ohne die Priorität der jeweiligen Ebene zu verändern.

Wenn wir unsere Ebenenreihenfolge vorab mit der Ebenen-Anweisung festgelegt haben, müssen wir uns keine Gedanken mehr über die Reihenfolge dieser Ebenen-Blöcke machen:

/* establish the order up-front */
@layer defaults, components, utilities;

/* add styles to layers in any order */
@layer utilities {
  [hidden] { display: none; }
}

/* utilities will override defaults, based on established order */
@layer defaults {
  * { box-sizing: border-box; }
  img { display: block; }
}

Gruppieren (verschachteln) von Ebenen

Ebenen können gruppiert werden, indem man Ebenen-Regeln verschachtelt:

@layer one {
  /* sorting the sub-layers */
  @layer two, three;

  /* styles ... */
  @layer three { /* styles ... */ }
  @layer two { /* styles ... */ }
}

Dies erzeugt gruppierte Ebenen, die dargestellt werden können, indem man den Namen der Eltern- und Kind-Ebene mit einem Punkt verbindet. Das bedeutet, dass auf die resultierenden Unterebenen auch direkt von außerhalb der Gruppe zugegriffen werden kann:

/* sorting nested layers directly */
@layer one.two, one.three;

/* adding to nested layers directly */
@layer one.three { /* ... */ }
@layer one.two { /* ... */ }

Die Regeln der Ebenensortierung gelten auf jeder Ebene der Verschachtelung. Alle Stile, die nicht weiter verschachtelt sind, gelten in diesem Kontext als „ungelayert“ und haben Vorrang vor weiter verschachtelten Stilen:

@layer defaults {
  /* un-layered defaults (higher priority) */
  :any-link { color: rebeccapurple; }

  /* layered defaults (lower priority) */
  @layer reset {
    a[href] { color: blue; }
  }
}

Gruppierte Ebenen sind zudem in ihrer Elternebene eingeschlossen, sodass sich die Ebenenreihenfolge nicht gruppenübergreifend vermischt. In diesem Beispiel werden zuerst die obersten Ebenen sortiert und dann die Ebenen innerhalb jeder Gruppe:

@layer reset.type, default.type, reset.media, default.media;

Daraus ergibt sich folgende Ebenenreihenfolge:

  • ungelayert (am mächtigsten)
  • Default-Gruppe
    • Default ungelayert
    • default.media
    • default.type
  • Reset-Gruppe
    • Reset ungelayert
    • reset.media
    • reset.type

Beachten Sie, dass Ebenennamen auch so isoliert sind, dass sie nicht mit gleichnamigen Ebenen außerhalb ihres verschachtelten Kontexts interagieren oder kollidieren. Beide Gruppen können eigenständige media-Unterebenen haben.

Diese Gruppierung wird besonders wichtig, wenn @import oder <link> verwendet werden, um ganze Stylesheets zu schichten. Ein Drittanbieter-Tool wie Bootstrap könnte intern Ebenen verwenden – aber wir können diese Ebenen beim Import in eine gemeinsame bootstrap-Ebenengruppe verschachteln, um potenzielle Namenskonflikte bei den Ebenen zu vermeiden.

Ganze Stylesheets können mit der neuen layer()-Funktionssyntax in @import-Regeln einer Ebene hinzugefügt werden:

/* styles imported into to the <layer-name> layer */
@import url('example.css') layer(<layer-name>);

Es gibt auch einen Vorschlag, ein layer-Attribut im HTML-Element <link> hinzuzufügen – obwohl dies noch in der Entwicklung ist und bisher nirgends unterstützt wird. Dies kann verwendet werden, um Drittanbieter-Tools oder Komponentenbibliotheken zu importieren, während alle internen Ebenen unter einem einzigen Ebenennamen zusammengefasst werden – oder als Möglichkeit, Ebenen in separaten Dateien zu organisieren.

Anonyme (unbenannte) Ebenen

Ebenennamen sind hilfreich, da sie uns ermöglichen, von mehreren Stellen aus auf dieselbe Ebene zuzugreifen, um Ebenenblöcke zu sortieren oder zu kombinieren – sie sind jedoch nicht erforderlich.

Es ist möglich, anonyme (unbenannte) Ebenen mit der Block-Ebenenregel zu erstellen:

@layer { /* ... */ }
@layer { /* ... */ }

Oder unter Verwendung der Import-Syntax mit dem Schlüsselwort layer anstelle der Funktion layer():

/* styles imported into to a new anonymous layer */
@import url('../example.css') layer;

Jede anonyme Ebene ist einzigartig und wird dort der Ebenenreihenfolge hinzugefügt, wo sie angetroffen wird. Auf anonyme Ebenen kann von anderen Ebenenregeln aus nicht verwiesen werden, um Stile zu sortieren oder weitere Stile anzuhängen.

Diese sollten vermutlich sparsam eingesetzt werden, aber es gibt einige Anwendungsfälle:

  • Projekte könnten sicherstellen, dass alle Stile für eine bestimmte Ebene an einer einzigen Stelle platziert werden müssen.
  • Drittanbieter-Tools könnten ihre interne Schichtung in anonymen Ebenen „verstecken“, damit sie nicht Teil der öffentlichen API des Tools werden.

Werte auf die vorherige Ebene zurücksetzen

Es gibt verschiedene Möglichkeiten, einen Stil in der Kaskade auf einen vorherigen Wert „zurückzusetzen“ (revert), der durch einen Ursprung oder eine Ebene mit niedrigerer Priorität definiert wurde. Dazu gehören eine Reihe bestehender globaler CSS-Werte und ein neues Schlüsselwort revert-layer, das ebenfalls global sein wird (auf jede Eigenschaft anwendbar).

Kontext: Vorhandene globale Kaskaden-Schlüsselwörter*

CSS verfügt über mehrere globale Schlüsselwörter, die für jede Eigenschaft verwendet werden können, um die Kaskade auf verschiedene Weise zurückzuspulen.

  • initial setzt eine Eigenschaft auf den spezifizierten Wert zurück, bevor Stile (einschließlich Browser-Standards) angewendet werden. Das kann überraschen, da wir Browser-Stile oft als Anfangswert betrachten – aber zum Beispiel ist der initial-Wert von display immer inline, egal auf welches Element wir ihn anwenden.
  • inherit bewirkt, dass die Eigenschaft den Wert von ihrem Elternelement übernimmt. Dies ist der Standard für vererbte Eigenschaften, kann aber dennoch verwendet werden, um einen vorherigen Wert zu entfernen.
  • unset wirkt so, als würden einfach alle vorherigen Werte entfernt – sodass vererbte Eigenschaften wieder inherit anwenden, während nicht vererbte Eigenschaften auf ihren initial-Wert zurückfallen.
  • revert entfernt nur Werte, die wir im Autoren-Ursprung (d. h. in den Website-Stilen) angewendet haben. Dies ist in den meisten Fällen das Gewünschte, da Browser- und Benutzerstile dadurch intakt bleiben.
Neu: das Schlüsselwort revert-layer

Cascade Layers fügen das neue globale Schlüsselwort revert-layer hinzu. Es funktioniert genauso wie revert, entfernt aber nur Werte, die wir in der aktuellen Kaskadenebene angewendet haben. Wir können dies verwenden, um die Kaskade zurückzuspulen und den Wert zu nutzen, der in den vorherigen Ebenen definiert wurde.

In diesem Beispiel entfernt die Klasse no-theme alle in der theme-Ebene gesetzten Werte.

@layer default {
  a { color: maroon; }
}

@layer theme {
  a { color: var(--brand-primary, purple); }

  .no-theme {
    color: revert-layer;
  }
}

Ein Link-Tag mit der Klasse .no-theme wird also zurückgesetzt, um den in der Ebene default gesetzten Wert zu verwenden. Wenn revert-layer in ungelayerten Stilen verwendet wird, verhält es sich wie revert – es wird auf den vorherigen Ursprung zurückgesetzt.

Zurücksetzen wichtiger Ebenen

Interessant wird es, wenn wir !important zum Schlüsselwort revert-layer hinzufügen. Da jede Ebene zwei unterschiedliche „normale“ und „wichtige“ Positionen in der Kaskade hat, ändert dies nicht einfach die Priorität der Deklaration – es ändert, welche Ebenen zurückgesetzt werden.

Nehmen wir an, wir haben drei Ebenen definiert, in einem Ebenenstapel, der so aussieht:

  1. Utilities (am mächtigsten)
  2. Komponenten
  3. Standardwerte (am wenigsten mächtig)

Wir können das konkretisieren, um nicht nur normale und wichtige Positionen jeder Ebene einzubeziehen, sondern auch ungelayerte Stile und Animationen:

  1. !important Standardwerte (am mächtigsten)
  2. !important Komponenten
  3. !important Utilities
  4. !important ungelayerte Stile
  5. CSS-Animationen
  6. normale ungelayerte Stile
  7. normale Utilities
  8. normale Komponenten
  9. normale Standardwerte (am wenigsten mächtig)

Wenn wir nun revert-layer in einer normalen Ebene verwenden (nehmen wir utilities), ist das Ergebnis recht direkt. Wir setzen nur diese Ebene zurück, während alles andere normal angewendet wird:

  1. !important Standardwerte (am mächtigsten)
  2. !important Komponenten
  3. !important Utilities
  4. !important ungelayerte Stile
  5. ✅ CSS-Animationen
  6. ✅ normale, nicht-geschichtete Stile
  7. ❌ normale Utilities
  8. ✅ normale Komponenten
  9. ✅ normale Standardwerte (am schwächsten)

Aber wenn wir dieses revert-layer in die wichtige Position verschieben, setzen wir sowohl die normalen als auch die wichtigen Versionen zurück, zusammen mit allem, was dazwischen liegt

  1. !important Standardwerte (am mächtigsten)
  2. !important Komponenten
  3. !important Utilities
  4. !important nicht-geschichtete Stile
  5. ❌ CSS-Animationen
  6. ❌ normale nicht-geschichtete Stile
  7. ❌ normale Utilities
  8. ✅ normale Komponenten
  9. ✅ normale Standardwerte (am schwächsten)

Anwendungsfälle: Wann sollte ich Kaskaden-Ebenen verwenden?

In welchen Situationen könnten wir Kaskaden-Ebenen (Cascade Layers) einsetzen? Hier sind mehrere Beispiele, in denen Kaskaden-Ebenen sehr sinnvoll sind, sowie andere, in denen sie weniger sinnvoll sind.

Weniger intrusive Resets und Standardwerte

Einer der klarsten ersten Anwendungsfälle ist die Erstellung von Standardwerten mit niedriger Priorität, die leicht zu überschreiben sind.

Einige Resets tun dies bereits, indem sie die Pseudoklasse :where() um jeden Selektor anwenden. :where() entfernt jegliche Spezifität von den Selektoren, auf die es angewendet wird, was zwar den gewünschten Effekt erzielt, aber auch einige Nachteile hat.

  • Es muss auf jeden Selektor einzeln angewendet werden
  • Konflikte innerhalb des Resets müssen ohne Spezifität gelöst werden

Ebenen erlauben es uns, einfacher das gesamte Reset-Stylesheet zu umschließen, entweder mit der Block-Regel @layer

/* reset.css */
@layer reset {
  /* all reset styles in here */
}

Oder wenn Sie den Reset importieren

/* reset.css */
@import url(reset.css) layer(reset);

Oder beides! Ebenen können verschachtelt werden, ohne ihre Priorität zu ändern. Auf diese Weise können Sie einen Drittanbieter-Reset verwenden und sicherstellen, dass er der gewünschten Ebene hinzugefügt wird, unabhängig davon, ob das Reset-Stylesheet selbst intern mit Ebenen geschrieben wurde.

Da geschichtete Stile eine niedrigere Priorität haben als standardmäßige „ungeschichtete“ Stile, ist dies eine gute Möglichkeit, mit der Verwendung von Kaskaden-Ebenen zu beginnen, ohne Ihre gesamte CSS-Codebasis neu schreiben zu müssen.

Die Reset-Selektoren behalten weiterhin ihre Spezifitätsinformationen, um interne Konflikte zu lösen, ohne jeden einzelnen Selektor umschließen zu müssen – aber Sie erhalten dennoch das gewünschte Ergebnis eines Reset-Stylesheets, das leicht zu überschreiben ist.

Verwaltung einer komplexen CSS-Architektur

Wenn Projekte größer und komplexer werden, kann es nützlich sein, klarere Grenzen für die Benennung und Organisation von CSS-Code zu definieren. Aber je mehr CSS wir haben, desto mehr Potenzial für Konflikte besteht – insbesondere durch verschiedene Teile eines Systems wie ein „Theme“, eine „Komponentenbibliothek“ oder einen Satz von „Utility-Klassen“.

Wir wollen diese nicht nur nach Funktion organisieren, sondern es kann auch nützlich sein, sie basierend darauf zu ordnen, welche Teile des Systems im Falle eines Konflikts Priorität haben. Harry Roberts' Inverted Triangle CSS (ITCSS) visualisiert sehr gut, was diese Ebenen enthalten könnten.

Tatsächlich nutzte der ursprüngliche Vorschlag zur Einführung von Ebenen in die CSS-Kaskade die ITCSS-Methodik als primäres Beispiel und als Leitfaden für die Entwicklung des Features.

Es ist keine spezielle Technik dafür erforderlich, aber es ist wahrscheinlich hilfreich, Projekte auf einen vordefinierten Satz von Top-Level-Ebenen zu beschränken und diesen Satz dann bei Bedarf durch verschachtelte Ebenen zu erweitern.

Zum Beispiel:

  1. Low-Level-Reset und Normalisierungsstile
  2. Element-Standards für grundlegende Typografie und Lesbarkeit
  3. Themes, wie Hell- und Dunkelmodus
  4. Wiederverwendbare Muster, die in mehreren Komponenten vorkommen könnten
  5. Layouts und größere Seitenstrukturen
  6. Einzelne Komponenten
  7. Overrides und Utilities

Wir können diesen Top-Level-Layer-Stack ganz am Anfang unseres CSS mit einer einzigen Layer-Anweisung erstellen

@layer
  reset,
  default,
  themes,
  patterns,
  layouts,
  components,
  utilities;

Die genau benötigten Ebenen und deren Benennung können sich von Projekt zu Projekt unterscheiden.

Von dort aus erstellen wir noch detailliertere Ebenen-Aufteilungen. Vielleicht haben unsere Komponenten intern selbst Standardwerte, Strukturen, Themes und Utilities.

@layer components {
  @layer defaults, structures, themes, utilities;
}

Ohne die Top-Level-Struktur zu ändern, haben wir nun eine Möglichkeit, die Stile innerhalb jeder Komponente weiter zu schichten.

Verwendung von Drittanbieter-Tools und Frameworks

Die Integration von Drittanbieter-CSS in ein Projekt ist einer der häufigsten Orte für Kaskaden-Probleme. Ob wir einen gemeinsamen Reset wie Normalizer oder CSS Remedy, ein generisches Designsystem wie Material Design, ein Framework wie Bootstrap oder ein Utility-Toolkit wie Tailwind verwenden – wir können nicht immer die Selektorspezifität oder die Wichtigkeit des gesamten verwendeten CSS kontrollieren. Manchmal erstreckt sich dies sogar auf interne Bibliotheken, Designsysteme und Tools, die an anderer Stelle in einer Organisation verwaltet werden.

Infolgedessen müssen wir oft unser internes CSS um den Drittanbieter-Code herum strukturieren oder Konflikte eskalieren, wenn sie auftreten – mit künstlich hoher Spezifität oder !important-Flags. Und dann müssen wir diese Hacks über die Zeit pflegen und an Änderungen der Upstream-Quelle anpassen.

Kaskaden-Ebenen geben uns die Möglichkeit, Drittanbieter-Code genau dort in die Kaskade eines Projekts einzufügen, wo wir ihn haben wollen – egal wie die Selektoren intern geschrieben sind. Je nach Art der Bibliothek, die wir verwenden, können wir das auf verschiedene Weise tun. Beginnen wir mit einem einfachen Ebenen-Stack, der sich von Resets bis zu Utilities hocharbeitet.

@layer reset, type, theme, components, utilities;

Und dann können wir einige Tools integrieren…

Einen Reset verwenden

Wenn wir ein Tool wie CSS Remedy verwenden, haben wir möglicherweise auch eigene Reset-Stile, die wir einbinden möchten. Importieren wir CSS Remedy in eine Unterebene von reset

@import url('remedy.css') layer(reset.remedy);

Jetzt können wir unsere eigenen Reset-Stile der Ebene reset hinzufügen, ohne weitere Verschachtelung (außer wir wünschen es). Da Stile direkt in reset alle tiefer verschachtelten Stile überschreiben, können wir sicher sein, dass unsere Stile bei einem Konflikt immer Vorrang vor CSS Remedy haben – egal was sich in einer neuen Version ändert.

@import url('remedy.css') layer(reset.remedy);

@layer reset {
  :is(ol, ul)[role='list'] {
    list-style: none;
    padding-inline-start: 0;
  }
}

Und da die Ebene reset ganz unten im Stack liegt, wird das restliche CSS in unserem System sowohl Remedy als auch unsere eigenen lokalen Reset-Ergänzungen überschreiben.

Utility-Klassen verwenden

Am anderen Ende unseres Stacks können „Utility-Klassen“ im CSS eine nützliche Möglichkeit sein, gängige Muster (wie zusätzlichen Kontext für Screenreader) breit anwendbar zu reproduzieren. Utilities neigen dazu, die Spezifitäts-Heuristik zu durchbrechen, da wir sie allgemein definiert haben wollen (was zu einer niedrigen Spezifität führt), aber wir in der Regel möchten, dass sie in Konflikten „gewinnen“.

Indem wir eine utilities-Ebene ganz oben in unserem Ebenen-Stack haben, machen wir das möglich. Wir können dies ähnlich wie im Reset-Beispiel verwenden, indem wir sowohl externe Utilities in eine Unterebene laden als auch unsere eigenen bereitstellen.

@import url('tailwind.css') layer(utilities.tailwind);

@layer utilities {
  /* from https://kittygiraudel.com/snippets/sr-only-class/ */
  /* but with !important removed from the properties */
  .sr-only {
    border: 0;
    clip: rect(1px, 1px, 1px, 1px);
    -webkit-clip-path: inset(50%);
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    margin: -1px;
    padding: 0;
    position: absolute;
    width: 1px;
    white-space: nowrap;
  }
}
Verwendung von Designsystemen und Komponentenbibliotheken

Es gibt viele CSS-Tools, die irgendwo in der Mitte unseres Ebenen-Stacks liegen – sie kombinieren Typografie-Standards, Themes, Komponenten und andere Aspekte eines Systems.

Je nach speziellem Tool könnten wir etwas Ähnliches wie in den Reset- und Utility-Beispielen oben tun – aber es gibt noch ein paar andere Optionen. Ein hochintegriertes Tool könnte eine Top-Level-Ebene verdienen.

@layer reset, bootstrap, utilities;
@import url('bootstrap.css') layer(bootstrap);

Wenn diese Tools beginnen, Ebenen als Teil ihrer öffentlichen API anzubieten, könnten wir sie auch in Teile zerlegen – was es uns ermöglicht, unseren Code mit der Bibliothek zu vermischen.

@import url('bootstrap/reset.css') layer(reset.bootstrap);
@import url('bootstrap/theme.css') layer(theme.bootstrap);
@import url('bootstrap/components.css') layer(components.bootstrap);

@layer theme.local {
  /* styles here will override theme.bootstrap */
  /* but not interfere with styles from components.bootstrap */
}

Verwendung von Ebenen mit bestehenden (ungeschichteten, mit !important gefüllten) Frameworks

Wie bei jeder größeren Sprachänderung wird es eine Anpassungsphase geben, wenn CSS Cascade Layers weit verbreitet werden. Was passiert, wenn Ihr Team bereit ist, nächsten Monat mit Ebenen zu beginnen, aber Ihr Lieblings-Framework beschließt, noch drei Jahre zu warten, bevor es auf geschichtete Stile umstellt? Viele Frameworks werden wahrscheinlich immer noch häufiger !important verwenden, als uns lieb ist! Da !important-Ebenen umgekehrt gewertet werden, ist das nicht ideal.

Trotzdem können uns Ebenen helfen, das Problem zu lösen. Wir müssen nur geschickt vorgehen. Wir entscheiden, welche Ebenen wir für unser Projekt wollen, und das bedeutet, dass wir Ebenen über und auch unter den von uns erstellten Framework-Ebenen hinzufügen können.

Vorerst können wir jedoch eine untere Ebene verwenden, um !important-Stile aus dem Framework zu überschreiben, und eine höhere Ebene, um normale Stile zu überschreiben. Etwa so:

@layer framework.important, framework.bootstrap, framework.local;
@import url('bootstrap.css') layer(framework.bootstrap);

@layer framework.local {
  /* most of our normal framework overrides can live here */
}

@layer framework.important {
  /* add !important styles in a lower layer */
  /* to override any !important framework styles */
}

Es fühlt sich immer noch ein wenig wie ein Hack an, aber es hilft uns, uns in die richtige Richtung zu bewegen – hin zu einer strukturierteren Kaskade. Hoffentlich ist es eine vorübergehende Lösung.

Entwerfen eines CSS-Tools oder Frameworks

Für jeden, der eine CSS-Bibliothek verwaltet, können Kaskaden-Ebenen bei der internen Organisation helfen und sogar Teil der Entwickler-API werden. Durch die Benennung interner Ebenen einer Bibliothek können wir Benutzern unseres Frameworks ermöglichen, sich in diese Ebenen einzuklinken, wenn sie unsere bereitgestellten Stile anpassen oder überschreiben.

Beispielsweise könnte Bootstrap Ebenen für „reboot“, „grid“ und „utilities“ bereitstellen – wahrscheinlich in dieser Reihenfolge gestapelt. Nun kann ein Benutzer entscheiden, ob er diese Bootstrap-Ebenen in verschiedene lokale Ebenen laden möchte.

@import url(bootstrap/reboot.css) layer(reset); /* reboot » reset.reboot */
@import url(bootstrap/grid.css) layer(layout); /* grid » layout.grid */
@import url(bootstrap/utils.css) layer(override); /* utils » override.utils */

Oder der Benutzer lädt sie in eine Bootstrap-Ebene, wobei lokale Ebenen dazwischen geschaltet sind.

@layer bs.reboot, bs.grid, bs.grid-overrides, bs.utils, bs.util-overrides;
@import url('bootstrap-all.css') layer(bs);

Es ist auch möglich, die interne Schichtung vor den Benutzern zu verbergen, indem man private/interne Ebenen in einer anonymen (unbenannten) Ebene gruppiert. Anonyme Ebenen werden der Ebenen-Reihenfolge dort hinzugefügt, wo sie angetroffen werden, aber sie werden Benutzern, die Stile neu anordnen oder anhängen, nicht offenbart.

Ich möchte nur, dass diese eine Eigenschaft !important-er ist

Entgegen mancher Erwartungen machen es Ebenen nicht einfach, einen bestimmten Stil schnell zu eskalieren, damit er einen anderen überschreibt.

Wenn der Großteil unserer Stile ungeschichtet ist, wird jede neue Ebene im Verhältnis zum Standard depriorisiert. Wir könnten das mit einzelnen Stilblöcken machen, aber es würde schnell schwierig werden, den Überblick zu behalten.

Ebenen sind eher als Grundlage gedacht, nicht für einzelne Stile, sondern um konsistente Muster über ein Projekt hinweg zu etablieren. Idealerweise erhalten wir das korrekte Ergebnis, indem wir unseren Stil in die entsprechende (und vordefinierte) Ebene verschieben, wenn wir das System richtig eingerichtet haben.

Wenn der Großteil unserer Stile bereits in gut definierten Ebenen liegt, können wir jederzeit in Erwägung ziehen, eine neue, höchstpriorisierte Ebene oben im Stack hinzuzufügen oder ungeschichtete Stile zu verwenden, um die Ebenen zu überschreiben. Wir könnten sogar eine debug-Ebene ganz oben im Stack haben, um explorative Arbeiten außerhalb der Produktion durchzuführen.

Aber das spontane Hinzufügen neuer Ebenen kann den organisatorischen Nutzen dieses Features zunichtemachen und sollte vorsichtig eingesetzt werden. Am besten fragen Sie sich: Warum sollte dieser Stil den anderen überschreiben?

Wenn die Antwort damit zu tun hat, dass ein Stiltyp immer einen anderen Typ überschreiben sollte, sind Ebenen wahrscheinlich die richtige Lösung. Das könnte daran liegen, dass wir Stile überschreiben, die von einer Stelle kommen, die wir nicht kontrollieren, oder weil wir eine Utility schreiben und diese in unsere utilities-Ebene gehört. Wenn die Antwort damit zu tun hat, dass gezieltere Stile weniger gezielte Stile überschreiben sollen, sollten wir in Erwägung ziehen, die Selektoren so zu gestalten, dass sie diese Spezifität widerspiegeln.

Oder, in seltenen Fällen, haben wir vielleicht Stile, die wirklich wichtig sind – das Feature funktioniert einfach nicht, wenn man diesen speziellen Stil überschreibt. Wir könnten sagen, dass das Hinzufügen von display: none zum Attribut [hidden] in unseren Reset mit niedrigster Priorität gehört, aber dennoch schwer zu überschreiben sein sollte. In diesem Fall ist !important wirklich das richtige Werkzeug für diese Aufgabe.

@layer reset {
  [hidden] { display: none !important; }
}

Scoping und Name-Spacing von Stilen? Nein!

Kaskaden-Ebenen sind eindeutig ein organisatorisches Werkzeug, das die Auswirkungen von Selektoren „einfängt“, insbesondere wenn diese kollidieren. Daher kann es auf den ersten Blick verlockend sein, sie als Lösung für die Verwaltung von Scope oder Name-Spacing zu sehen.

Ein häufiger erster Instinkt ist es, für jede Komponente in einem Projekt eine Ebene zu erstellen – in der Hoffnung, dass dies sicherstellt (zum Beispiel), dass .post-title nur innerhalb eines .post angewendet wird.

Aber Kaskaden-Konflikte sind nicht dasselbe wie Namenskonflikte, und Ebenen sind nicht besonders gut für diese Art von Scope-basierter Organisation ausgelegt. Kaskaden-Ebenen schränken nicht ein, wie Selektoren auf das HTML passen oder angewendet werden, sondern nur, wie sie in der Kaskade zusammenwirken. Sofern wir also nicht sicher sein können, dass Komponente X immer Komponente Y überschreibt, helfen einzelne Komponentenebenen nicht viel. Stattdessen sollten wir die vorgeschlagene @scope-Spezifikation im Auge behalten, die gerade entwickelt wird.

Es kann nützlich sein, Ebenen und Komponenten-Scopes stattdessen als überlappende Anliegen zu betrachten.

An illustration showing how CSS Cascade Layers can be organized by scope, such as buttons, cards, and login layers that fall into component, theme, and default scopes.

Scopes beschreiben, was wir stylen, während Ebenen beschreiben, warum wir stylen. Wir können Ebenen auch so betrachten, dass sie repräsentieren, woher der Stil kommt, während Scopes repräsentieren, woran der Stil haftet.

Testen Sie Ihr Wissen: Welcher Stil gewinnt?

Gehen Sie für jede Situation von diesem Absatz aus:

<p id="intro">Hello, World!</p>

Frage 1

@layer ultra-high-priority {
  #intro {
    color: red;
  }
}

p {
  color: green;
}
Welche Farbe hat der Absatz?

Obwohl die Ebene einen Namen hat, der ziemlich wichtig klingt, haben ungeschichtete Stile eine höhere Priorität in der Kaskade. Daher wird der Absatz green sein.

Frage 2

@layer ren, stimpy;

@layer ren {
  p { color: red !important; }
}

p { color: green; }

@layer stimpy {
  p { color: blue !important; }
}
Welche Farbe hat der Absatz?

Unsere normale Ebenenreihenfolge wird am Anfang festgelegt – ren ganz unten, dann stimpy, dann (wie immer) ungeschichtete Stile ganz oben. Aber diese Stile sind nicht alle normal, einige von ihnen sind important. Wir können sofort auf die !important-Stile filtern und das unwichtige green ignorieren. Denken Sie daran, dass „Origins und Importance“ der erste Schritt der Kaskade sind, noch bevor wir die Schichtung berücksichtigen.

Damit bleiben uns zwei wichtige Stile, beide in Ebenen. Da unsere wichtigen Ebenen umgekehrt gewertet werden, rückt ren nach oben und stimpy nach unten. Der Absatz wird red sein.

Frage 3

@layer Montagues, Capulets, Verona;

@layer Montagues.Romeo { #intro { color: red; } }
@layer Montagues.Benvolio { p { color: orange; } }

@layer Capulets.Juliet { p { color: yellow; } }
@layer Verona { * { color: blue; } }
@layer Capulets.Tybalt { #intro { color: green; } }
Welche Farbe hat der Absatz?

Alle unsere Stile haben denselben Ursprung und Kontext, keiner ist als wichtig markiert und keiner davon ist ein Inline-Stil. Wir haben hier eine breite Palette an Selektoren, von einer hochspezifischen ID #intro bis hin zu einem universellen Selektor * mit der Spezifität null. Aber Ebenen werden aufgelöst, bevor wir die Spezifität berücksichtigen, also können wir die Selektoren vorerst ignorieren.

Die primäre Ebenenreihenfolge wird vorab festgelegt, und dann werden intern Unterebenen hinzugefügt. Unterebenen werden jedoch zusammen mit ihrer Elternebene sortiert – das bedeutet, dass alle Montagues die niedrigste Priorität haben, gefolgt von allen Capulets, und Verona hat das letzte Wort in der Ebenenreihenfolge. Wir können also sofort auf die Verona-Stile filtern, die Vorrang haben. Obwohl der *-Selektor die Spezifität null hat, wird er gewinnen.

Seien Sie vorsichtig mit universellen Selektoren in mächtigen Ebenen!

Debugging von Ebenen-Konflikten in den Browser-Entwicklertools

Die Browser Chrome, Safari, Firefox und Edge verfügen alle über Entwicklertools, mit denen Sie die auf ein bestimmtes Element der Seite angewendeten Stile untersuchen können. Das Styles-Panel dieses Element-Inspektors zeigt die angewendeten Selektoren an, sortiert nach ihrer Kaskadenpriorität (höchste Priorität oben), gefolgt von geerbten Stilen darunter. Stile, die aus irgendeinem Grund nicht angewendet werden, sind in der Regel ausgegraut oder sogar durchgestrichen – manchmal mit zusätzlichen Informationen darüber, warum der Stil nicht angewendet wird. Dies ist die erste Anlaufstelle beim Debuggen jeglicher Aspekte der Kaskade, einschließlich Ebenen-Konflikten.

Safari Technology Preview und Firefox Nightly zeigen (und sortieren) Kaskaden-Ebenen bereits in diesem Panel an. Es wird erwartet, dass diese Tools zeitgleich mit den Kaskaden-Ebenen in den stabilen Versionen eingeführt werden. Die Ebene jedes Selektors ist direkt über dem Selektor selbst aufgeführt.

Showing CSS Cascade Layers in Safari DevTools.
Safari
Showing CSS Cascade Layers in FireFox DevTools.
Firefox

Chrome/Edge arbeiten an ähnlichen Tools und gehen davon aus, dass diese in Canary-Releases (Nightly) verfügbar sein werden, wenn Kaskaden-Ebenen in die stabile Version kommen. Wir werden hier Aktualisierungen vornehmen, wenn sich diese Tools ändern und verbessern.

Browser-Unterstützung und Fallbacks

Diese Browser-Unterstützungsdaten stammen von Caniuse, wo es weitere Details gibt. Eine Zahl gibt an, dass der Browser die Funktion ab dieser Version unterstützt.

Desktop

ChromeFirefoxIEEdgeSafari
9997Nein9915.4

Mobil / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712715.4

Da Ebenen als grundlegende Bausteine einer gesamten CSS-Architektur gedacht sind, ist es schwer vorstellbar, manuelle Fallbacks auf dieselbe Weise zu erstellen, wie man es für andere CSS-Funktionen tun würde. Fallbacks würden wahrscheinlich die Duplizierung großer Codeabschnitte mit unterschiedlichen Selektoren zur Verwaltung der Kaskadenschichtung erfordern – oder die Bereitstellung eines viel einfacheren Fallback-Stylesheets.

Funktionsunterstützung abfragen mit @supports

Es gibt eine @supports-Funktion im CSS, die es Autoren ermöglicht, die Unterstützung von @layer und anderen At-Rules zu testen.

@supports at-rule(@layer) {
  /* code applied for browsers with layer support */
}

@supports not at-rule(@layer) {
  /* fallback applied for browsers without layer support */
}

Es ist jedoch auch unklar, wann diese Abfrage selbst in den Browsern unterstützt wird.

Es gibt noch keine offizielle Spezifikation für eine Syntax, um ganze Stylesheets über das HTML-<link>-Tag zu schichten, aber es wird ein Vorschlag entwickelt. Dieser Vorschlag enthält ein neues layer-Attribut, mit dem die Stile einer benannten oder anonymen Ebene zugewiesen werden können.

<!-- styles imported into to the <layer-name> layer -->
<link rel="stylesheet" href="example.css" layer="<layer-name>">

<!-- styles imported into to a new anonymous layer -->
<link rel="stylesheet" href="example.css" layer>

Alte Browser ohne Unterstützung für das layer-Attribut werden es jedoch komplett ignorieren und das Stylesheet weiterhin ohne Schichtung laden. Die Ergebnisse könnten ziemlich unerwartet sein. Daher erweitert der Vorschlag auch das bestehende media-Attribut, sodass es Abfragen zur Funktionsunterstützung in einer support()-Funktion erlaubt.

Das würde es uns ermöglichen, geschichtete Links von der Unterstützung für Schichtung abhängig zu machen.

<link rel="stylesheet" layer="bootstrap" media="supports(at-rule(@layer))" href="bootstrap.css">

Potenzielle Polyfills und Workarounds

Die wichtigsten Browser sind alle zu einem „Evergreen“-Modell übergegangen, bei dem Updates in recht kurzen Abständen an die Benutzer verteilt werden. Selbst Safari veröffentlicht regelmäßig neue Funktionen in „Patch“-Updates zwischen den seltener erscheinenden Hauptversionen.

Das bedeutet, dass wir erwarten können, dass die Browserunterstützung für diese Funktionen sehr schnell zunimmt. Für viele von uns mag es vernünftig sein, bereits in wenigen Monaten mit der Verwendung von Ebenen zu beginnen, ohne sich große Sorgen um alte Browser machen zu müssen.

Für andere kann es länger dauern, bis sie sich mit der nativen Browserunterstützung wohlfühlen. Es gibt viele andere Möglichkeiten, die Kaskade zu verwalten, indem man Selektoren, Custom Properties und andere Tools verwendet. Es ist theoretisch auch möglich, das grundlegende Verhalten nachzuahmen (Polyfill). Es gibt Leute, die an einem solchen Polyfill arbeiten, aber es ist auch noch nicht klar, wann dieser bereit sein wird.

Weitere Ressourcen

CSS Cascade Layers entwickeln sich noch weiter, aber es gibt bereits viele Ressourcen, darunter Dokumentationen, Artikel, Videos und Demos, die Ihnen helfen, sich noch besser mit Ebenen und ihrer Funktionsweise vertraut zu machen.

Referenz

Artikel

Videos

Demos