Erste Schritte mit Stilabfragen

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

Browser Support

  • Chrome: 105.
  • Edge: 105.
  • Firefox: 110.
  • Safari: 16.

Source

Die Begrenzungsspezifikation umfasst jedoch nicht nur Abfragen nach Größe, sondern auch nach Stilwerten eines übergeordneten Elements. Ab Chromium 111 können Sie Stileinschränkungen auf Werte benutzerdefinierter Properties anwenden und ein übergeordnetes Element nach dem Wert einer benutzerdefinierten Property abfragen.

Browser Support

  • Chrome: 111.
  • Edge: 111.
  • Firefox: not supported.
  • Safari: 18.

Source

So haben wir noch mehr logische Kontrolle über Stile in CSS und können die Logik und Datenebene einer Anwendung besser von den Stilen trennen.

Die CSS-Spezifikation für das Begrenzungsmodul der Ebene 3, die Abfragen zu Größe und Stil abdeckt, ermöglicht es, alle Stile von einem übergeordneten Element abzufragen, einschließlich Attribut- und Wertpaaren wie font-weight: 800. Bei der Einführung dieser Funktion funktionieren Stilabfragen jedoch derzeit nur mit Werten für benutzerdefinierte CSS-Eigenschaften. Das 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 das folgende HTML-Element:

<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. Je nachdem, ob Sie ein direktes oder indirektes übergeordnetes Element abfragen, ist ein etwas anderer Ansatz erforderlich.

Direkte übergeordnete Elemente abfragen

Diagramm einer Stilabfrage.

Anders als bei Stilabfragen müssen Sie .card-container nicht mithilfe der Eigenschaft container-type oder container einschränken, damit .card die Stile seines direkten übergeordneten Elements abfragen kann. Wir müssen die Stile (in diesem Fall Werte für benutzerdefinierte Properties) jedoch auf einen Container (in diesem Fall .card-container) oder ein Element anwenden, das das Element enthält, für das wir im DOM ein Styling vornehmen. Wir können die Stile, die wir abfragen, nicht auf das direkte Element anwenden, das wir mit dieser Abfrage stylen, da dies zu einer unendlichen Schleife führen kann.

Wenn Sie ein übergeordnetes Element direkt abfragen möchten, können Sie Folgendes eingeben:

/* 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 in style() einschließt. So lassen sich Größenwerte von Stilen unterscheiden. Sie können beispielsweise eine Abfrage für die Breite des Containers als @container (min-width: 200px) { … } schreiben. Die Stile werden angewendet, wenn der übergeordnete Container mindestens 200 Pixel breit ist. min-width kann aber auch eine CSS-Eigenschaft sein. Mit Styleabfragen können Sie den CSS-Wert von min-width abfragen. Deshalb sollten Sie den style()-Wrapper verwenden, um den Unterschied deutlich zu machen: @container style(min-width: 200px) { … }.

Indirekte übergeordnete Elemente stylen

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. So können wir beispielsweise Stile auf .card basierend auf den Stilen von .card-list anwenden, indem wir .card-list eine 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 {
    ...
  }
}

Es ist im Allgemeinen eine Best Practice, Ihren Containern Namen zu geben, damit klar ist, was Sie abfragen, und damit Sie einfacher auf diese Container zugreifen können. Das ist beispielsweise praktisch, wenn Sie Elemente direkt in .card stylen möchten. Ohne einen benannten Container auf .card-container können sie ihn nicht direkt abfragen.

In der Praxis macht das aber viel mehr Sinn. Sehen wir uns einige Beispiele an:

Stilabfragen in Aktion

Demobild mit mehreren Produktkarten, einige mit den Tags „Neu“ oder „Begrenzte Stückzahl“ und die Karte „Begrenzte Stückzahl“ mit einem roten Hintergrund.

Stilabfragen sind besonders nützlich, wenn Sie entweder eine wiederverwendbare Komponente mit mehreren Varianten haben oder wenn Sie nicht über alle Stile die Kontrolle haben, aber in bestimmten Fällen Änderungen vornehmen müssen. In diesem Beispiel sind mehrere Produktkarten zu sehen, die dieselbe Kartenkomponente verwenden. Einige Produktkarten enthalten zusätzliche Details/Hinweise wie „Neu“ oder „Begrenzte Stückzahl“, die durch eine benutzerdefinierte Property namens --detail ausgelöst werden. Wenn ein Produkt nur noch „wenig auf Lager“ ist, wird es außerdem mit einem dunkelroten Rahmen hervorgehoben. Diese Art von Informationen wird wahrscheinlich auf dem Server gerendert und kann über Inline-Styles auf die Karten angewendet werden, z. B. so:

 <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>

Anhand dieser strukturierten Daten können Sie Werte an --detail übergeben und diese benutzerdefinierte CSS-Eigenschaft verwenden, um die Stile anzuwenden:

@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 aber aufgefallen, dass der Codeblock redundant ist. Derzeit gibt es keine Möglichkeit, nur nach der Anwesenheit von --detail mit @container style(--detail) zu fragen. Das würde eine bessere Weitergabe von Stilen und weniger Wiederholungen ermöglichen. Diese Funktion wird derzeit in der Arbeitsgruppe diskutiert.

Wetterkarten

Im vorherigen Beispiel wurde eine einzelne benutzerdefinierte Property mit mehreren möglichen Werten verwendet, um Stile anzuwenden. Sie können aber auch mehrere benutzerdefinierte Properties verwenden und abfragen. Hier ein Beispiel für eine Wetterkarte:

Demo für Wetterkarten

Um die Hintergrundverläufe und Symbole für diese Karten zu gestalten, suchen Sie nach Wettereigenschaften 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 anhand ihrer individuellen Merkmale gestalten. Sie können aber auch Stile für Kombinationen von Merkmalen (benutzerdefinierte Eigenschaften) festlegen, indem Sie den Kombinator and auf die gleiche Weise wie bei Medienabfragen verwenden. Ein Tag, der sowohl bewölkt als auch sonnig ist, würde beispielsweise so 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 besteht ein struktureller Vorteil darin, die Datenebene (das DOM, das auf der Seite gerendert wird) von den angewendeten Stilen zu trennen. Die Stile werden als mögliche Varianten geschrieben, die im Komponentenstil vorhanden sind, während ein Endpunkt die Daten senden könnte, die dann zum Stilisieren der Komponente verwendet werden. Sie können einen einzelnen Wert verwenden, z. B. im ersten Fall, indem Sie den --detail-Wert aktualisieren, oder mehrere Variablen, z. B. im zweiten Fall, indem Sie entweder --rainy oder --cloudy oder --sunny festlegen. Das Beste daran ist, dass Sie diese Werte auch kombinieren können. Wenn Sie sowohl nach --sunny als auch nach --cloudy suchen, wird ein Stil für bewölkten Himmel angezeigt.

Die Aktualisierung von benutzerdefinierten Property-Werten über JavaScript kann nahtlos erfolgen, entweder beim Einrichten 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 wenigen Codezeilen der --theme einer Schaltfläche aktualisiert und Stile mithilfe von Stilabfragen und dieser benutzerdefinierten Property (--theme) angewendet:

Formatieren Sie die Karte mithilfe von Stilabfragen. Das JavaScript, das zum Aktualisieren der Werte der benutzerdefinierten Property verwendet wird, lautet:

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 nur der Anfang. Containerabfragen werden in Zukunft noch mehr Möglichkeiten bieten, dynamische, responsive Benutzeroberflächen zu erstellen. Bei Stilanfragen 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, aber noch nicht in einem Browser implementiert. Die boolesche Kontextbewertung wird voraussichtlich der aktuellen Spezifikationsebene hinzugefügt, sobald das ausstehende Problem behoben ist. Die Bereichsanfrage ist für die nächste Ebene der Spezifikation geplant.