kulturbanause Blog

Responsive Design, WordPress, Konzeption, HTML, CSS, JS & UX/UI …

Container Queries & Element Queries im Responsive Web Design

Eine der drei Grundvoraussetzungen, die Ethan Marcotte 2010 in seinem legendären Artikel »Responsive Webdesign« als Grundlage für selbiges aufgeführt hat, sind die Media Queries. Mit Media Queries kann man das Layout einer Website anhand von Geräteeigenschaften verändern. Beispielsweise, wenn der Viewport eine bestimmte Größe über- oder unterschreitet. Seit 2010 ist einige Zeit vergangen und es hat sich in der Praxis herausgestellt, dass für die Veränderung des Layouts die Viewportgröße nicht immer der ideale Ausgangspunkt ist. Häufig wäre es besser, wenn ein Element immer dann verändert werden könnte, wenn der Raum für dieses spezielle Element einen bestimmten Punkt über- oder unterschreitet. Genau das sollen sog. Element Queries bzw. Container Queries erledigen. Und noch vieles mehr.

Workshops und Seminare von kulturbanause

Unsere Seminar-Termine für 2018 sind online!

Jetzt Tickets sichern!

Der Weg zum modularen Design

Früher haben Webdesigner ein Projekt vorrangig in Unterseiten geplant und gelayoutet. Es wurden z. B. eine Startseite, eine Produktansicht, eine Textseite usw. erstellt. Dann kam ab 2011 das Thema Responsive Design auf und erforderte ein flexibles Layout, dass auf verschiedenen Bildschirmgrößen funktioniert.

Breakpoints für Geräteklassen

Designer ohne HTML- und CSS-Kenntnisse konnten in Photoshop & Co. keine wirklich flexiblen Layouts gestalten. Daher haben Sie zusätzliche Layouts für verschiedene Geräteklassen entworfen. Im Ergebnis hatte man dann beispielsweise das Layout der Startseite auf dem Smartphone, dem Tablet und dem Desktop gestaltet. Bei der Breite dieser verschiedenen Layouts orientierte man sich meist an der Größe typischer Geräte wie dem iPhone oder dem iPad. Das Ergebnis war daher oft ein sog. adaptives Layout mit wenigen globalen Breakpoints.

Wenige globale Breakpoints in einem adaptiven Layout (Extrembeispiel)

Auch entstanden zahlreiche Layout-Dateien, die immer wieder die gleichen Bestandteile besaßen. Beispielsweise ist der Header meist auf allen Unterseiten identisch – musste aber doppelt gepflegt werden. Auch wenn Photoshop und Co. sich größte Mühe gaben den veränderten Bedingungen gerecht zu werden, stellte sich der Workflow als zunehmend träge, fehleranfällig und teuer heraus.

Erstellen Sie Übergangspunkte auf Grundlage der Inhalte und niemals auf Grundlage bestimmter Geräte, Produkte oder Marken. (Google Web Fundamentals)

Breakpoints für Komponenten und gestalterische/Inhaltliche Anforderungen

Designer mit HTML und CSS-Kenntnissen begannen parallel damit Layouts direkt im Browser zu gestalten und während der Arbeit permanent die Viewportgröße zu verändern. Unterstützt wurden Sie von immer besseren Entwickler-Tools in den Browsern. »Echte« responsive Layouts mit einem flüssigen Gestaltungsraster waren die Folge. Auch wurden die Breakpoints zunehmend dort gesetzt, wo der Content oder ein bestimmter Teilbereich des Layouts (die Komponente) eine Veränderung brauchte. Die Vorgehensweise dieser Designer führte daher zu einem robusteren und modularen Ergebnis mit deutlich mehr Breakpoints.

Modulare Breakpoint-Struktur in einem modernen, responsiven Projekt

 

Design-Pattern und -Systeme

Die Vorgehensweise Layouts nicht für Geräteklassen zu gestalten, sondern modular zu entwickeln setzte sich durch, und schlaue Leute begannen damit Konzepte für diesen Workflow zu entwickeln.

