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
- Einführung: Was sind Cascade Layers?
- Wo ordnen sich Ebenen in der Kaskade ein?
- !important-Ursprünge, Kontext und Ebenen sind umgekehrt!
- Festlegen einer Ebenen-Reihenfolge
- Syntax: Arbeiten mit Cascade Layers
- Anwendungsfälle: Wann sollte ich Cascade Layers verwenden?
- Testen Sie Ihr Wissen: Welcher Stil gewinnt?
- Debugging von Ebenen-Konflikten in Browser-Entwicklertools
- Browser-Unterstützung und Fallbacks
- Weitere Ressourcen
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:
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!
!importantBrowser-Stile (am mächtigsten)!importantBenutzer-Präferenzen!importantAutoren-Stile- normale Autoren-Stile
- normale Benutzer-Präferenzen
- 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.

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:
!importantShadow-Kontext (am mächtigsten)!importantHost-Kontext- normaler Host-Kontext
- 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:
- Utilities (am mächtigsten)
- Komponenten
- Standardwerte (am wenigsten mächtig)
Wenn Stile in diesen Ebenen als wichtig markiert werden, würden sie drei neue, umgekehrte wichtige Ebenen erzeugen:
!importantStandardwerte (am mächtigsten)!importantKomponenten!importantUtilities- normale Utilities
- normale Komponenten
- 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; }
- ungelayerte Stile (am mächtigsten)
- Ebene-3
- Ebene-2
- 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; }
!importantEbene-1 (am mächtigsten)!importantEbene-2!importantEbene-3!importantungelayerte Stile- normale ungelayerte Stile
- normale Ebene-3
- normale Ebene-2
- 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; }
- ungelayerte Stile (am mächtigsten)
- Ebene-3
- Ebene-3 unverschachtelt
- Ebene-3 Unterebene-2
- Ebene-3 Unterebene-1
- Ebene-2
- 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:
- ungelayerte Stile (am mächtigsten)
- Utilities
- Komponenten
- Framework
- Standardwerte
- 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 mit @import oder <link> schichten
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.
initialsetzt 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 derinitial-Wert vondisplayimmerinline, egal auf welches Element wir ihn anwenden.inheritbewirkt, 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.unsetwirkt so, als würden einfach alle vorherigen Werte entfernt – sodass vererbte Eigenschaften wiederinheritanwenden, während nicht vererbte Eigenschaften auf ihreninitial-Wert zurückfallen.revertentfernt 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:
- Utilities (am mächtigsten)
- Komponenten
- 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:
!importantStandardwerte (am mächtigsten)!importantKomponenten!importantUtilities!importantungelayerte Stile- CSS-Animationen
- normale ungelayerte Stile
- normale Utilities
- normale Komponenten
- 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:
- ✅
!importantStandardwerte (am mächtigsten) - ✅
!importantKomponenten - ✅
!importantUtilities - ✅
!importantungelayerte Stile - ✅ CSS-Animationen
- ✅ normale, nicht-geschichtete Stile
- ❌ normale Utilities
- ✅ normale Komponenten
- ✅ 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
- ✅
!importantStandardwerte (am mächtigsten) - ✅
!importantKomponenten - ❌
!importantUtilities - ❌
!importantnicht-geschichtete Stile - ❌ CSS-Animationen
- ❌ normale nicht-geschichtete Stile
- ❌ normale Utilities
- ✅ normale Komponenten
- ✅ 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:
- Low-Level-Reset und Normalisierungsstile
- Element-Standards für grundlegende Typografie und Lesbarkeit
- Themes, wie Hell- und Dunkelmodus
- Wiederverwendbare Muster, die in mehreren Komponenten vorkommen könnten
- Layouts und größere Seitenstrukturen
- Einzelne Komponenten
- 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.

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.


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
| Chrome | Firefox | IE | Edge | Safari |
|---|---|---|---|---|
| 99 | 97 | Nein | 99 | 15.4 |
Mobil / Tablet
| Android Chrome | Android Firefox | Android | iOS Safari |
|---|---|---|---|
| 127 | 127 | 127 | 15.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.
Zuweisen von Ebenen in HTML mit dem <link>-Tag
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
- Die Zukunft von CSS: Cascade Layers (CSS
@layer) von Bramus Van Damme - Erste Schritte mit CSS Cascade Layers von Stephanie Eckles, Smashing Magazine
- Kaskaden-Ebenen kommen in Ihren Browser von Una Kravets, Chrome Developers
Videos
- Wie funktioniert CSS !important eigentlich? von Una Kravets
- Ein Überblick über die neuen @layer und layer() CSS-Primitive von Una Kravets
- CSS Revert & Revert-Layer Keywords von Una Kravets
Diese Umkehrung der Layer-Rangfolge bei der Verwendung von !important ist wirklich verblüffend und birgt großes Potenzial für Verwirrung. Ich finde, dieses Verhalten hätte in den offiziellen Dokumenten viel deutlicher hervorgehoben werden müssen – nachdem ich in diesem Artikel davon erfahren hatte, musste ich die Spezifikation immer wieder durchsuchen, bis ich den Absatz fand, der es beschreibt. In der MDN-Dokumentation konnte ich kein Wort darüber finden.
Das wird besonders schlimm und verwirrend sein, wenn man vorhandenes „schlechtes“ CSS mit !important in einen Layer importiert – wenn man CSS von Drittanbietern in Layer lädt, ist das definitiv nicht das, was man erwarten würde.
Ich habe das Gefühl, dass sie hier einen Fehler gemacht haben? Layer sollen CSS-Effekte eingrenzen und kontrollieren, aber !important scheint aus irgendeinem Grund noch destruktivere Kräfte zu haben als ohnehin schon. Es wäre viel nützlicher gewesen, die Auswirkungen importierter Regeln mit !important auf die Dateien zu beschränken, in denen sie ursprünglich Unheil anrichteten, um uns durch Layering mehr Kontrolle von außen über sie zu geben.
Wenn jemand eine Erklärung anbieten kann, bitte gerne?
(In Anbetracht dessen, dass !important jetzt noch gefährlicher ist als zuvor, und da es wahrscheinlich schon zu spät ist, es zu ändern, wäre es vielleicht an der Zeit, es ganz einzustellen…)
Zustimmung. Auf den ersten Blick schien es, als handele es sich um isolierte Code-Silos, in denen ein Code-Satz immer eine höhere Spezifität hat als ein anderer, was sehr geordnet und nützlich klingt. Das wird mit
!importantüber den Haufen geworfen. Es wird regelrecht verrückt, wenn wir dierevert-layer-Funktionalität betrachten. Ich habe das Gefühl, dass!importantheute zwar nicht als besonders gute Praxis gilt, diese Wahrnehmung in Zukunft aber noch stark verstärkt wird. Ja, es gibt Workarounds, aber es wirkt doch sehr nach Bastellösung, wenn einige Overrides davor und andere danach stehen müssen. Das wird ein erhebliches Hindernis für die Akzeptanz sein.Um zur Verwirrung beizutragen: Im
!important-Kontext wird nicht alles umgekehrt. Spätere Regeln gewinnen immer noch über ähnliche frühere Regeln, wenn beide als important deklariert sind, höhere Spezifität gewinnt über niedrigere, und wichtige Inline-Regeln überschreiben alle Layer.Ohne
!important1. Inline
2. Unlayered (Nicht in Layern)
2.1 Spezifität innerhalb unlayered
2.2 Reihenfolge innerhalb unlayered
3. Später Layer
3.1 Spezifität innerhalb später Layer
3.2 Reihenfolge innerhalb später Layer
4. Früher Layer
4.1 Spezifität innerhalb früher Layer
4.2 Reihenfolge innerhalb früher Layer
dann…
Mit
!important1. Inline
4. Früher Layer
4.1 Spezifität innerhalb früher Layer
4.2 Reihenfolge innerhalb früher Layer
3. Später Layer
3.1 Spezifität innerhalb später Layer
3.2 Reihenfolge innerhalb später Layer
2. Unlayered (Nicht in Layern)
2.1 Spezifität innerhalb unlayered
2.2 Reihenfolge innerhalb unlayered
Hallo zusammen, ich denke, ihr habt recht, die Wichtigkeits-Umkehrung als potenziellen Verwirrungspunkt für Autoren hervorzuheben, und ich hoffe auch, dass MDN dem mehr Aufmerksamkeit schenkt. Ich erwarte, dass ihre Seite einige Updates erhalten wird, jetzt wo das Feature in den Browsern eingeführt wird und nicht mehr theoretisch ist. Aber wie immer wird das etwas Zeit und Aufmerksamkeit erfordern (Dokumentation entsteht nicht durch Zauberei, aber sie nehmen Issues/Anfragen und PRs an).
Ein Teil der Verwirrung rührt daher, dass Webautoren
!importantprimär als Hack zum Überschreiben der Spezifität genutzt haben, während es in der gesamten Kaskade einem anderen Zweck dient. In gewisser Weise kann man diese Änderung also als „Deprecation“ (Veralten) des Wichtigkeits-Hacks betrachten, den Autoren bisher genutzt haben, und ihn durch ein explizites Layering-Feature ersetzen. Es wird einige Komplikationen während dieser Übergangszeit geben, aber ansonsten erwarte ich, dass Autoren, die Layer verwenden, anfangen werden, Important-Flags zu entfernen, die auf diese Weise genutzt wurden – nämlich als Spezifitäts-Overrides.Wenn man Wichtigkeit nur als diesen einfachen Spezifitäts-Override-Hack betrachtet, sieht das Layer-Umkehrverhalten sehr verwirrend aus. Für diesen Anwendungsfall ergibt es keinen Sinn! Aber sobald wir diesen Anwendungsfall als veraltet betrachten… bleibt immer noch der ursprüngliche Zweck des Wichtigkeits-Features. Autoren sind nicht die Einzigen, die CSS nutzen, und
!importantist weiterhin nützlich und notwendig als Ausgleichsmechanismus zwischen den Ursprüngen (Origins) – ein Anwendungsfall, der auch in Bezug auf Layer Sinn ergibt. Nicht als einfacher Prioritätssprung (dafür kann man einen neuen Layer nehmen), sondern als Weg, um zu markieren, dass einige Stile tatsächlich erforderlich sind, damit ein Selektor-Muster wie beabsichtigt funktioniert. Dieser Anwendungsfall existiert weiterhin und kann sehr nützlich sein – und ich denke, das Layer-Umkehrverhalten passt ziemlich gut zu diesem Anwendungsfall.Mein Punkt ist: Dasselbe Feature kann für einen Anwendungsfall verwirrend sein, während es in einem anderen hilfreich ist. Und es kann für eine Situation (inoffiziell) „veraltet“ sein, während es in anderen Fällen immer noch notwendig und nützlich ist. Also ja, ich stimme zu, dass dies eine verwirrende Umstellung für Leute sein wird, die an das alte (autorenzentrierte) Muster gewöhnt sind. Aber ich denke auch, dass dies ein sehr mächtiges neues Muster bietet, das die Leute in ihren Design-Systemen mit großem Erfolg zu nutzen lernen könnten.
Ich hoffe, dass Autoren sich die Zeit nehmen, den Anwendungsfall und das mentale Modell für die beabsichtigte Nutzung von Wichtigkeit zu lernen, anstatt es einfach zu verwerfen, nur weil das alte mentale Modell nicht mehr passt.
Großartige Sache! Ich wende Cascade Layers gerade bei einem neuen Projekt an. Zu
!important: Ich finde, dass die Verwendung von Cascade Layers die Notwendigkeit dafür fast vollständig eliminiert, sogar bei proaktiver Nutzung wie Overrides.Die Tatsache, dass das Hinzufügen von
!importantin einem Layer die Layer-Reihenfolge umkehrt, ist bemerkenswert. Nehmen wir an, Sie haben so etwas wie das hier:-Und Sie importieren Code von Drittanbietern und weisen ihm den Layer "utilities" zu:-
Falls nun in der
blabla.cssein!importantvorkommt, werden wir Schwierigkeiten haben, es zu überschreiben. Unlayered Styles können es nicht überschreiben, also wird die einzige Lösung sein, ein!importantin die Layer "reset" und "default" zu werfen.Ich denke, es ist an der Zeit, dass
!importanteingestellt wird und Platz für Cascade Layers macht, genau wieclipeingestellt und stattdessenclip-pathbevorzugt wurde.Außerdem denke ich, dass Entwickler höchstwahrscheinlich das
layer-Attribut in der HTML-Datei verwenden wollen, anstatt es mit dem Import-Statement zu nutzen, da das Verarbeiten zusätzlicher Anfragen über eine externe CSS-Datei definitiv Performance-Probleme verursachen würde.Wenn ich den Artikel richtig verstanden habe, könnte dies der Weg sein, damit umzugehen
Wenn ich nun wieder dein Beispiel nehme
Und
blabla.cssenthält einen Stil mit!important, sollten wir in der Lage sein, diesen mit einem anderen Important-Stil im Important-Layer zu überschreiben?Ich habe viel Zeit in die Implementierung des Layer-Supports im Polypane-Browser investiert und ihn Anfang dieses Monats veröffentlicht, noch vor vielen anderen Browsern (Safari TP war früher dran).
Wenn Sie also nach einem Chromium-basierten Browser suchen, der speziell für Entwickler entwickelt wurde und Support für @layer bietet (der diese auch in seinen Devtools anzeigt), schauen Sie sich Polypane an.
Hier ist ein kurzer Screenshot, der es in Aktion zeigt; es gibt Support für benannte und unbenannte Layer sowie verschachtelte Layer
Die Lösung für die Komplexität der Kaskade ist… noch mehr Komplexität hinzuzufügen? Das ist der Grund, warum ich einzelne Klassennamen mit CSS-Modulen verwende. Es hebelt die Kaskade komplett aus.
Es hebelt die Kaskade nicht aus. Die Kaskade findet immer noch statt. Von der gesamten Kaskade hast du lediglich die „Spezifität“ flachgeklopft, und das nur innerhalb der normalen und wichtigen Autoren-Ursprünge. Um das zu erreichen, musstest du auf viele mächtige Selektorkombinationen verzichten, viele eindeutige Klassen hinzufügen (unter Verwendung zusätzlicher Abhängigkeiten, um diese Eindeutigkeit sicherzustellen) und eine strikte „Reihenfolge des Erscheinens“ beibehalten, wo immer interne Kaskadenkonflikte noch möglich sind.
Man kann das so machen, aber es ist nur insofern einfacher, als dass du dich entscheidest, keine anderen, expliziteren Werkzeuge zur Kaskadenverwaltung zu nutzen, die CSS bietet. Wie wenn man einen Werkzeugkasten nur auf Hämmer reduziert, weil Schraubendreher Komplexität hinzufügen. Aber dann ersetzt du diese Sprach-„Komplexität“ (Features) durch die zusätzliche Komplexität (Features) von Build-Tools wie CSS-Modulen und eindeutigen Klassenkonventionen. Weil du verstehst, dass zusätzliche Sprach-/Tooling-Komplexität genutzt werden kann, um unseren eigentlichen Code klarer, expliziter und bewusster zu machen.
Es steht dir (natürlich) frei, die Werkzeuge zu verwenden, die du zur Verwaltung der Kaskade am liebsten magst. Aber solange du CSS in irgendeiner Weise verwendest, bleibt die Kaskade unvermeidlich und unbesiegt.
Ich tue mich hier schwer. Ich sehe keinen Anwendungsfall für diese CSS-Layer.
Ich hatte seit Jahren keinen Spezifitätskrieg mehr. Dank Namespacing. Erst recht nicht mit dem Reset oder dem Framework, das ich einsetze.
Wenn man sich diese Anwendungsfälle ansieht (https://noti.st/jensimmons/QOEOYT/three-topics#syyV24S), sieht man, dass CSS-Layer kein einziges dieser Probleme lösen. Ich denke, sie dachten, es gäbe verschiedene Ursprünge und keine Kaskade zwischen diesen Ursprüngen. Jetzt haben wir Layer, mit der Kaskade.
Man kann alte Stile also nicht wirklich refactoren. Die alten Stile werden immer in die neuen Stile kaskadieren, man muss sie also immer wieder überschreiben. Ja, das kann man mit einer niedrigeren Spezifität tun, aber abgesehen davon gibt es nichts zu gewinnen, was die Quellcode-Reihenfolge nicht auch könnte. Zudem kann man die alten Stile nicht parallel zu den neuen Stilen laufen lassen, weil ein Teil der neuen Stile (mit der niedrigeren Spezifität) innerhalb der alten Stile Unheil anrichtet.
Dasselbe gilt für Drittanbieter-Sachen: Wenn die schlechtes CSS schreiben, könnte irgendwo ein
!importantsein. Um einige Stile zu überschreiben, hat man einen Layer unter dem Drittanbieter-Layer, aber für den!important-Override braucht man einen weiteren Layer über dem Drittanbieter-Layer. Ich denke, das erhöht die Komplexität und Wartbarkeit. Vor allem, wenn die Lösung darin bestünde, den Selektor zu kopieren und später in der Quellcode-Reihenfolge einzufügen. So einfach ist das.Es zeigt sich, dass es nur begrenzte echte Anwendungsfälle gibt. In den letzten Wochen hatten alle Blogeinträge, die ich über CSS-Layer gelesen habe, diese konstruierten theoretischen Beispiele. Diese zeigen zwar, wie die Layer funktionieren, aber sie zeigen keinen realen Anwendungsfall.
Contra
– Erhöht Komplexität
– Schwerer zu warten
– Nebenwirkungen durch zu generische Selektoren (Spezifität hat ihren Grund)
– Bringt die Quellcode-Reihenfolge durcheinander (Spaghetti-CSS-Code)
– CSS-Parsing wurde langsamer
Pro
– Einfaches Überschreiben bestehender Stile sogar mit niedrigerer Spezifität.
CSS-Layer klingen auf dem Papier sehr gut, aber in der realen Welt sind sie von begrenztem Nutzen. Momentan lösen sie keine Probleme, die wir nicht mit den bereits vorhandenen Werkzeugen lösen könnten. Stattdessen schaffen sie neue Fallstricke und Verwirrung über Kaskade, Spezifität und Wichtigkeit. Sie machen CSS schwerer zu erlernen. Sie fügen eine Ebene („Layer“) der Verwirrung hinzu (entschuldigt das Wortspiel).
Jedes Mal, wenn eine CSS-Datei geparst wird, wird Zeit und Energie für so wenig Gewinn verschwendet. Es fühlt sich nach Aufgeblähtheit und „Featureritis“ an.
Ich war auch kein großer Fan dieses Features (siehe mein Kommentar oben), aber dein Kommentar hat mich zum Nachdenken gebracht, und ich erkenne jetzt, welchem Ziel Layer dienen sollen.
Auf den ersten Blick schien es, dass Layer eine Methode bieten, um schlecht geschriebene oder halbkompatible CSS-Module so „einzuschließen“, dass alles darin eine niedrige Priorität hat und alles, was man darüber schreibt, es überschreibt. Ich hatte Bedenken wegen der damit verbundenen Komplexität, aber ich sah den Punkt. Deine (ziemlich guten) Punkte haben mir klar gemacht, dass Layer überhaupt nicht dafür da sind.
Stattdessen können Layer solchen CSS-Modulen dienen, die mit Layern im Hinterkopf geschrieben wurden. Sie dienen nicht dem Nutzer des Moduls, sie dienen dem Autor des Moduls! Nun kann der Modulautor beliebige Selektorsequenzen verwenden, die innerhalb der Logik seines eigenen Moduls funktionieren, und muss sich keine Sorgen machen, dass der Nutzer des Moduls später mit seinen Selektoren kämpfen muss, weil das Layer-System sich darum kümmert.
Vor diesem Hintergrund verstehe ich den Sinn von
!importantmit dem Layer-Umkehr-Mechanismus. Wie Miriam anmerkte, kann!importantnun als veraltet angesehen werden als Werkzeug zum Überschreiben vorheriger CSS-Regeln. Die Verwendung in diesem Sinne innerhalb eines geschichteten Moduls würde ein absolutes Chaos verursachen.Stattdessen kommuniziert
!important: „Diese Deklaration ist essenziell, damit das Modul korrekt funktioniert. Sie muss resistent gegen alle zukünftigen unbekannten Deklarationen sein und darf in einem nachfolgenden Modul nicht überschrieben werden.“ Jetzt macht es Sinn, dass der Mechanismus nachfolgende Module aktiv daran hindert, die wichtige Deklaration zu überschreiben. Der Modulnutzer kann sich entscheiden, sie trotzdem zu überschreiben, aber das wäre nur mit einem speziellen „Overrides“-Layer möglich, nicht durch zufälligen Code.Wie sieht es mit der Abwärtskompatibilität mit älteren Browsern aus? Man bräuchte zwei Stylesheets – mit und ohne Layer. Das ist nicht, wie CSS funktionieren sollte. Es sollte keine bahnbrechenden Änderungen (Breaking Changes) geben.
Alle neuen Features funktionieren in alten Browsern nicht, das lässt sich nicht vermeiden. Die Anforderung an die Abwärtskompatibilität ist, dass neue Features altes CSS in keinem Browser kaputt machen dürfen.
In diesem Fall wird, wie bei Media Queries, jeder Code innerhalb eines
@layervor alten Browsern verborgen. Und es ist ein Polyfill in Entwicklung, der automatisch ein Legacy-Stylesheet generieren und verlinken könnte. Es ist keine ideale Situation, aber eine vorübergehende.Gibt es eine Möglichkeit, Drittanbieter-Komponenten, die kein Stylesheet offenlegen, einen Layer zuzuweisen, damit ich die Stile der Drittanbieter-Komponenten einen Layer unter meinem eigenen Layer halten kann?
Hallo Mark, theoretisch ist die Antwort „Ja“ – da beliebige Stile von überall her einem Layer hinzugefügt werden können. Aber die praktische Antwort hängt wahrscheinlich stark von der genauen Tool-Chain ab, die zum Ausliefern deiner Komponenten verwendet wird, und auch von der Tool-Chain zum Anwenden derselben. Ohne viel mehr Details kann ich dazu nichts sagen, und selbst dann gibt es nur einen begrenzten Satz an Tools, mit denen ich Erfahrung habe. Ich würde ein Issue bei demjenigen eröffnen, der die Komponenten ausliefert, um zu sehen, ob er einen Rat hat.
Hey! Toller Artikel :)
Es gibt eine Frage, die ich gerne stellen würde.
Gibt es eine Möglichkeit, einen ganzen Layer zurückzusetzen? Genau wie eine einzelne CSS-Regel:
color: revert-layer.Ich meine, ist es möglich, die Layer-Reihenfolge dynamisch in Abhängigkeit von Media Queries oder Klassennamen zu ändern?
Du kannst
all: revert-layerverwenden, um alle Layer-Stile zurückzusetzen, ähnlich wieall: revertverwendet wird, um Autoren-Stile zurückzusetzen.Media Queries können die Layer-Reihenfolge absolut ändern. Die
@layer-Regeln sind innerhalb von Media Queries erlaubt und beeinflussen die Reihenfolge der Layer nur dann, wenn die Media Query zutrifft. Du könntest so etwas machen wieHey :)
Ich spiele gerade mit Cascade Layers für ein neues Projekt herum und scheine einen „Undefined mixin“-Fehler zu bekommen, wenn ich Bootstrap wie folgt seinen eigenen Layer zuweise
`// Layers order
@layer bootstrap, theme, utilities, third-party;
// Variables
@import “1-variables/app”;
// Theme mixins
@import “2-mixins/badge”;
@import “2-mixins/button”;
@import “2-mixins/modal”;
@import “2-mixins/switch”;
@import “2-mixins/tabs”;
@import “2-mixins/theme”;
// Bootstrap
@layer bootstrap {
@import “~bootstrap/scss/bootstrap”;
}
// Theme components
@layer theme {
@import “3-components/accordion”;
@import “3-components/alert”;
@import “3-components/avatar”;
}`
Oder sogar
@import '~bootstrap/scss/bootstrap' layer(bootstrap);Der Fehler
ERROR in ./resources/sass/phoenix/phoenix.scssModule build failed (from ./node_modules/laravel-mix/node_modules/mini-css-extract-plugin/dist/loader.js)
ModuleBuildError: Module build failed (from ./node_modules/sass-loader/dist/cjs.js)
SassError: Undefined mixin.
╷
26 │ @include border-end-radius($alert-border-radius);
Wenn ich Bootstrap dann außerhalb eines Layers importiere, baut das Projekt einwandfrei.
Wie man sieht, besagt die Reihenfolge meiner Layer, dass Bootstrap an erster Stelle steht, daher ist das für mich seltsam.
Ist sonst noch jemand auf dieses Problem gestoßen?
Ich weiß nicht, welche Version von Sass du verwendest, aber Sass hat keine spezielle Behandlung für Layer. Daher behandelt es deinen Import wahrscheinlich als CSS-Import (statt als Sass-Import), weil er eine Nicht-Sass-Syntax enthält.
Wenn
~bootstrap/scss/bootstrapsowohl Sass-Features (Mixins, Variablen etc.) bereitstellt als auch direkten CSS-Output hat, müssen diese etwas unterschiedlich gehandhabt werden. Du willst den CSS-Output in einen Layer legen, aber die Sass-Features überall verfügbar halten. Das sollte mit Sass-Modulen und spezifischeren Bootstrap-Pfaden möglich sein. Etwa so…Leite alle Sass-Features von einer Datei weiter
Dann kannst du in deiner Hauptdatei die weitergeleiteten Sass-Features mit
@usenutzen und das CSS von Bootstrap separat ladenEntschuldigung, ein kleiner Tippfehler. Für
load-css()benötigst du das integrierte „meta“-Modul von SassIch sehe keinerlei Wert darin, jemals Layer zu verwenden, außer für den einen Fall, in dem ihr !important dasjenige in allen anderen Nicht-Layer-Stilen auf einer typischen Webseite überschreibt. Das war's. Und selbst das ist verwirrend, da es umgekehrt ist, wobei erste Layer spätere Layer überschreiben. Super dämliches Design.
Fakt ist: In dem Moment, in dem man ein Element in seinen Autoren-Stylesheets ändert, wie div, p, span usw., kann man diese niemals mittels Layern über irgendeine Kaskade, Spezifität usw. überschreiben. Und die meisten modernen Webentwickler erstellen „Reset“-Stylesheets, um ihr Element-Design zu ändern. Damit sterben Layer.
Die traurige Realität ist, dass Präprozessoren, Variablen, Layer und andere neue Webtechnologien von Leuten entworfen wurden, die nie verstanden haben, wie Cascading Style Sheets funktionieren und wie textbasierte Eigenschaften vererbt werden. Wenn sie es verstünden, würden sie sehen, dass sie diese Werkzeuge nie brauchen. Man muss Dinge, die vererbt werden (etwa vom Body-Element einer typischen Webseite), nicht ändern. Und wenn man es doch tut, hat man bereits ein gewichtetes und ursprungsähnliches, layerähnliches System mit IDs und Klassen, das exakt genauso funktioniert wie die Priorität von Stylesheet-Ursprung und Layer-Ursprung – mit einem Bruchteil des benötigten CSS-Codes!
Beispiel: HTML, das per ID modifiziert wurde, kann niemals durch einen stärker gewichteten Satz an Klassen überschrieben werden. Sie bleiben im Grunde in ihrem eigenen Layer.
Das ist der Grund, warum CSS seit über 20 Jahren perfekt funktioniert und sich selten ändert oder ändern muss.
Nur eine kurze Notiz: Ein Postcss-Polyfill ist jetzt verfügbar: https://www.npmjs.com/package/@csstools/postcss-cascade-layers
Es ist auch in
postcss-preset-enventhalten: https://preset-env.cssdb.org/features/#cascade-layersIch mag das farbige Kaskaden-Bild (https://css-tricks.de/wp-content/uploads/2022/02/layers-tall-outlines2.svg), aber sollten Klassen nicht wichtiger als Elemente sein? Das Bild liest sich aktuell so, als stünde dort * -> .class -> element -> #ID.
Gut aufgepasst! Habe das korrigiert. ✨
Wie ändert man die Wichtigkeit durch Importieren von externem CSS?
@layer component-1,component,importLayer;
@import url('example.css') layer(importLayer)Der Component-Layer funktioniert, aber nicht der importLayer? Könnten Sie ein Beispiel geben?