Hintergrund und Übersicht
Häufig muss ein Autor kontextbezogene Änderungen am Design einer Komponente vornehmen. In der Vergangenheit mussten Autoren Selektoren verwenden, um solche Änderungen anzuwenden. Das bedeutete oft, dass viele ihrer Design-Tokens zwar als benutzerdefinierte Eigenschaften existieren konnten, Design-Tokens höherer Ordnung jedoch nur als Selektormuster (z.B. mit einem Klassennamen oder einer Attributkonvention) oder als Props/Kontext in einem JavaScript-Framework codiert werden konnten.
Mit Container-Stilabfragen können Autoren Elemente basierend auf dem berechneten Wert einer benutzerdefinierten Eigenschaft eines übergeordneten Elements formatieren. Das bedeutet, dass Autoren aussagekräftige Design-Token-Werte in ihren Stylesheets schreiben können, anstatt sich für die Darstellung auf Markup oder JavaScript zu verlassen.
Implementierung
Die Implementierung eines reaktiven Design-Tokens mithilfe einer Container-Stilabfrage ist recht einfach:
- Legen Sie das Design-Token höherer Ordnung als benutzerdefinierte Eigenschaft für einen Container fest. Dies muss keine registrierte benutzerdefinierte Eigenschaft sein.
- Verwenden Sie die Regel
@container style(), um den Wert dieser benutzerdefinierten Eigenschaft abzufragen. - Wenden Sie die entsprechenden Stile auf ein untergeordnetes Element im Container an.
Beachten Sie bitte Folgendes:
- Der Container, der mit einer Stilabfrage abgefragt wird, muss nicht
container-typeodercontainer-nameenthalten. Mitcontainer-namekönnen jedoch spezifischere Abfragen durchgeführt werden. - Der Container selbst kann nicht über die Containerstilabfrage formatiert werden.
Das folgende Beispiel zeigt die oben beschriebenen Implementierungsschritte.
<div class="features">
<div class="card"></div>
<div class="card"></div>
</div>
<div class="bugs">
<div class="card"></div>
<div class="card"></div>
<div class="card"></div>
<div class="card"></div>
</div>
.features {
--density: spacious;
}
.bugs {
--density: compact;
}
@container style(--density: compact) {
.card {
padding: 8px;
}
}
@container style(--density: spacious) {
.card {
padding: 24px;
}
}
Fallback-Strategien
Bis es eine Baseline-Unterstützung für Containerstil-Abfragen gibt, wird NICHT EMPFOHLEN, sie für Hauptfunktionen zu verwenden, die in allen Browsern verfügbar sein müssen. Es ist nämlich nicht einfach, einen Fallback für sie zu erstellen, der ihre Vorteile nicht beeinträchtigt oder der nicht eigene Einschränkungen hat. Wenn beispielsweise die UI-Dichte nicht als Hauptfunktion gilt, die in allen Umgebungen verfügbar sein muss, können Container-Stilabfragen verwendet werden, um die Funktion ohne Fallback zu implementieren.
Selektoren verwenden
Für Hauptfunktionen sollte ein alternativer Ansatz mit Selektoren verwendet werden. In diesem Beispiel wird ein data-density-Attribut verwendet, um das Design-Token für die Dichte im Markup zu codieren, anstatt es als benutzerdefinierte Eigenschaft zu definieren:
<div class="features" data-density="spacious">
<div class="card"></div>
<div class="card"></div>
</div>
<div class="bugs" data-density="compact">
<div class="card"></div>
<div class="card"></div>
<div class="card"></div>
<div class="card"></div>
</div>
/* This example uses `:where()` to avoid increasing specificity */
:where([data-density="compact"]) .card {
padding: var(--card-padding-compact);
}
:where([data-density="spacious"]) .card {
padding: var(--card-padding-spacious);
}
Eine wesentliche Einschränkung dieses Fallback-Ansatzes besteht darin, dass keine verschachtelten Elemente mit dem Attribut data-density unterstützt werden. Da die Selektorspezifität gleich ist, wird die Reihenfolge des Auftretens verwendet, um die Stile zu bestimmen (d. h. [data-density="spacious"] hat immer Vorrang vor [data-density="compact"]).
Stilabfragen als progressive Verbesserung verwenden
Es wird NICHT EMPFOHLEN, Stilabfragen als progressive Verbesserung für eine Hauptfunktion zu verwenden. Wenn Sie dies dennoch tun möchten, können Sie benutzerdefinierte Eigenschaften erstellen und dann die Stilabfragen einfügen, um Duplikate zu vermeiden. Achten Sie darauf, dass beim Fallback-Ansatz :where() verwendet wird, wenn die Containerelemente ausgewählt werden, um die Spezifität nicht zu erhöhen.
.card {
--card-padding-compact: 8px;
--card-padding-spacious: 24px;
}
:where([data-density="compact"]) .card {
padding: var(--card-padding-compact);
}
:where([data-density="spacious"]) .card {
padding: var(--card-padding-spacious);
}
/* Use style queries as a progressive enhancement: same specificity, so order of appearance is used */
@container style(--density: compact) {
.card {
padding: var(--card-padding-compact);
}
}
@container style(--density: spacious) {
.card {
padding: var(--card-padding-spacious);
}
}
Funktionsprüfung nur mit CSS
Wenn Sie Container-Stilabfragen prüfen müssen, um bestimmte UI-Elemente, die davon abhängen, bedingt anzuzeigen, können Sie dazu eine Stilabfrage verwenden:
:root {
--style-queries-supported: check;
}
.density-toggle {
display: none;
}
@container style(--style-queries-supported) {
.density-toggle {
display: revert;
}
}
Funktionsprüfung mit JavaScript
Die Funktionsprüfung mit JavaScript ist etwas komplizierter, da die CSSContainerRule-Schnittstelle bereits vor dem Hinzufügen von Container-Stilabfragen vorhanden war und daher für diesen Zweck unzuverlässig ist. Stattdessen müssen Sie den berechneten Wert einer bekannten Eigenschaft prüfen, die mit einer Stilabfrage festgelegt wird.
In diesem Beispiel wird eine benutzerdefinierte Eigenschaft verwendet, da sie keine visuelle Auswirkung hat:
:root {
--style-queries-supported: check;
}
@container style(--style-queries-supported: check) {
body {
--style-queries-supported: yes;
}
}
Prüfen Sie dann den berechneten Wert in JavaScript so:
if (getComputedStyle(document.body).getPropertyValue("--style-queries-supported") === "yes") {
// Use container style queries
} else {
// Use fallback strategy
}