Bei Atomic Design-Ansatz werden aus kleineren Einheiten größere Komponenten entwickelt, die später zu Layouts zusammengebaut werden. Auch Frameworks wie Bootstrap und Foundation gehen in diese Richtung. Foundation hat beispielsweise mit den Building Blocks eine entsprechende Bibliothek veröffentlicht. Auch im kulturbanause-Blog findet ihr zahlreiche Design-Pattern – beispielsweise für Navigationen.

Ob nun Atomic Design oder andere modulare Ansätze. Der Trend geht eindeutig in die Richtung responsiver Komponenten, die zu immer größeren Modulen kombiniert werden und gemeinsam mit einem flexiblen Gestaltungsraster letztendlich ein ganzes Projekt ergeben.

Media Queries am Limit

Bei einem derart modularen Aufbau passen Media Queries nicht gut ins Konzept. Mit einem Media Query kann man abfragen, ob ein bestimmtes Gerät (z. B. der Bildschirm) eine bestimmte Eigenschaft (z. B. die Viewport-Breite) besitzt und anschließend darauf reagieren. Doch die Viewport-Breite ist oft nicht relevant für eine Komponente die nur in einem Teilbereich des Layouts platziert wird. Wenn man sich mit responsiven Layouts beschäftigt, merkt man schnell, dass Media Queries an ihre Grenzen stoßen.

Das folgende Beispiel zeigt warum. Das Layout beinhaltet eine typische responsive Komponente – die sog. Card, die aus Bildbereich und Inhalt besteht. Wenn die Card wenig Platz zur Verfügung hat, ist sie blau und das Bild soll über dem Inhalt stehen. Wenn mehr Raum verfügbar ist, soll das Bild nach links rutschen und die Card grün umgefärbt werden.

Das Beispiel Layout mit vier Layoutvarianten

Wenn wir das Projekt technisch mobile First aufbauen, stellt die blaue Card das Standardverhalten dar. Um bei einer Viewportbreite von 500 Pixeln auf die grüne Card umzuschalten, schreiben wir einen Media Query. Leider müssen wir ab 800 Pixeln das Verhalten widerrufen. Daher bietet es sich an, eine sog. »Media Query Range« zu verwenden, die von 500 bis 800 Pixeln die grüne Card erzeugt. Damit springt ab 800 Pixeln wieder das Standardverhalten ein. Doch bei 1000 Pixeln Viewportbreite müssen wir den Code für die grüne Card erneut schreiben. Vereinfacht dargestellt sieht der Code so aus:

.card {
 /* blau */
}

@media screen and (min-width: 500px) and (max-width:800px) {
 .card {
 /* grün */
 }
}

@media screen and (min-width: 1000px) {
 .card {
 /* grün */
 }
}

Die Wiederholung der Stile für »grün« könnten wir in diesem Beispiel durch Zusammenfassen der Media Queries zwar noch vermeiden, doch spätestens wenn eine weitere Unterseite mit alternativem Layout hinzukommt, müssen wir eine Sonderregel schreiben.

Folgendes Verhalten ist für zwei weitere Unterseiten gewünscht:

Auf anderen Seiten soll die Card in der Desktop-Version blau sein und den Bildbereich über dem Text zeigen

Wie man sieht, soll die Card nun in der Desktopversion blau sein. Doch da wir die Optik des Elements bisher mit einem Media Query anhand der Viewportbreite festlegen, werden die falschen Stile angewandt. Erst einmal erhalten wir folgendes Ergebnis:

Fehldarstellung durch den Einsatz des Media Queries

Wir müssen folglich für diese beiden Unterseiten die unerwünschten Stile in den größeren Layoutvarianten überschreiben. In diesem Beispiel ab dem Breakpoint bei 800 Pixeln. Das ist weder flexibel noch robust.

@media screen and (min-width: 801px) {
 .page-1 .card, 
 .page-2 .card {
   /* blau */
 }
}

Praktischer wäre in diesem Fall eine Komponente die eigene Breakpoints besitzen kann – also beispielsweise auf den verfügbaren Raum im Elternelement reagiert. In unserem Beispiel hätte die Komponente »Card« dann zwei Zustände und einen Breakpoint (Live-Beispiel).

