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 stabil unterstützt.

Unterstützte Browser

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

Quelle

Die Begrenzungsspezifikation umfasst jedoch nicht nur Abfragen nach Größe, sondern auch nach Stilwerten 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.
  • Edge: 111.
  • Firefox: nicht unterstützt
  • Safari: 18.

Quelle

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 Spezifikation des CSS-Begrenzungsmoduls 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 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 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 (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 nach der 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 eine 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 leichter 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 kann er nicht direkt abgefragt werden.

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 anwenden müssen. In diesem Beispiel sind mehrere Produktkarten zu sehen, die dieselbe Kartenkomponente verwenden. Einige Produktkarten enthalten zusätzliche Details/Hinweise wie „Neu“ oder „Auf Lager“, die durch eine benutzerdefinierte Eigenschaft namens --detail ausgelöst werden. Wenn ein Produkt nur noch „wenig auf Lager“ ist, wird außerdem ein dunkelroter Rahmen angezeigt. Diese Art von Informationen wird wahrscheinlich auf dem Server gerendert und kann über Inline-Stile 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 mit dieser benutzerdefinierten CSS-Eigenschaft die Stile anwenden:

@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 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 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 basierend auf ihren individuellen Merkmalen 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. Beispiel: Ein Tag, an dem es bewölkt und sonnig ist, würde 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 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, mit dem die Werte der benutzerdefinierten Properties aktualisiert werden, 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 erst der Anfang. Mit Containerabfragen können Sie dynamische, responsive Oberflächen 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.