Vom Autor definierte CSS-Namen und Shadow DOM: In Spezifikation und Praxis

Von Nutzern definierte CSS-Namen und das Shadow-DOM sollten zusammenarbeiten. Allerdings sind Browser nicht mit der Spezifikation konform, manchmal auch nicht untereinander, und jeder CSS-Name ist auf eine etwas andere Weise inkonsistent.

In diesem Artikel wird der aktuelle Status der Funktionsweise von vom Autor definierten CSS-Namen in verschiedenen Schattenbereichen dokumentiert. Wir hoffen, dass er als Leitfaden für die Verbesserung der Interoperabilität in naher Zukunft dienen kann.

Was sind vom Autor definierte CSS-Namen?

Vom Autor definierte CSS-Namen sind ein relativ alter CSS-Syntaxmechanismus, der ursprünglich für die @keyframes-Regel eingeführt wurde. Dabei wird eine <keyframe-name> entweder als benutzerdefinierte Identität oder als String definiert. Mit diesem Konzept können Sie etwas in einem Teil eines Stylesheets deklarieren und in einem anderen Teil darauf verweisen.

/* "fade-in" is a CSS name, representing a set of keyframes */
@keyframes fade-in {
  from { opacity: 0 };
  to { opacity: 1 }
}

.card {
  /* "fade-in" is a reference to the above keyframes */
  animation-name: fade-in;
}

Weitere CSS-Funktionen, die CSS-Namen verwenden, sind Schriftarten, Property-Deklarationen, Containerabfragen und seit Kurzem auch Ansichtsübergänge, Ankerpositionierung und scrollgesteuerte Animationen. Die folgende unvollständige Tabelle enthält Namen, deren Status in Chrome geprüft wird.

Funktion Erklärung zum Namen Name der Referenz
Keyframes @keyframes animation-name
Schriftarten @font-face { }
@font-palette-values
font-family
font-palette
Property-Erklärungen @property
Jede nicht registrierte Erklärung für benutzerdefinierte Properties
var()
Übergänge ansehen view-transition-name
view-transition-class
::view-transition-* Pseudoelemente
Ankerpositionierung anchor-name position-anchor
Scroll-basierte Animation view-timeline-name
scroll-timeline-name
animation-timeline
Listenstile @counter-style list-style
Zähler counter-reset
counter-set
counter-increment
Containerabfragen container-name @container
Seite page @page

Wie Sie in der Tabelle sehen, hat ein Name für ein Preisvergleichsportal in der Regel eine entsprechende Referenz. animation-name ist beispielsweise eine Referenz auf den Namen @keyframes. CSS-Namen unterscheiden sich von im DOM definierten Namen wie Attributen und Tagnamen, da sie im Kontext von Stylesheets deklariert und dann darauf verwiesen werden.

Beziehung zwischen Namen und Shadow-DOM

Während CSS-Namen Beziehungen zwischen verschiedenen Teilen eines Dokuments oder Stylesheets herstellen sollen, ist das Shadow-DOM genau für das Gegenteil gedacht. Es kapselt Beziehungen ein, damit sie nicht in Webkomponenten eindringen, die einen eigenen Namensraum haben sollen.

Durch die Kombination von CSS-Namen und dem Schatten-DOM sollte das Erstellen von Webkomponenten ausdrucksstark genug sein, um flexibel zu sein, aber gleichzeitig so eingeschränkt, dass es stabil ist.

Das ist in der Theorie gut. In der Praxis sind Browser inkonsistent in der Art und Weise, wie CSS-Namen mit dem Schatten-DOM interagieren, sowohl zwischen Funktionen im selben Browser, zwischen Browsern als auch zwischen den Funktionen und der Spezifikation.

So sollten Namen und das Shadow-DOM zusammenarbeiten

Um das Problem zu verstehen, ist es hilfreich zu wissen, wie diese Teile von CSS theoretisch zusammenarbeiten sollten.