Die Card als Komponente mit eigenem Breakpoint

Der Code könnte beispielsweise so aussehen:

.card {
  /* blau */
}

/* Schreibweise nach github.com/marcj/css-element-queries */
.card[min-width~="500px"] {
  /* grün */
}

Oder so:

.card {
  /* blau */
}

/* Schreibweise nach github.com/tomhodgins/element-queries-spec */
@element .card and (min-width:500px) {
  $this {
    /* grün */
  }
}

Oder so:

.card {
  /* blau */
}

/* Schreibweise nach github.com/ausi/cq-prolyfill */
.card:media(min-width:500px) {
    /* grün */
}

Element Queries & Container Queries

Es gibt im Zusammenhang mit dem oben beschriebenen Wunsch nach einer komponentenbasierten Herangehensweise zahlreiche Gruppen und Entwickler die seit geraumer Zeit an Lösungen arbeiten. KEINE davon ist final und es gibt aktuell keine standardisierte Syntax! Es gibt auch unterschiedliche Ansätze wie das gewünschte Element angesprochen werden kann und daraus haben sich verschieden Begrifflichkeiten entwickelt, die teilweise synonym verwendet werden. Ich nutze für die folgenden Beispiele die Syntax und das Wording des Scripts EQCSS, auf das ich später noch detailliert eingehe.

Was sind »Scoped Styles«?

Ein sog. »Scoped Style« begrenzt die Stile auf einen bestimmten Wirkungsbereich.

/* Wenn .sidebar irgendwo im Dokument existiert … */ 
@element .sidebar {

   /* … reduziere die Breite von .content. Wichtig: .content muss kein Kind-Element von .sidebar sein! */
   .content {
     width: 60%;
   }
}

Sog. »Scoped CSS« gab es in anderer Form übrigens schon einmal – der Browser-Support hat allerdings nachgelassen.

Was sind »Responsive Conditions«?

»Responsive Conditions« erweitern einen Scoped Style um eine Bedingung.

/* Wenn .card existiert und breiter als 500px ist … */ @element .card and (min-width:500px) {
   
  /* … ordne .card-header und .card-content nebeneinander an */
  .card-header, .card-content {
    flex:1; 
  }
}

Was sind »Element Queries«?

Die Kombination von Scoped Style und Responsive Condition nennt man »Element Query«. Man kann einen Element Query verwenden um andere Elemente zu stylen oder das gleiche Element. Im letzten Code-Beispiel haben wir beispielsweise die Breite der .card abgefragt und anschließend die enthaltenen Elemente verändert, wenn die .card mehr als 500 Pixel Platz hat. Wir haben also eine Abfrage für das Container-Element (die .card) verwendet, um die Kind-Elemente (.card-header und .card-content) zu stylen.

Was sind »Container Queries«?

Aus technischer Sicht ist ein Container Query ein Element Query, der dazu benutzt wird enthaltene Elemente zu stylen. Die Abfrage einer Bedingung erfolgt über einen Container – daher der Name. Container Queries passen gut zusammen mit Namenskonventionen wie BEM. Streng betrachtet sind die klassischen Media Queries auch Container Queries, da man das Styling der Website über die Abfrage einer Container-Eigenschaft verändert.

Vom Konzept her sind Container Queries gegenüber Element Queries eingeschränkt, da ein Element Query immer auch ein Container Query sein kann – aber nicht umgekehrt.

Es ist mit einem Container Query beispielsweise nicht möglich ein Element anzusprechen, dass keine Kindelemente besitzt oder besitzen kann. Ein <input>-Feld grün umzufärben, wenn es mindestens 20 Zeichen enthält, gestaltet sich mit einem Container Query daher schwierig. Ein Element Query würde beispielsweise folgende Syntax mit dem Schlüsselwort $this nutzen:

/* Wenn <input> mindestens 20 Zeichen enthält … */
@element input and (min-characters:20) {

  /* … ändere die Farbe auf grün */
  $this {
    color:lime;
  }

}

EQCSS – Element Query CSS

