SELF-Treffen in Mainz 2023

Die Mitgliederversammlung findet am 25.11.2023 um 10:00 statt; davor und danach gibt es Gelegenheiten zum gemütlichen Beisammensein.
Weitere Informationen und eine Anmeldemöglichkeit gibt es in der Veranstaltungs-Ankündigung.

CSS/Media Queries/Container Queries

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Während Medienabfragen eine Methode zur Abfrage von Aspekten des Browsers oder der Geräteumgebung bieten, in der ein Dokument angezeigt wird (z. B. Abmessungen des Viewports oder Benutzereinstellungen), ermöglichen Containerabfragen das Testen von Aspekten von Elementen innerhalb des Dokuments (z. B. Rahmenabmessungen oder berechnete Stile).

Ein Abfragecontainer wird durch die Angabe der möglichen Abfragetypen unter Verwendung der Eigenschaft container-type (oder der shorthand container) erstellt. Stilregeln, die auf seine Nachkommen angewendet werden, können dann durch Abfragen mit der bedingten Gruppenregel @container konditioniert werden.

Einsatz

Bei der Erstellung eines responsiven Designs verwenden Sie häufig eine Medienabfrage, um das Layout des Dokuments auf der Grundlage der Viewportbreite zu ändern.

Viele Designs haben jedoch gemeinsame Komponenten, die das Layout in Abhängigkeit von der verfügbaren Breite ihres Containers ändern. Dies muss sich nicht immer auf den Viewport beziehen, sondern auch darauf, wo im Layout die Komponente platziert ist.[1]

Infografik von Una Kravets, https://developer.chrome.com/blog/cq-polyfill/
Infografik von Una Kravets
Mit media queries müssen die cards jeweils einzeln gestylt werden; mit container queries reicht eine Festlegung!

Anwendungsbeispiel

Cards sind eines der Entwurfsmuster, die für Container Queries geradezu prädestiniert scheinen. Sie sollen sich nicht nur responsiv über die Seitenbreite verteilen; sondern eben auch je nach verfügbarem Platz ihr Aussehen ändern.

Infografik, die 3 Container in verschiedenen Layouts zeigt

container-type

Um Container-Abfragen zu verwenden, müssen Sie zunächst ein Wrapper-Element festlegen. Durch Hinzufügen der container-type-Eigenschaft mit einem Größenwert wird ein containment-context für dieses Eltern-Element erstellt. Das bedeutet, dass der Browser weiß, dass wir diesen Container später abfragen wollen. Sie können dann eine Abfrage schreiben, die sich auf diesen Containment-Kontext bezieht und nicht auf die Viewport-Größe, um Layout-Entscheidungen für eine Komponente zu treffen.

Eine Container-Abfrage wird mit @container erstellt. Damit wird der nächstgelegene containment-context abgefragt. Um die Karte nur dann zweispaltig darzustellen, wenn die Seitenleiste breiter als 40em ist, verwenden wir das folgende CSS:

container-type
.card-container {
  container-type: inline-size;
}

.card {
}

@container (min-width: 40em){
  .card {
    display: grid;
    grid-template-columns: 2fr 1fr;
  }
}

Das Container-Parent mit der Klasse .card-container erhält mit container-type den Wert inline-size.

Mit .card erhält die Karte selbst (hier nicht dargestellte) Festlegungen.

Mit @container wird nun bei einer Mindestbreite von 40em ein grid erstellt.

container-name

Problematisch wird dies, wenn Sie auf einer Seite mehrere Container erstellen wollen. Hier empfiehlt es sich, dem Eltern- oder Wrapper-Element mit Eigenschaft einen Namen zu geben:

container-name zur festen Zuordnung
.card-container {
  container-type: inline-size;
  container-name: card;
}

@container card (min-width: 40em){
  .card {
    display: grid;
    grid-template-columns: 2fr 1fr;
  }
}

Mit dem in container-name festgelegten Namen card kann die @container-Abfrage fest mit dem Parent verknüpft werden. Dies schafft Übersicht!

container

container ist die zusammenfassende Eigenschaft von container-type und container-name:

container als zusammenfassende Eigenschaft
.card-container {
  container: card / inline-size;
}

@container card (width > 40em){
  .card {
    display: grid;
    grid-template-columns: 2fr 1fr;
  }
}

