Erste Schritte mit Stilabfragen

Die Möglichkeit, die Inline-Größe eines übergeordneten Elements und die Werte der Containerabfrageeinheit abzufragen, werden seit Kurzem von allen modernen Browser-Engines unterstützt.

Unterstützte Browser

  • Chrome: 105 <ph type="x-smartling-placeholder">
  • Edge: 105. <ph type="x-smartling-placeholder">
  • Firefox: 110 <ph type="x-smartling-placeholder">
  • Safari: 16. <ph type="x-smartling-placeholder">

Quelle

Die Begrenzungsspezifikation umfasst jedoch mehr als nur Größenabfragen. Es ermöglicht auch die Abfrage der Stilwerte eines übergeordneten Elements. Ab Chromium 111 können Sie Stilbegrenzungen für benutzerdefinierte Eigenschaftswerte anwenden und ein übergeordnetes Element nach dem Wert einer benutzerdefinierten Eigenschaft abfragen.

Unterstützte Browser

  • Chrome: 111. <ph type="x-smartling-placeholder">
  • Edge: 111. <ph type="x-smartling-placeholder">
  • Firefox: nicht unterstützt <ph type="x-smartling-placeholder">
  • Safari: 18. <ph type="x-smartling-placeholder">

Quelle

Das bedeutet, dass wir in CSS noch mehr logische Kontrolle über Stile haben und die Logik und die Datenschicht einer Anwendung besser von ihren Stilen trennen können.

Die Spezifikation des CSS-Begrenzungsmoduls in Ebene 3, in der Größen- und Stilabfragen behandelt werden, ermöglicht es, alle Stile von einem übergeordneten Element abzufragen, einschließlich Eigenschafts- und Wertpaaren wie font-weight: 800. Bei der Einführung dieser Funktion funktionieren Stilabfragen derzeit jedoch nur mit benutzerdefinierten CSS-Eigenschaftswerten. Dies ist immer noch sehr nützlich, um Stile zu kombinieren und Daten vom Design zu trennen. Sehen wir uns an, wie Sie Stilabfragen mit benutzerdefinierten CSS-Eigenschaften verwenden:

Erste Schritte mit Stilabfragen

Angenommen, wir haben folgenden HTML-Code:

<ul class="card-list">
  <li class="card-container">
    <div class="card">
      ...
    </div>
  </li>
</ul>

Wenn Sie Stilabfragen verwenden möchten, müssen Sie zuerst ein Containerelement einrichten. Dies erfordert einen etwas anderen Ansatz, je nachdem, ob Sie eine direkte oder indirekte übergeordnete Person abfragen.

Direkte übergeordnete Elemente abfragen

Diagramm einer Stilabfrage.

Anders als bei Stilabfragen müssen Sie keine Begrenzung mit der Eigenschaft container-type oder container auf .card-container anwenden, damit .card die Stile des direkt übergeordneten Elements abfragen kann. Wir müssen die Stile jedoch (in diesem Fall benutzerdefinierte Eigenschaftswerte) auf einen Container (in diesem Fall .card-container) oder auf ein Element anwenden, das das Element enthält, das im DOM gestaltet werden soll. Wir können keine Stile anwenden, die wir auf das direkte Element, das wir mit dieser Abfrage abfragen, abfragen, da dies zu Endlosschleifen führen könnte.

Um ein übergeordnetes Element direkt abzufragen, können Sie Folgendes schreiben:

/* styling .card based on the value of --theme on .card-container */
@container style(--theme: warm) {
  .card {
    background-color: wheat;
    border-color: brown; 
    ...
  }
}

Sie haben vielleicht bemerkt, dass die Stilabfrage die Abfrage mit style() umschließt. Damit lassen sich Größenwerte von Stilen unterscheiden. Sie können beispielsweise eine Abfrage nach der Breite des Containers als @container (min-width: 200px) { … } schreiben. Dadurch würden Stile angewendet, wenn der übergeordnete Container mindestens 200 px breit ist. min-width kann jedoch auch eine CSS-Eigenschaft sein und der CSS-Wert von min-width kann mithilfe von Stilabfragen abgefragt werden. Deshalb sollten Sie den style()-Wrapper verwenden, um den Unterschied zu verdeutlichen: @container style(min-width: 200px) { … }.