Wie bereits mehrfach erwähnt, gibt es in CSS noch keine Element Queries, Container Queries oder ähnliches. Es kursieren verschiedene Ideen in diesem Zusammenhang und es existieren einige JavaScript-Bibliotheken die die verschiedenen Ideen mehr oder weniger einsatzfähig machen.

Eine dieser Bibliotheken ist EQCSS. EQCSS ermöglicht Element Queries mit zahlreichen Bedingungen ab dem Internet Explorer 9 und aufwärts. Ein zusätzliches Skript rüstet den Support bis zum IE8 nach. EQCSS ist auf der Website elementqueries.com bzw. containerqueries.com (… ein Beweis für die Uneinigkeit hinsichtlich des Wordings) und in der zugehörigen (nicht offiziellen!) Spezifikation detailliert erklärt.

EQCSS – Funktionsweise

Das JavaScript kann auf der Website heruntergeladen oder über ein CDN bezogen werden. Es reicht aus, dass Skript im Footer zu laden.

<script src="eqcss.js"></script>

Anschließend können Element Queries im CSS-Code verwendet werden. Ob ihr dazu einen <style>-Abschnitt im selben Dokument verwendet oder ein über <link> referenziertes Stylesheet ist euch überlassen. Solltet ihr Element Queries strikt vom restlichen CSS-Code trennen wollen, so stehen auch dafür einige Optionen zur Verfügung.

EQCSS – Syntax

Element Queries nach EQCSS-Spezifikation werden wie folgt geschrieben:

@element SELEKTOR and (RESPONSIVE CONDITION) { 
  SELEKTOR {
    EIGENSCHAFT: WERT;
  }
}

Wenn also beispielsweise der <header> blau eingefärbt werden soll, wenn der <body> eine Höhe von 800 Pixeln hat, genügt folgender Code:

@element body and (min-height:800px) { 
  header {
    background: blue;
  }
}

EQCSS – Responsive Conditions

In EQCSS stehen eine Reihe interessanter Bedingungen für die Abfrage zur Verfügung:

  • min-width & max-width
  • min-height & max-height
  • min-children & max-children
  • min-characters & max-characters
  • min-lines & max-lines
  • min-scroll-x, max-scroll-x, min-scroll-y & max-scroll-y
  • orientation
  • min-aspect-ratio & max-aspect-ratio

EQCSS – Meta Selectors

Wenn man EQCSS nutzen möchte um das abgefragte Element direkt anzusprechen, kommt das Schlüsselwort $this ins Spiel. Das folgende Beispiel zeigt den Code, der notwendig ist um den <header> blau einzufärben, wenn er selbst breiter als 600 Pixel wird.

@element header and (min-width:600px) { 
  $this {
    background: blue;
  }
}

Neben $this existieren auch noch die Meta Selektoren $prev für das vorherige Element, $next für das nächste Element, $parent für das Elternelement (!!), und $root für das Root-Element.

Da es beim Dollarzeichen zu Konflikten im Zusammenhang mit Sass kommen kann, möchte ich hier auf eine Lösung hinweisen.

EQCSS – CSS Einheiten

Wenn EQCSS im Einsatz ist, können auch neue Einheiten verwendet werden. Die Einheit ew steht für die Breite des Elements (element width), eh für die Höhe (element height). emin und emax stehen für den jeweils kleineren bzw. größeren Wert – genauso wie bei den verwandten CSS-Einheiten vh, vw, vmin und vmax.

EQCSS – CSS Funktionen

In EQCSS kann auch eine neue CSS-Funktion namens eval() verwendet werden. Damit ist es möglich JavaScript einzuschleusen.

EQCSS Beispiel 1 – Mehrspaltige Komponenten

Das folgende Beispiel zeigt einen Hauptinhalt und eine Seitenleiste, die jeweils drei Boxen enthalten.

<main class="container">
  <div class="box">1</div>
  <div class="box">2</div>
  <div class="box">3</div>
</main>

<aside class="container">
  <div class="box">1</div>
  <div class="box">2</div>
  <div class="box">3</div>
</aside>

Mit Flexbox wird dafür gesorgt, dass die Seitenleiste neben dem Inhaltsbereich sitzt und dass die Boxen jeweils die volle Höhe des Container-Elements einnehmen.