Die container-Eigenschaft erhält als Werte die durch ein / getrennten Namen und Schlüsselwert.

Ist Ihnen aufgefallen, dass die Abfrage kürzer als gewohnt ist? In Stufe 4 der Media Queries Spezifikation ist eine für Programmierer ansprechendere Schreibweise hinzugekommen.

Mehrspaltiges Layout mit CSS Container Queries

In diesen zwei Beispielen soll nun die Funktionsweise gezeigt werden; zuerst mit nur einer Karte, später als komplexeres Beispiel:

1. HTML-Struktur von card und container
<article class="card-container">

  <div class="card">
      <h3 class="card__title">Karte</h3>
      <div class="content">
        <p>...</p>
      </div>
      <img src="Landscape.svg" at="Landschaft mit Berg und Kirche als Symbolbild">
  </div>

</article>

Als wrapper-Element verwenden wir ein article-Element, das eine passende Klasse erhält.

Unsere Karte besteht aus einem div mit einer h3-Überschrift, einem Inhaltsbereich (.content) und einem Bild.

2. CSS (Container Query mit @container ansehen …
.card-container {
    container: card/ inline-size;
}

@container card (width >= 30em) {
  .card-container .card {
	  --color: pink;
    grid-template-columns: 33% 1fr;
    grid-template-areas: "image title"
						 "image content";
	gap: 1em;
  }
	
  .card-container .card__title {
    grid-area: title;
  }	
  
  .card-container .card p {
    grid-area: content;
  }
  
  .card-container .card img {
    grid-area: image;		
  }
}

@container (width >= 50em) {
  .card-container .card {
	  --color: skyblue;
    grid-template-columns: 50% 12em 1fr;
    grid-template-areas: "image title content";
  }
}

.card {
    display: grid;	
}

Die Karte befindet sich im Container, den Sie mit der Maus größer oder kleiner ziehen können. Sobald die Containerabfrage greift, wird das Grid Layout aufgebaut und die Inhalte nebeneinander dargestellt. Zusätzlich wird die Hintergrundfarbe geändert.


Diese Karte soll nun in verschiedenen Containern verwendet werden. Es so aber weiterhin eine CSS-Festlegung „für ae Gelegenheiten geben“:

3. komplexe Seite mit mehreren Containern ansehen …
<article> 
  <div class="card-container">
    <div class="card">
      ...
    </div>
  </div>

  <div class="card-container">
    <div class="card">
      ...
    </div>
 </div>
 ...
</article>

<aside class="card-container">
    <div class="card">
      ...
    </div>
</aside>

<footer class="card-container">
    <div class="card">
      ...
    </div>
</footer
Empfehlung: Öffnen Sie das Beispiel unter Vorschau in einem neuen Tab, damit sie es in einem großen Viewport sehen.

Das CSS für den article verteilt die Karten nun über den verfügbaren Platz:

3.a. artice mit Flexbox ansehen …
article {
  display: flex;
  flex-wrap: wrap;
  gap: 1em;
}

article .card-container {
  flex: 1 0 30%;
  gap: 1em;
}
article .card {
	min-height: 14em;
}

Die .card-container verteilen sich mit flex auf den verfügbaren Platz. Für schmale Viewports müsste man eine weitere Medienabfrage mit weniger SPaltne einbauen.

Zwischenfazit:

Ich bin nicht ganz zufrieden mit dem Ergebnis.

Ursprünglich wollte ich die cards im article mit grid-template-columns: repeat(auto-fit, minmax(min(12em, 100%), 1fr)); verteilen. Dabei bemerkte ich aber dass die Containerabfrage die Breite des Elternelements und nicht, wie von mir erwünscht - des vom Grid erzeugten Rasterfelds abfragt. Eigentlich logisch, erfordert hier aber die Anage eines Wrapper-Elements um jede einzelne Karte.

Andererseits wollte ich den verfügbaren Patz auf die Karten aufteilen, so dass ich mit der Flexbox-Lösung besser fahre. Für schmale Viewports wäre hier eine weitere media query nötig, was ich neben den wrapper-Elementen eigentlich vermeiden wollte.

Durch die Wrapper-Elemente haben die einzelnen Karten je nach Inhalt unterschiedliche Höhen - etwas, was mit Grid auch automatisch ausgeglichen würde. Ich würde dieses Beispiel am liebsten mit einem normalen Grid und einer eigenen Festlegung für Karten in einem Grid ohne Container Queries bauen. In der Zukunft finden sich hier vielleicht neue Ansätze.

--Matthias Scharwies (Diskussion) 06:50, 4. Aug. 2023 (CEST)

Container-Query-Einheiten

Das CSS Containment Module enthält neben den oben besprochenen Eigenschaften auch zahlreiche neue Einheiten. Container-Query-Längeneinheiten geben eine Länge relativ zu den Abmessungen eines Abfragecontainers an. Komponenten, die Längeneinheiten relativ zu ihrem Container verwenden, können flexibler in verschiedenen Containern verwendet werden, ohne dass konkrete Längenwerte neu berechnet werden müssen.[2][3]

Wer mit den Einheiten vw, vh etc. für Viewports bereits vertraut ist, wird hier wenig Startschwierigkeiten haben. Wenn sie sich innerhalb eines Containers befinden, werden die Containerabmessungen anstelle der Viewport-Abmessungen verwendet. Wenn sie sich nicht innerhalb eines Containers befinden, verwenden sie Werte der Viewport-Abmessungen.

Ein Vergleich mit „normalen“ relativen rems zeigt die Ersparnis:

.card__title {
    font-size: 1rem;
}

/* The horizontal style, v1 */
@container (min-width: 400px) {
    .card__title {
        font-size: 1.15rem;
    }
}

/* The horizontal style, v2 */
@container (min-width: 600px) {
    .card__title {
        font-size: 1.25rem;
    }
}

/* The hero style */
@container (min-width: 800px) {
    .card__title {
        font-size: 2rem;    
    }
}
.card__title {
    font-size: clamp(1rem, 3cqw, 2rem);
}

Im linken Beispiel wird die Schriftgröße von .card__title mit 1rem festgesetzt und dann durch mehrere Media queries passend zur Viewportgröße festgelegt. Auf der rechten Seite wird dies durch eine einzige Deklaration erreicht. Die Schriftgröße wird mit einer clamp()-Funktion mit cqw auf 3% der Containerbreite festgelegt, minimal 1rem, maximal aber 2rem.[4]

Schriftgröße abhängig vom Container ändern

Oft möchte man die Überschrift über die gesamte Breite laufen lassen. Im pixelgenauen Layout kann so etwas mit Magic Numbers fest eingestellt werden. Jede Änderung der Breite zerschießt dann das Layout. Hier kann die Schriftgröße fließend „mitwachsen“:

fluide Typographie: mitwachsende Schriftgrößen ansehen …
<div class="container">
  <div class="card"></div>
</div>
 
.container {
    container-type: inline-size;
}

.card {
  font-size: clamp(1em, 18cqi, 5em);
}

Als Wert für font-size wird in der CSS-Funktion clamp() eine variable Schriftgröße mit einer Mindest- und einer Maximalgröße angegeben. Die mittlere Angabe in clamp() legt den Orientierungspunkt fest. Mit 18cqi gestalten wir eine dynamische Schriftgröße auf Grundlage von 18% der Inline-Größe des Containers.

Polyfill

Mittlerweile (Stand Mai 2023) werden Container Queries von den letzten Versionen der Browser (Anteil: ca. 86%) unterstützt. Für ältere Versionen können Sie einen Polyfill von Chris Coyier verwenden.[5][6]

Laden des Polyfills
// Support Test
const supportsContainerQueries = "container" in document.documentElement.style;

// Conditional Import
if (!supportsContainerQueries) {
  import("https://cdn.skypack.dev/container-query-polyfill");
}



Siehe auch

Weblinks

Screenshot of Container examples

Quellen

  1. Wann Media Queries an ihre Grenzen stoßen im Blog von kulturbanause
  2. MDN: Container query length units
  3. Container Units Should Be Pretty Handy (css-tricks.com)
  4. CSS Container Query Units von Ahmad Shadeed, 18 Sep 2021
  5. css-tricks: A New Container Query Polyfill That Just Works vom 06.01.2022
  6. github: GoogleChromeLabs /container-query-polyfill von surma
  7. css-tricks: iShadeed’s Container Queries Lab Geoff Graham on Sep 1, 2022
  8. iShadeed Lab - Container queries


deutsch