Stile für indirekte übergeordnete Elemente festlegen

Wenn Sie Stile für ein Element abfragen möchten, das kein direktes übergeordnetes Element ist, müssen Sie diesem Element ein container-name zuweisen. Beispielsweise können wir Stile basierend auf den Stilen von .card-list auf .card anwenden, indem wir .card-list ein container-name zuweisen und in der Stilabfrage darauf verweisen.

/* styling .card based on the value of --moreGlobalVar on .card-list */
@container cards style(--moreGlobalVar: value) {
  .card {
    ...
  }
}

Im Allgemeinen empfiehlt es sich, Container Namen zu geben, damit deutlich wird, was abgefragt wird. Außerdem können Sie so leichter auf diese Container zugreifen. Dies ist beispielsweise dann von Vorteil, wenn Sie Elemente innerhalb von .card direkt gestalten möchten. Ohne einen benannten Container in .card-container können sie ihn nicht direkt abfragen.

Aber in der Praxis ist das viel sinnvoller. Sehen wir uns einige Beispiele an:

Stilabfragen in der Praxis

Demobild mit mehreren Produktkarten, teilweise mit Tags „neu“ oder „wenig auf Lager“, und der Karte „Niedrige Lager“ mit rotem Hintergrund.

Stilabfragen sind besonders nützlich, wenn Sie entweder eine wiederverwendbare Komponente mit mehreren Variationen haben oder wenn Sie nicht die Kontrolle über alle Stile haben, aber in bestimmten Fällen Änderungen anwenden müssen. Dieses Beispiel zeigt eine Reihe von Produktkarten mit derselben Kartenkomponente. Einige Produktkarten enthalten zusätzliche Details/Hinweise wie „Neu“ oder „Niedriger Lagerbestand“, die durch eine benutzerdefinierte Eigenschaft namens --detail ausgelöst werden. Wenn ein Produkt „auf Lager“ ist, erhält es einen dunkelroten Rahmen als Hintergrund. Diese Art von Informationen wird wahrscheinlich vom Server gerendert und kann über Inline-Stile auf die Karten angewendet werden. Beispiel:

 <div class="product-list">
  <div class="product-card-container" style="--detail: new">
    <div class="product-card">
      <div class="media">
        <img .../>
      <div class="comment-block"></div>
    </div>
  </div>
  <div class="meta">
    ...
  </div>
  </div>
  <div class="product-card-container" style="--detail: low-stock">
    ...
  </div>
  <div class="product-card-container">
    ...
  </div>
  ...
</div>

Aufgrund dieser strukturierten Daten können Sie Werte an --detail übergeben und diese benutzerdefinierte CSS-Eigenschaft zum Anwenden der Stile verwenden:

@container style(--detail: new) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'New';
    border: 1px solid currentColor;
    background: white;
    ...
  }
}

@container style(--detail: low-stock) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'Low Stock';
    border: 1px solid currentColor;
    background: white;
    ...
  }
  
  .media-img {
    border: 2px solid brickred;
  }
}

Mit dem Code oben können wir einen Chip für --detail: low-stock und --detail: new anwenden. Möglicherweise ist Ihnen jedoch eine gewisse Redundanz im Codeblock aufgefallen. Derzeit gibt es keine Möglichkeit, in einer Abfrage nur das Vorhandensein von --detail mit @container style(--detail) abzufragen. Dies würde eine bessere gemeinsame Nutzung von Stilen und weniger Wiederholungen ermöglichen. Diese Funktion wird derzeit in der Arbeitsgruppe diskutiert.

Wetterkarten