* { box-sizing: border-box; }

body {
  display: flex;
  justify-content: space-between;
}

main { width: 70%; }

aside { width: 25%; }

.box {
  background: white;
  margin:.5em;
  padding:1em;
  flex:1;
}

.container {
  padding:.5em;
  height:300px;
  background:#8cb11c;
  display: flex;
  flex-direction: column;
}

Nun sollen die Boxen innerhalb des Containers nebeneinander angezeigt werden, sobald der Container 600 Pixel breit ist. Dafür nutzen wir folgenden Element Query:

@element .container and (min-width: 600px) {
  $this {
    flex-direction: row;
  }
}
Das Layout nach Einsatz des Element Queries

Beispiel anschauen

EQCSS Beispiel 2 – Formulareingabe

Das folgende Formular besteht aus drei <label>/<input>-Kombinationen.

<label for="field-1">Bitte gib mehr als 10 Zeichen ein. </label>
<input type="text" id="field-1">

…

Mit einem Element Query prüfen wir, ob in das <input>-Feld mindestens 10 Zeichen eingegeben wurden. Wenn das der Fall ist, färben wir das Feld grün ein und fügen den Hinweis »Vielen Dank!« mittels Pseudoelement an das vorausgegangene Element (das <label>) an.

@element input and (min-characters: 10) {
  $this {
    background:#8cb11c;
    color:white;
  }

  $prev::after {
    content:'Vielen Dank!';
  }
}
Mit einem Element Query wird das Eingabefeld gefärbt und ein Hinweis eingeblendet

Beispiel anschauen

EQCSS Beispiel 3 – Sticky Navigation

Im nächsten Beispiel sitzt die Navigation unter dem Header. Wenn gescrolled wird, soll die Navigation fixiert werden, sobald ihre Oberkante die Oberkante des Viewports erreicht hat.

/* relevanter CSS Code */
@element body and (min-scroll-y: 600px) {
  nav {
    position: fixed;
    top:0;
    left:0;
    right:0;
  }
}

Beispiel anschauen

EQCSS Beispiel 5 – Viewport- und Elementgröße auslesen

Mit Hilfe von JavaScript und der Funktion eval() ist es möglich die Viewport-Breite oder die Höhe eines Elements auszulesen. Mittels Pseudoelement ::after wird die Abmessung dann sichtbar gemacht.

<body>
  <div class="resizeable"></div>
</body>
@element body {
  $this::after {
    position:fixed;
    top:1em;
    left:1em;
    content: 'eval("'Viewport: '+window.innerWidth+' x '+window.innerHeight")';
  }
}

@element .resizeable {
  $this::after {
    content: 'eval("'Element: '+offsetWidth+' x '+offsetHeight")';
  }
}
Höhe und Breite von Element und Viewport werden mittels eval() angezeigt

Beispiel anschauen

Alternative Lösungen

Neben der hier beschrieben Lösung mittels EQCSS gibt es auch alternative Ansätze, die ich euch nicht vorenthalten möchte.

Kritikpunkte

Auch wenn der Ruf nach einer Element/Container Query-Lösung laut ist, so gibt es doch auch Kritik.

Beispielsweise sind in allen mir aktuell bekannten Lösungsansätzen Endlosschleifen möglich. Der folgende Code erzeugt beispielsweise einen solchen Konflikt durch Selbstreferenzierung:

// Wenn der div breiter ist als 1000 Pixel …
@element div and (min-width: 1000px) {

  // … verkleinere ihn auf 999 Pixel
  $this {
    width: 999px;
  }
}

Das ist in der Tat ein Problem, aber möglicherweise nicht ein solcher Blocker für die Weiterentwicklung von Element- bzw. Container Queries wie es oft dargestellt wird. Denn erstens ist ein solcher Konflikt auch mit validem Standard-CSS möglich (siehe Beispiel) und zweitens springt der Browser bei einem Element Query-Konflikt nicht permanent zwischen zwei Bedingungen hin und her. Das bedeutet, dass es zwar ggf. nicht so aussieht wie gewünscht, aber es gibt keine Endlos-Schleife die in einem Browser-Absturz oder ähnlichem resultiert. Zuletzt löst der CSS-Befehl contain: strict; viele Probleme, wenn der Browser-Support vorhanden ist.

