</> HTML5Advent
ENFRESDEITPT

// css · Web Platform Advent #19

CSS Cascade Layers: wie @layer die Spezifität verhandelbar macht

Mit Cascade Layers entscheidest du über die Reihenfolge der Layer, welche Regeln gewinnen, statt Selektoren immer weiter aufzurüsten. Wie @layer funktioniert und die zwei Stellen, an denen sich die Prioritätsreihenfolge umkehrt.

Eine Sandsteinwand mit waagerechten Schichten übereinander, helle Bänder über dunklerem Gestein

Die meisten Spezifitätsprobleme in CSS drehen sich in Wirklichkeit gar nicht um Spezifität. Sie entstehen, weil zwei Stylesheets uneins sind und das einzige verfügbare Werkzeug darin besteht, einen Selektor schwerer zu machen als den anderen. Cascade Layers ersetzen dieses Wettrüsten durch eine ausdrückliche Reihenfolge.

Was ein Layer tatsächlich verändert

MDN benennt den Vorteil klar: Regeln innerhalb eines Cascade Layers kaskadieren gemeinsam und geben Webentwicklern damit mehr Kontrolle über die Kaskade. Die Konsequenz ist der spannende Teil, denn sie beseitigt eine Einschränkung, um die du wahrscheinlich seit Jahren herumarbeitest.

Du kannst einfachere Selektoren verwenden, weil du nicht mehr dafür sorgen musst, dass ein Selektor eine ausreichend hohe Spezifität besitzt, um eine konkurrierende Regel zu überschreiben. Du musst nur noch dafür sorgen, dass er in einem späteren Layer auftaucht.

Der Mechanismus ist unmissverständlich: sobald die Layer-Reihenfolge festgelegt ist, werden Spezifität und Reihenfolge des Auftretens ignoriert, wenn es zwischen Layern geht. Eine Regel in einem späteren Layer greift selbst dann, wenn sie eine geringere Spezifität hat als die Regel, die sie überschreibt. Innerhalb eines einzelnen Layers entscheiden weiterhin die normalen Spezifitätsregeln zwischen konkurrierenden Regeln, dein Wissen ist also nicht verschwendet, es gilt nur nicht mehr über Layer-Grenzen hinweg.

Die Reihenfolge vorab festlegen

Die Reihenfolge ergibt sich aus der Reihenfolge der Deklaration: der zuerst deklarierte Layer bekommt die niedrigste Priorität, der zuletzt deklarierte die höchste.

Damit ist die Statement-Form diejenige, zu der es sich zu greifen lohnt, denn sie legt die gesamte Reihenfolge in einer einzigen Zeile fest, bevor überhaupt eine Regel existiert:

@layer theme, layout, utilities;

Von da an schlägt alles in utilities alles in layout, was wiederum alles in theme schlägt, ganz gleich, wie die Selektoren geschrieben sind.

Ein Detail macht das in einer echten Codebasis robust. Sind die Namen einmal deklariert, fügst du einem Layer Regeln hinzu, indem du seinen Namen erneut deklarierst, und die Stile werden diesem Layer angehängt, ohne die Reihenfolge zu verändern. Du kannst einen Layer also über viele Dateien verteilen, in beliebiger Reihenfolge importiert, und die Priorität, die du in jener ersten Zeile bestimmt hast, gilt weiterhin.

Zwei Blöcke aus blaugrünem Glas übereinandergestapelt, mit einer schillernden Naht entlang der Fuge
Zwei Blöcke aus blaugrünem Glas übereinander gestapelt, entlang der Fuge schimmert eine irisierende Naht. Welcher oben liegt, ist entschieden, bevor man hinsieht.

Die erste Umkehrung: Stile ohne Layer gewinnen

Hier versagt bei den meisten Menschen die Intuition, und es lohnt sich, das in den stärksten Worten zu sagen, die MDN verwendet.

Stile, die nicht in einem Layer definiert sind, überschreiben immer Stile, die in benannten und anonymen Layern deklariert wurden.

Halte das gegen die naheliegende Annahme. Du würdest erwarten, dass Layer eine Hierarchie bilden, zu der gewöhnliches CSS ganz unten dazustößt. Es ist genau umgekehrt: CSS ohne Layer sitzt über jedem Layer, den du deklariert hast, und zwar für normale Deklarationen.

Das ist kein Versehen, und es ist wirklich nützlich, sobald man die Form dahinter erkennt. Packe ein fremdes Stylesheet oder ein Reset in einen Layer, und deine eigenen Regeln ohne Layer überschreiben es ohne einen einzigen Spezifitätskampf. Zur Falle wird es nur für denjenigen, der alles in Layer legt bis auf eine vergessene Datei, und dann nicht herausfindet, warum ausgerechnet diese Datei immer gewinnt.

Die zweite Umkehrung: !important dreht das Ganze um

Die andere Überraschung ist, dass die Prioritätsreihenfolge unter wichtigen Regeln die Umkehrung der Reihenfolge normaler Regeln ist.

MDN führt beide Hälften aus. Innerhalb der Autorenstile haben alle wichtigen Deklarationen innerhalb von CSS-Layern Vorrang vor wichtigen Deklarationen außerhalb eines Layers. Und alle normalen Deklarationen innerhalb von Layern haben eine niedrigere Priorität als Deklarationen außerhalb eines Layers.

Dieselbe Codebasis hat also zwei Reihenfolgen, die gleichzeitig in entgegengesetzte Richtungen laufen. Bei normalen Deklarationen gewinnt der letzte Layer, und CSS ohne Layer gewinnt über alles. Bei !important-Deklarationen gewinnt der zuerst deklarierte Layer, und Stile in Layern schlagen die ohne Layer.

Praktisch heißt das: ein Reset in deinem ersten Layer kann eine Deklaration tatsächlich unüberschreibbar machen, indem er sie als wichtig markiert, was eine echte Fähigkeit und zugleich eine echte Fußangel ist. Es heißt auch, dass man beim Debuggen eines Kaskadenproblems wissen muss, ob die beteiligte Deklaration wichtig ist, bevor man überhaupt über die Layer-Reihenfolge nachdenkt.

Wie du es ohne Überraschungen einsetzt

Drei Gewohnheiten decken das meiste ab.

Deklariere die vollständige Reihenfolge in einer Zeile, und zwar früh. Ein einzelnes @layer-Statement am Anfang deines Einstiegs-Stylesheets ist die gesamte Konfiguration. Wer es liest, weiß, was was schlägt, ohne eine weitere Datei zu öffnen.

Entscheide bewusst, was ohne Layer bleibt. Da CSS ohne Layer bei normalen Deklarationen über jedem Layer rangiert, ist es eine Entscheidung mit Folgen und keine neutrale Voreinstellung, Stile außerhalb eines Layers zu lassen. Entweder du packst alles in einen Layer, oder du hältst die Menge ohne Layer klein und absichtsvoll.

Behandle wichtige Deklarationen als eigenes System. Sie folgen nicht der Reihenfolge, die du entworfen hast, sondern ihrem Spiegelbild. Wenn du in einer geschichteten Architektur zu !important greifst, ist meist die Layer-Reihenfolge das, was angepasst werden musste.

Cascade Layers machen CSS nicht einfacher zu lernen, sie fügen einen Mechanismus mit zwei kontraintuitiven Umkehrungen hinzu. Was sie leisten: der Ausgang eines Konflikts wird zu etwas, das du im Voraus entschieden hast, statt zu etwas, das du durch Zählen von Selektoren herausgefunden hast.