Im vorherigen Beispiel wurde eine einzelne benutzerdefinierte Eigenschaft mit mehreren möglichen Werten zum Anwenden von Stilen verwendet. Sie können dies jedoch auch kombinieren, indem Sie mehrere benutzerdefinierte Eigenschaften verwenden und abfragen. Hier ein Beispiel für eine Wetterkarte:

Demo der Wetterkarten

Um die Farbverläufe und Symbole für den Hintergrund dieser Karten zu gestalten, suchen Sie nach Wettermerkmalen wie „bewölkt“, „regnerisch“ oder „sonnig“:

@container style(--sunny: true) {
  .weather-card {
    background: linear-gradient(-30deg, yellow, orange);
  }
  
  .weather-card:after {
    content: url(<data-uri-for-demo-brevity>);
    background: gold;
  }
}

So können Sie jede Karte basierend auf ihren individuellen Merkmalen gestalten. Sie können aber auch Stile für charakteristische Kombinationen (benutzerdefinierte Eigenschaften) erstellen. Verwenden Sie dazu den Kombinator and auf dieselbe Weise wie für Medienabfragen. Ein Tag, an dem es bewölkt und sonnig ist, würde zum Beispiel wie folgt aussehen:

@container style(--sunny: true) and style(--cloudy: true) {
    .weather-card {
      background: linear-gradient(24deg, pink, violet);
    }
  
  .weather-card:after {
      content: url(<data-uri-for-demo-brevity>);
      background: violet;
  }
}

Daten vom Design trennen

In beiden Demos hat es einen strukturellen Vorteil, die Datenschicht (DOM, das auf der Seite gerendert wird) von den angewendeten Stilen zu trennen. Die Stile werden als mögliche Varianten geschrieben, die innerhalb des Komponentenstils enthalten sind. Ein Endpunkt könnte hingegen die Daten senden, die er dann zum Gestalten der Komponente verwenden würde. Sie können einen einzelnen Wert verwenden, wie im ersten Fall den --detail-Wert aktualisieren, oder mehrere Variablen, wie im zweiten Fall --rainy, --cloudy oder --sunny. Und das Beste ist, dass Sie diese Werte auch kombinieren können. Wenn Sie sowohl --sunny als auch --cloudy prüfen, könnte der Stil teilweise bewölkt sein.

Die Werte benutzerdefinierter Eigenschaften über JavaScript können nahtlos aktualisiert werden, entweder während der Einrichtung des DOM-Modells (d.h. beim Erstellen der Komponente in einem Framework) oder jederzeit mit <parentElem>.style.setProperty('--myProperty’, <value>). I

In dieser Demo wird mit ein paar Zeilen Code der --theme einer Schaltfläche aktualisiert und Stile mithilfe von Stilabfragen und dieser benutzerdefinierten Eigenschaft (--theme) angewendet:

Gestalten Sie die Karte mithilfe von Stilabfragen. Das JavaScript, das zum Aktualisieren der Werte der benutzerdefinierten Eigenschaften verwendet wird, lautet wie folgt:

const themePicker = document.querySelector('#theme-picker')
const btnParent = document.querySelector('.btn-section');

themePicker.addEventListener('input', (e) => {
  btnParent.style.setProperty('--theme', e.target.value);
})

Die in diesem Artikel beschriebenen Funktionen sind erst der Anfang. Mit Containerabfragen können Sie dynamische, responsive Oberflächen erstellen. Insbesondere bei Stilabfragen gibt es noch einige offene Probleme. Eine davon ist die Implementierung von Stilabfragen für CSS-Stile, die über benutzerdefinierte Eigenschaften hinausgehen. Diese Funktion ist bereits Teil der aktuellen Spezifikationsebene, wurde aber noch in keinem Browser implementiert. Die boolesche Kontextbewertung wird der aktuellen Spezifikationsebene hinzugefügt, wenn das ausstehende Problem behoben ist. Eine Bereichsabfrage ist für die nächste Ebene der Spezifikation geplant.