Ein weiterer Kritikpunkt ist, dass Element Queries durch neue Layoutlösungen wie Flexbox oder CSS Grids teilweise überflüssig sind bzw. dass sie nur das Styling betreffen, nicht aber das Verhalten eines Elements. Das stimmt zwar – wäre für mich aber kein Grund nicht trotzdem an der Technologie zu arbeiten, da es haufenweise sinnvolle Anwendungsfälle gibt.

Fazit

Wir sind zwar noch weit entfernt von standardisierten CSS Element Queries – aber das Konzept ist höchst interessant und funktioniert mit JavaScript bereits erstaunlich gut. Ob man sich traut EQs in einem Projekt einzusetzen, muss im Einzelfall entschieden werden, aber es lohnt sich allemal mit der Technologie zu spielen und sie ggf. sogar aktiv mit voranzutreiben.

Links / Quellen

Jetzt bist du gefragt!

Hast du Anregungen, Ergänzungen, einen Fehler gefunden oder ist dieser Beitrag nicht mehr aktuell? Dann freuen wir uns auf deinen Kommentar.

Du kannst diesen Beitrag natürlich auch weiterempfehlen. Wir sind dir für jede Unterstützung dankbar!

Unterstützung bei Responsive Design-Projekten

Unsere Agentur ist auf responsive Design spezialisiert. Wir realisieren u.a. maßgeschneiderte Websites und führen Schulungen durch. Wenn du Unterstützung bei der Planung, Gestaltung und Entwicklung einer Website im Responsive Design benötigst, helfen wir gerne weiter.
Responsive Webdesign-Leistungsangebot →

Das könnte dich auch interessieren

5 Kommentare

  1. Detlef

    Verfasst am 2. Juni 2017 um 13:59 Uhr.

    Super artikel … Danke!

  2. Philipp

    Verfasst am 2. Juni 2017 um 14:08 Uhr.

    Das Konzept finde ich auf jeden Fall spannend, selbst wenn wir mit Flexbox mittlerweile ein sehr angenehm zu nutzendes Werkzeug in der Hand haben.

    Ich bin gespannt, was ich in dieser Richtung tun wird, im Moment mag ich mich aber noch nicht für einen Ansatz entscheiden.

    Viele Grüße

    Philipp

  3. Gunnar Bittersmann

    Verfasst am 3. Juni 2017 um 10:33 Uhr.

    Der IMHO wichtigste Satz kam em Ende noch, ging aber als Nebensatz etwas unter: „dass Element Queries durch neue Layoutlösungen wie Flexbox oder CSS Grids teilweise überflüssig sind“.

    Wenn Entwickler denn anfingen, die Möglichkeiten zu nutzen, die CSS bietet, würden die Rufe nach Features, die CSS (aus Gründen) nicht bietet, leiser werden.

    Look Ma, no media queries: https://codepen.io/gunnarbittersmann/pen/jmoWNp

    • Christian Schaefer

      Verfasst am 3. Juni 2017 um 10:58 Uhr.

      Boah, Gunnar, Du bist echt der Ober-Kommentar-Hooligan.

      Wie vergrößerst Du Schlaumeier denn die Schrift dynamisch bei unterschiedlichen Elementgrößen mit Deinen Flexbox und Grid Fähigkeiten? Oder wie änderst die Text-Ausrichtung, je nach Elementgröße? Oder lädst Du andere Hintergrund-Grafiken? Geht nicht. Danke. Weitermachen.

      • Gunnar Bittersmann

        Verfasst am 3. Juni 2017 um 12:51 Uhr.

        Du hast das Wörtchen „teilweise“ überlesen‽

Kommentar verfassen

Dieser Blog lebt vom Feedback der Besucher! Also los, mach mit!
Bitte habe Verständnis dafür, dass Kommentare die mit dem Inhalt dieses Beitrags nichts zu tun haben, gelöscht werden.