Die allgemeine Regel

Die allgemeine Regel dafür, wie sich CSS-Namen in verschiedenen Schattenbäumen verhalten, ist in der CSS Scoping Level 1-Spezifikation definiert. Zusammenfassung: Ein CSS-Name ist innerhalb des Gültigkeitsbereichs, in dem er definiert ist, global. Das bedeutet, dass auf ihn über untergeordnete Schattenbäume zugegriffen werden kann, aber nicht über Geschwister- oder übergeordnete Schattenbäume. Dies unterscheidet sich von Namen auf der Webplattform wie Element-IDs, die im selben Baumbereich gekapselt sind.

Ausnahme von der Regel: @property

Im Gegensatz zu anderen CSS-Namen werden CSS-Properties nicht vom Shadow-DOM gekapselt. Sie sind vielmehr die gängige Methode, um Parameter zwischen verschiedenen Schattenbäumen zu übergeben. Das macht den @property-Beschreibungstyp besonders: Er soll sich wie eine dokumentglobale Typendeklaration verhalten, die definiert, wie eine bestimmte benannte Eigenschaft funktioniert. Da Properties in allen Schattenbäumen übereinstimmen müssen, würden Abweichungen bei Property-Deklarationen zu unerwarteten Ergebnissen führen. Daher werden @property-Deklarationen so angegeben, dass sie gemäß der Dokumentreihenfolge flachgelegt und aufgelöst werden.

So sollte die Regel mit ::part funktionieren

Schattenteile machen ein Element in einem Schattenbaum für das übergeordnete Element sichtbar. So kann der übergeordnete Baum auf dieses Element zugreifen und es auch mit dem ::part-Element formatieren.

Da mit ::part zwei Baumbereiche dasselbe Element stylen können, wird die folgende Kaskadenabfolge angegeben:

  1. Prüfen Sie zuerst den Stil im Schattenkontext. Dies ist der „Standard“-Stil des Teils.
  2. Wenden Sie dann den externen Stil wie in ::part definiert an. Dies ist der „benutzerdefinierte“ Stil des Teils.
  3. Wenden Sie dann einen internen Stil an, der zusammen mit !important definiert ist. So kann in einem benutzerdefinierten Element angegeben werden, dass eine bestimmte Eigenschaft eines bestimmten Teils nicht mit ::part angepasst werden kann.

Das bedeutet, dass auf Namen innerhalb des Shadow DOM nicht über ein ::part verwiesen werden kann, da das ::part ein Stil auf Hostebene und kein Stil auf Shadow-Ebene ist. Beispiel:

// inside the shadow DOM:
@keyframes fade-in {
  from { opacity: 0}
}

// This shouldn't work!
// The host style shouldn't know the name "fade-in"
::part(slider) {
  animation-name: fade-in;  
}

So sollte die Regel mit Inline-Styles funktionieren

Im Gegensatz zu ::part sind Inline-Styles mit dem Attribut style oder solche, bei denen der Stil programmatisch mithilfe eines Scripts festgelegt wird, auf den Bereich beschränkt, in dem sich das Element befindet. Das liegt daran, dass Sie zum Anwenden eines Stils auf ein Element Zugriff auf den Element-Handle und damit auf den Schatten-Stamm selbst benötigen.

So funktionieren CSS-Namen und das Shadow-DOM in der Praxis

Obwohl die vorstehenden Regeln klar definiert und konsistent sind, spiegeln die aktuellen Implementierungen dies nicht immer wider. In der Praxis funktioniert @property in allen Browsern konsistent anders als in der Spezifikation. Bei den meisten anderen Funktionen gibt es offene Fehler. Einige davon sind noch nicht veröffentlicht, sodass noch Zeit für die Behebung bleibt.

