Erste Schritte mit Stilabfragen

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

Browser Support

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

Source

Die Containment-Spezifikation umfasst jedoch mehr als nur Größenabfragen. Sie ermöglicht auch das Abfragen von Stilwerten eines übergeordneten Elements. Ab Chromium 111 können Sie Style-Containment für benutzerdefinierte Eigenschaftswerte anwenden und ein übergeordnetes Element nach dem Wert einer benutzerdefinierten Eigenschaft abfragen.

Browser Support

  • Chrome: 111.
  • Edge: 111.
  • Firefox: 151.
  • Safari: 18.

Das bedeutet, dass wir noch mehr logische Kontrolle über Stile in CSS haben und die Logik- und Datenschicht einer Anwendung besser von ihren Stilen getrennt werden kann.

Die Spezifikation für das CSS Containment Module Level 3, die Größen- und Stilanfragen abdeckt, ermöglicht es, beliebige Stile von einem übergeordneten Element abzufragen, einschließlich Eigenschafts- und Wertepaaren wie font-weight: 800. Bei der Einführung dieser Funktion funktionieren Stilanfragen derzeit jedoch nur mit benutzerdefinierten CSS-Eigenschaftswerten. Das ist trotzdem sehr nützlich, um Stile zu kombinieren und Daten vom Design zu trennen. Sehen wir uns an, wie Sie Stilanfragen mit benutzerdefinierten CSS-Eigenschaften verwenden:

Erste Schritte mit Stilabfragen

Angenommen, wir haben den 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. Dafür ist ein etwas anderer Ansatz erforderlich, je nachdem, ob Sie eine direkte oder indirekte übergeordnete Einheit abfragen.

Direkte übergeordnete Elemente abfragen

Diagramm einer Stilabfrage

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

Wenn Sie ein übergeordnetes Element direkt abfragen möchten, 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; 
    ...
  }
}

Vielleicht ist Ihnen aufgefallen, dass die Stilabfrage die Abfrage mit style() umschließt. So werden Größenwerte von Stilen unterschieden. Sie können beispielsweise eine Abfrage für die Breite des Containers als @container (min-width: 200px) { … } schreiben. Stile werden angewendet, wenn der übergeordnete Container mindestens 200 Pixel breit ist. min-width kann aber auch eine CSS-Eigenschaft sein. In diesem Fall können Sie den CSS-Wert von min-width mit Stilabfragen abfragen. Deshalb verwenden Sie den Wrapper style(), um den Unterschied deutlich zu machen: @container style(min-width: 200px) { … }.

Formatieren von nicht direkten übergeordneten Elementen

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. Wir können beispielsweise Stile auf .card anwenden, die auf den Stilen von .card-list basieren, indem wir .card-list ein container-name geben und in der Stilanfrage darauf verweisen.

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

Es ist generell empfehlenswert, Containern Namen zu geben, damit klar ist, was Sie abfragen, und Sie leichter auf diese Container zugreifen können. Das ist beispielsweise nützlich, wenn Sie Elemente in .card direkt gestalten möchten. Ohne einen benannten Container auf .card-container können sie ihn nicht direkt abfragen.

In der Praxis wird das alles aber viel deutlicher. Sehen wir uns einige Beispiele an:

Stilabfragen in Aktion

Demobild mit mehreren Produktkarten, einige mit den Tags „Neu“ oder „Niedriger Lagerbestand“ und die Karte mit dem Tag „Niedriger Lagerbestand“ mit rotem Hintergrund.

Stilabfragen sind besonders nützlich, wenn Sie entweder eine wiederverwendbare Komponente mit mehreren Varianten haben oder wenn Sie nicht alle Ihre Stile steuern können, aber in bestimmten Fällen Änderungen vornehmen müssen. In diesem Beispiel sehen Sie eine Reihe von Produktkarten, die dieselbe Kartenkomponente verwenden. 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 „Begrenzt auf Lager“ ist, erhält es außerdem einen dunkelroten Rahmen. Diese Art von Informationen wird wahrscheinlich serverseitig gerendert und kann über Inline-Stile auf die Karten angewendet werden:

 <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 oben stehenden Code können wir einen Chip für --detail: low-stock und --detail: new anwenden. Möglicherweise haben Sie jedoch eine gewisse Redundanz im Codeblock bemerkt. Derzeit ist es nicht möglich, nur das Vorhandensein von --detail mit @container style(--detail) abzufragen. Dadurch könnten Stile besser geteilt und Wiederholungen vermieden werden. Diese Funktion wird derzeit in der Arbeitsgruppe diskutiert.

Wetterkarten

Im vorherigen Beispiel wurde eine einzelne benutzerdefinierte Eigenschaft 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 zu Wetterkarten.

Um die Hintergrundverläufe und Symbole für diese 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 entsprechend ihren individuellen Merkmalen gestalten. Sie können aber auch Kombinationen von Merkmalen (benutzerdefinierte Eigenschaften) gestalten, indem Sie den Kombinator and auf dieselbe Weise wie für Media-Anfragen verwenden. Ein Tag, an dem es 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 bietet 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 innerhalb des Komponentenstils geschrieben, während ein Endpunkt die Daten senden könnte, die dann zum Formatieren der Komponente verwendet werden. Sie können einen einzelnen Wert verwenden, wie im ersten Fall, in dem der --detail-Wert aktualisiert wird, oder mehrere Variablen, wie im zweiten Fall, in dem entweder --rainy, --cloudy oder --sunny festgelegt wird. Das Beste daran ist, dass Sie diese Werte auch kombinieren können. Wenn Sie sowohl nach --sunny als auch nach --cloudy suchen, wird möglicherweise ein teilweise bewölkter Stil angezeigt.

Das Aktualisieren von benutzerdefinierten Eigenschaftswerten über JavaScript kann nahtlos erfolgen, entweder beim Einrichten des DOM-Modells (d.h. beim Erstellen der Komponente in einem Framework) oder jederzeit über <parentElem>.style.setProperty('--myProperty’, <value>). I

In dieser Demo wird in wenigen Codezeilen die --theme eines Buttons aktualisiert und es werden Stile mithilfe von Stilabfragen und der benutzerdefinierten Eigenschaft (--theme) angewendet:

So gestalten Sie die Karte mit Stilabfragen. Das JavaScript, das zum Aktualisieren der Werte der benutzerdefinierten Eigenschaft 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. Es wird noch mehr Containerabfragen geben, mit denen Sie dynamische, responsive Benutzeroberflächen erstellen können. Was speziell Stilabfragen betrifft, gibt es noch einige offene Probleme. Eines ist die Implementierung von Stilabfragen für CSS-Stile, die über benutzerdefinierte Eigenschaften hinausgehen. Dies ist bereits Teil der aktuellen Spezifikationsebene, aber noch nicht in Browsern implementiert. Die boolesche Kontextauswertung wird voraussichtlich der aktuellen Spezifikationsebene hinzugefügt, wenn das ausstehende Problem behoben ist. Bereichsabfragen sind für die nächste Ebene der Spezifikation geplant.