Um zu testen und zu demonstrieren, wie diese Funktionen in der Praxis funktionieren, haben wir die folgende Seite erstellt: https://css-names-in-the-shadow.glitch.me/. Diese Seite enthält mehrere Iframes, die jeweils auf eine der Funktionen ausgerichtet sind und sechs Szenarien testen:

  • Externer Verweis auf einen externen Namen: Es ist kein Shadow DOM erforderlich. Das sollte funktionieren.
  • Äußerer Verweis auf einen inneren Namen: Dies sollte nicht funktionieren, da der im Schattenkontext definierte Name sonst gehackt wurde.
  • Interner Verweis auf externen Namen: Dies sollte funktionieren, da Namen auf Baumebene von Schattenwurzeln übernommen werden.
  • Interner Verweis auf einen internen Namen: Dies sollte funktionieren, da sich sowohl der Name der Referenz als auch der Name des Elements im selben Gültigkeitsbereich befinden.
  • ::part-Verweis auf den äußeren Namen: Dies sollte funktionieren, da sowohl ::part als auch der Name im selben Gültigkeitsbereich deklariert sind.
  • ::part Referenz auf den inneren Namen: Dies sollte nicht funktionieren, da der äußere Bereich keine Informationen zu Namen erhalten sollte, die im Shadow-DOM deklariert wurden.

@keyframes

Gemäß der Spezifikation sollten Sie in einem Schatten-Root auf ‑Namen von ‑Frames verweisen können, solange sich die @keyframes-At-Rule in einem übergeordneten Gültigkeitsbereich befindet. In der Praxis wird dieses Verhalten von keinem Browser implementiert und die Keyframe-Definitionen können nur im Gültigkeitsbereich referenziert werden, in dem sie definiert sind. Siehe Problem 10540.

@property

Wie in der Spezifikation definiert, werden alle Deklarationen von @property auf den Dokumentumfang heruntergebrochen. Derzeit können Sie @property jedoch nur im Dokumentbereich deklarieren. @property-Deklarationen innerhalb von Schattenwurzeln werden ignoriert.
Sehen Sie sich Problem 10541 an.

Browserspezifische Fehler

Bei den anderen Funktionen ist das Verhalten nicht in allen Browsern gleich:

  • @font-face wird in Safari auf den Stammbereich reduziert.
  • Chromium erlaubt nicht, anchor-name-Regeln in einem Schatten-Stamm zu übernehmen
  • scroll-timeline-name und view-timeline-name sind nicht richtig auf ::part beschränkt (auch nicht in Chromium).
  • Kein Browser erlaubt die Deklaration von @font-palette-values in einem Schatten-Stamm.
  • view-transition-class kann innerhalb eines Schatten-Root-Elements definiert werden (der Übergang selbst befindet sich außerhalb des Schatten-Root-Elements).
  • In Firefox kann ::part auf Namen von inneren Schatten zugreifen (Containerabfragen, ‑keyframes).
  • In Firefox und Safari wird @counter-style in einem Schatten-Root nicht berücksichtigt.

Für counter-reset, counter-set und counter-increment gelten leicht abweichende Regeln, da es sich um implizite Namen handelt. Für die Deklaration von CSS-Eigenschaften gibt es eine etablierte und gut getestete Reihe von Regeln.

Fazit

Die schlechte Nachricht ist, dass der Snapshot des aktuellen Interoperabilitätsstatus in Bezug auf CSS-Namen und das Shadow DOM inkonsistent und fehlerhaft ist. Keine der hier untersuchten Funktionen funktioniert in allen Browsern und gemäß den Spezifikationen einheitlich. Positiv ist, dass die Abweichungen, die für eine einheitliche Nutzung sorgen, auf eine endliche Liste von Fehlern und Spezifikationsproblemen zurückzuführen sind. Lass uns das Problem beheben. In der Zwischenzeit kann Ihnen diese Übersicht hoffentlich helfen, wenn Sie Probleme mit den in diesem Artikel beschriebenen Inkonsistenzen haben.