CSS-Grid

Über ein Jahr ist es schon her, seit Firefox mit seiner Version 52 als erster Browser weltweit die neue Spezifikation der CSS-Grid implementiert hat.

Seitdem haben fast alle anderen Browser nachgezogen, was vielen Webseiten innerhalb kürzester Zeit erlaubt hat, dieses neue und mächtige Layout-Tool produktiv zu verwenden.

Inhalt

Statistik

Nahezu alle modernen Browser unterstützen die aktuelle Spezifikation der CSS-Grid. Lediglich Internet Explorer sticht heraus, dieser unterstützt dafür aber zumindest die alte Spezifikation und kann daher mit vielen Basis-Anwendungsfällen trotzdem umgehen.

Insgesamt unterstützen laut Can I use mehr als 85 % aller Browser in Deutschland die CSS-Grid, mehr als 80 % sogar komplett ohne Vendor-Präfixe. International sind es sogar noch mehr! (Stand: November 2018)

Struktur und Terminologie einer CSS-Grid

Die CSS-Grid ist praktisch eine Definition von Linien (Tracks), welche Zeilen (Rows) und Spalten (Columns) und daraus entstehende Zellen abgrenzen, in welche Inhalte platziert werden können. Diese Definition passiert auf dem Container. Die Inhalte (Items) verlassen sich zu einem Großteil auf diese Definition und fügen sich einfach in den Container ein, ggf. unter eigener Angabe einer Größe und Position. Die Items können eine oder mehrere Zellen gleichzeitig einnehmen, unter Angabe der Tracks, an denen sie starten und enden. Einen so definierten Bereich nennt man Area.

Der Container kann explizit definieren, wie viele Rows und Columns er hat. Jedoch können dem Container beliebig viele Items übergeben werden. Sollten die definierten Zellen der expliziten Grid dafür nicht mehr reichen, fügt der Container je nach Bedarf implizit weitere Rows und Columns ein. Auch über diese implizite Grid hat man (wenn auch leicht begrenzte) Kontrolle.

Tools

Die Firefox Entwicklertools bieten die aktuell unbestritten besten Werkzeuge zum Umgang mit der Grid. Im Layout-Tab, welcher bislang nur zur Darstellung des Box-Modells (Abstände, Ränder, Größe, Positionierung, … einer Box) gedient hat, zeigt nun auch einen Bereich für Grid-Layout, sofern auf der aktuellen Seite ein solches im Einsatz ist. Die Ansicht muss mit dem Häkchen bei „Gitteransicht hervorheben“ erst für die gewünschte Grid aktiviert werden. Sollten auf der Seite also mehrere Grids sein, kann man auch wählen, welche zu sehen sein soll:

Auswahl der anzuzeigenden Grid in Firefox Devtools Auswahl der anzuzeigenden Grid. Fährt man mit der Maus über das Fadenkreuz hinter den Einträgen, wird die entsprechende Grid hervorgehoben.

Ist die Ansicht aktiviert, zeigt Firefox eine Sammlung von Hilfslinien an (in der Farbe, die hinter der gewählten Grid zu sehen ist — diese kann man auch ändern), welche die Grenzen (Tracks) der Grid, einzelner Zeilen/Spalten sowie impliziter und expliziter Grid anzeigen:

Die Tracks der gewählten Grid Legende:

  • Durchgezogene Linien = Start/Ende der expliziten Grid
  • Gestrichelte Linien = Tracks der expliziten Zeilen bzw. Spalten
  • Gepunktete Linien = Tracks der impliziten Zeilen bzw. Spalten
  • Schraffierte Fläche = Abstände zwischen Zeilen/Spalten

Außerdem zeigen die Entwicklertools eine grobe Vorschau des Aufbaus der gesamten Grid:

Vorschau der gewählten Grid

Die Tracks werden auch nummeriert. Die Anzeige der Nummerierung muss extra aktiviert werden mit einem Häkchen auf „Zeilennummern anzeigen“ (vgl. Bild oben):

Nummerierung der Tracks Jede Track wird einzeln nummeriert. Der explizite Teil außerdem auch noch zusätzlich rückwärts nummeriert, beginnend bei -1.

Zu guter Letzt sind auch Namen der Flächen (Areas) zu sehen, wenn diese Anzeige aktiviert ist (Häkchen bei „Bereichsnamen anzeigen“, vgl. Bild oben) und Namen vorhanden sind:

Benannte Areas in Firefox Devtools

Grid-Container

Den Container definiert man mit den display-Werten grid oder inline-grid (analog zu block und inline-block, table und inline-table oder flex und inline-flex). Ob man die Block- oder Inline-Variante verwendet, hat keine Auswirkung auf das innere Verhalten, sondern nur auf den Fluss des gesamten Containers im Dokument.

Abstände definieren

Der Container kann definieren, wie viel Abstand zwischen allen seinen Items sein soll. Dies funktioniert ähnlich wie border-spacing bei Tabellen: Sämtliche Zeilen und Spalten halten den genannten Abstand zueinander.

Dieser Abstand wird mit der Eigenschaft grid-gap festgelegt. Möchte man nur den Abstand zwischen Zeilen definieren, kann man stattdessen grid-row-gap verwenden, für Abstände zwischen Spalten grid-column-gap.

Ein kurzes grid-gap-Beispiel zum selbst Probieren:

Demo grid-gap

Die neue Einheit fr

Extra für den Einsatz in der Grid wurde eine neue Einheit eingeführt: fr, kurz für „fractional“. Dieser Einheit liegt der „übrige“ Platz in der Row bzw. Column zugrunde. Damit ist der Platz gemeint, welcher abzüglich aller Items mit fest definierten Größen oder grid-gaps noch übrig bleibt.

Wenn mehrere Items fr-Größen zugewiesen bekommen, wird der Platz gleichmäßig unter ihnen verteilt, proportional zur Größe des Wertes. Zwei Items mit jeweils 1fr bekommen gleich viel Platz zugeteilt. Wenn eines stattdessen 2fr bekommt und das andere 1fr behält, bekommt ersteres doppelt so viel Platz zugeteilt (Verhältnis 2:1).

Insbesondere in Kombination mit grid-gap ist diese Einheit ein Muss. Prozentwerte z. B. beziehen diese eben nicht mit ein, daher ergibt sich aus insgesamt 100 % eine Größe, welche über den Container übersteht.

Ein kurzer Vergleich, wie sich fr und % im Vergleich verhalten.

Demo Grid fr

Explizite und implizite Grid

Der Container kann einen Satz von Zeilen und Spalten definieren. Dieser Bereich ist die explizite Grid. Sollte der Container mehr Items beinhalten, als er explizite Zellen verfügbar hat, werden die überschüssigen Items automatisch in der impliziten Grid platziert, d. h. es werden weitere Zeilen bzw. Spalten angehängt (je nach der angegebenen Richtung in grid-auto-flow).

Explizit

Der explizite Teil wird mithilfe der Eigenschaften grid-template-columns und grid-template-rows definiert. Hierin gibt man an, wie viele Spalten/Zeilen der Container hat und wie breit diese sein sollen.

Beispiel:

  grid-template-columns: 50px 50% 1fr;
  grid-template-rows: 1fr 1fr;

Hier wurde eine explizite Grid mit 3 Spalten und 2 Zeilen definiert. Die erste Spalte wird 50px breit, die zweite 50% der Gesamtbreite und die dritte nimmt sich den übrigen Platz. Die beiden Zeilen werden gleich hoch.

Es gibt außerdem noch grid-template-areas, über die ebenfalls die Anzahl von Zeilen und Spalten angegeben werden, jedoch nicht ihre Breiten. Dafür werden Namen definiert.

Die Kurzform grid-template fasst alle 3 Eigenschaften zusammen.

Implizit

Mit ganz ähnlichen Eigenschaften werden auch die Zeilen und Spalten der impliziten Grid definiert, nämlich grid-auto-columns und grid-auto-rows. Jedoch wird hier nur die Größe einer einzelnen Zeile/Spalte angegeben, die Anzahl kann nicht vorgegeben werden, da es sich ja um den impliziten, automatisch ergänzten Teil der Grid handelt.

Beispiel:

  grid-auto-rows: 50px;

Hier werden alle automatisch hinzugefügten Zeilen 50px hoch.

Die Grid kann nur in eine Richtung wachsen, sie ergänzt also entweder Zeilen oder Spalten. Demzufolge wirkt auch immer nur eine der obigen Eigenschaften. grid-auto-flow gibt die Richtung der impliziten Grid an.

Möchte man unbedingt alles auf einmal definieren, steht auch die Eigenschaft grid für alle oben genannten Eigenschaften zur Verfügung.

Explizite und implizite Grid in Firefox Devtools Explizite und implizite Grid in Firefox Devtools. Legende:

  1. Start/Ende der expliziten Grid (durchgezogene Linie)
  2. Tracks der expliziten Grid (getrichelte Linien zwischen den durchgezogenen)
  3. Tracks der impliziten Grid (gepunktete Linien nach der durchgezogenen)
  4. Links stehen die Nummerierungen der Tracks. Implizite Tracks werden ganz normal weiter nummeriert
  5. Rechts stehen die umgekehrten Nummerierungen der Tracks. Diese werden nur für die explizite Grid angelegt, d. h. Track Nr. -1 ist immer das Ende der expliziten Grid.

Hilfsfunktionen

Eine Grid mit z. B. fünf gleich breiten Spalten kann man mit grid-template-columns: 1fr 1fr 1fr 1fr 1fr; definieren, jedoch ist das lästige Tipperei. Hierfür bietet sich auch die Grid-Hilfsfunktion repeat() an:

grid-template-columns: repeat(5, 1fr);

repeat() nimmt zwei Werte an: Zuerst die Anzahl der Wiederholungen, danach die zu wiederholende Größe. In diesem Fall soll also die Größe 1fr fünfmal wiederholt werden.

Man kann auch durchaus mehrere Male repeat() aufrufen: grid-template-columns: repeat(4, 1fr) repeat(4, 100px); wird demnach zu grid-template-columns: 1fr 1fr 1fr 1fr 100px 100px 100px 100px;.

Manchmal findet man sich auch in der Situation wieder, wo man einer Spalte oder Zeile zwar den Freiraum geben möchte, ihre Größe anzupassen, jedoch soll sie ein bestimmtes Minimum nicht unterschreiten, oder ein Maximum nicht überschreiten. Für diese Fälle wurde die Grid-Hilfsfunktion minmax() zur Verfügung gestellt. Diese nimmt ebenfalls zwei Werte an: Der erste ist das Minimum, der zweite das Maximum. Mindestens einer der beiden Werte muss flexibel sein (fr, Prozent, auto usw.), andernfalls wird einfach der größere Wert fix gesetzt.

Eine Spalte mit minmax(500px, 1fr) ist demnach 1fr breit, aber wird auf keinen Fall schmaler als 500px. Mit minmax(1fr, 500px) wird sie dagegen nie breiter als 500px, kann darunter aber ihre Breite frei bestimmen.

In Kombination mit repeat() und minmax() gibt es noch zwei weitere Grid-Hilfsfunktionen: auto-fit und auto-fill. Diese können die Angabe der Anzahl in repeat() ersetzen, aber nur wenn die Breite mit minmax() angegeben wurde. Mit auto-fit und auto-fill ist es möglich, die notwendige Anzahl automatisch zu berechnen, abhängig davon, wie viele Inhalte zur Verfügung stehen und welche Größen-Beschränkungen diese haben. Wenn die Inhalte nicht in eine Zeile/Spalte passen, wird in eine neue umgebrochen. Wenn der Platz größer wird, dehnen die Inhalte sich aus. Sobald für ein zusätzliches, nicht vorhandenes Element genug Platz wäre, lässt auto-fill diesen sogar frei, „reserviert“ ihn also. auto-fit tut dies nicht, sondern dehnt die vorhanden Inhalte weiter aus.

Im folgenden Beispiel gibt es sechs Spalten, die nicht schmaler als 100px werden dürfen. Erst, sobald eine siebte Platz hätte, ist der Unterschied zwischen auto-fit und auto-fill erkennbar:

Demo Auto-Fit/Fill

Laufrichtung der Grid

Standardmäßig ordnet die Grid die Items zuerst von links nach rechts und dann von oben nach unten an.

Dieses Verhalten lässt sich aber auch bewusst mit der Eigenschaft grid-auto-flow steuern. Die vier möglichen Werte für diese Eigenschaft sind row (das ist der eben beschriebene Standard), column, row-dense und column-dense. Es funktioniert außerdem noch dense, dies tut aber das gleiche wie row-dense.

Die beiden row-Werte ordnen zuerst in Zeilen- und dann erst in Spaltenrichtung an. Sollten die Zeilen nicht genügen, werden neue, implizite angehängt. Die column-Werte tun das Gegenteil, sie ordnen zuerst in Spaltenrichtung an und ergänzen im Fall mangelnden Platzes neue Spalten.

Im Normalfall arbeitet die Grid sich stur Zeile für Zeile bzw. Spalte für Spalte durch und springt immer weiter, wenn der Platz gefüllt ist bzw. nicht für das nächste Item ausreicht. Die beiden dense-Werte erlauben es der Grid, bei der Anordnung der Items wieder zurück an den Anfang zu springen und nach der ersten freien übersprungenen Area zu suchen, der groß genug für das Item ist. Auf diese Weise ist die genaue Reihenfolge der Items nicht mehr bekannt, aber der Platz in der Grid wird optimal genutzt.

Im Beispiel können die Unterschiede zwischen den vier Varianten ausprobiert werden:

Demo Grid-Auto-Flow

Tracks und Areas benennen

Es ist möglich, den expliziten Tracks und Areas Namen zu geben, um zum Beispiel ihre beabsichtigte Funktion zu verdeutlichen. So kann man eine Area „header“ nennen. Die begrenzenden Tracks bekommen damit automatisch auch Namen: „header-start“ und „header-end“ (gültig für beide Richtungen). Namen können sich auch beliebig überlappen. Beispielsweise kann ein Track gleichzeitig „header-end“ und „main-start“ sein, wenn eine Area „main“ direkt auf den Header folgt.

Um Areas zu benennen, verwendet man grid-template-areas. Die Notation dieser Eigenschaft erlaubt es auch schöner Weise, die Areas optisch wie die Grid auszurichten. Eine Definition von Rows und Columns lässt sich also auch darüber lösen. Im Beispiel wird eine Grid mit je 3 Rows und Columns erzeugt. Diese enthalten einen Header, eine Sidebar, einen Main-Bereich und einen Footer. Möchte man eine Area nicht benennen, kann diese durch die Notation . ausgelassen werden.

display: grid;
grid-template-areas: "header  header header"
                     "sidebar main   main"
                     ".       footer footer";

Benannte Areas in Firefox Devtools Ausgabe des obigen Codes, mit angeschalteter Grid-Ansicht in den Firefox Entwicklertools. Die benannten Areas sind hier sichtbar geschalten.

Man kann auch explizit den Tracks Namen geben. Als wir oben die Breiten für verschiedene Rows und Columns festgelegt haben, haben wir genau genommen bestimmt, wie viel Platz der Bereich zwischen zwei Tracks einnehmen soll. Gedacht steht also tatsächlich da: [Track] Breite [Track] Breite [Track] …. An der Stelle, wo die Tracks gedacht dastehen, kann man sie auch benennen, mit beliebig vielen Namen: [header-start] Breite [header-end main-start] Breite [main-end …] …

Um obiges Beispiel also nur über benannte Tracks abzubilden, muss man Folgendes definieren:

display: grid;
grid-template-columns: [header-start sidebar-start] 1fr [sidebar-end main-start footer-start] 2fr [main-end footer-end header-end];
grid-template-rows: [header-start] 1fr [header-end sidebar-start main-start] 1fr [sidebar-end main-end footer-start] 1fr [footer-end];

Diese Schreibweise ist aber natürlich um einiges länger und deutlich schlechter verständlich.

Ausrichtungs-Möglichkeiten des Containers

Der Container kann seine eigene Ausrichtung innerhalb seines Elternelements sowie die Ausrichtung aller Items innerhalb der von ihnen belegten Areas verändern. Dazu stehen die CSS-Eigenschaften

  • justify-content, align-content und place-content für den Container und
  • justify-items, align-items und place-items für die Items

zur Verfügung.

Mehr Informationen dazu in einem späteren Abschnitt.

Grid-Items

Die Items sind direkte Kinder des Containers im Markup. Eine mögliche Ausnahme zu dieser Regel bietet die recht neue CSS-Eigenschaft display: contents, welche dafür sorgt, dass das zugehörige Element aus dem Markup „verschwindet“.

Die CSS-Eigenschaften float, display: inline-block, display: table-cell, vertical-align und alle column-*-Varianten haben auf einem Grid-Item keinerlei Wirkung. Dieses Verhalten kann gezielt ausgenutzt werden, um Fallback-Layouts für ältere Browser ohne Grid-Unterstützung zu definieren.

Die Items suchen sich selbstständig einen passenden Platz in der Grid, welcher ihren Anforderungen entspricht. Diese Aufgabe liegt nicht beim Container.

Platzierung und Größe der Items

Sofern man den Items nichts anderes mitteilt, fügt sich jedes in eine freie Zelle ein. Man kann aber explizit festlegen:

  • an welchem (benannten) Track es beginnen und enden soll:
    • beginnen: grid-column-start: <Nummer/Name>; // Spalte grid-row-start: <Nummer/Name>; // Zeile
    • enden: grid-column-end: <Nummer/Name>; // Spalte grid-row-end: <Nummer/Name>; // Zeile
    • kombiniert (Schreibweise: <Start> / <Ende>): grid-column: <Nummer/Name> / <Nummer/Name>; // Spalte grid-row: <Nummer/Name> / <Nummer/Name>; // Zeile
  • wie viele Zellen es überspannen soll: grid-column: span <Anzahl>; // Spalte grid-row: span <Anzahl>; // Zeile Dies ist kombinierbar mit einem Start- oder End-Track, z. B. um am zweiten Track zu starten und 3 folgende Zellen zu überspannen: grid-column: span 2 / span 3; // Spalte grid-row: span 2 / span 3; // Zeile
  • welche benannte Area ein Item besetzen soll: grid-column: <Name>; // Spalte grid-row: <Name>; // Zeile grid-area: <Name>; // grid-column + grid-row in Einem

Nützlich zu wissen: Die automatische Nummerierung der Tracks erfolgt in beide Richtungen. Möchte man also ein Item so platzieren, dass es am letzten Track endet, kann man die Zahl -1 angeben, für den vorletzten Track -2 usw. Dadurch bleibt man flexibel und kann problemlos weitere Rows und Columns einfügen, ohne alles anpassen zu müssen.

Ausrichtungs-Möglichkeiten der Items

Items können ihre eigene Ausrichtung innerhalb der Area, die sie im Container besetzen verändern. Dazu stehen die CSS-Eigenschaften justify-self, align-self und place-self zur Verfügung.

Mehr Informationen dazu im folgenden Abschnitt.

Ausrichtungs-Optionen (Container und Items)

Es gibt drei verschiedene Möglichkeiten, den Container oder die Items auszurichten:

  • horizontal (CSS-Eigenschaft beginnt mit justify-)
  • vertikal (CSS-Eigenschaft beginnt mit align-)
  • horizontal und vertikal gleichzeitig (CSS-Eigenschaft beginnt mit place-)

Der Container kann sowohl sich selbst als auch alle seine Items auf verschiedene Weisen ausrichten. Zusätzlich können die Items für sich selbst noch eine abweichende Ausrichtung angeben:

  • kompletten Container ausrichten: CSS-Eigenschaft endet auf -content
  • alle Items (via Container) ausrichten: CSS-Eigenschaft endet auf -items
  • einzelnes Item ausrichten: CSS-Eigenschaft endet auf -self

Mögliche Werte für die sich ergebenden Eigenschaften wie align-items oder place-self sind:

  • start (Inhalt steht am Anfang des Elternelements)
  • end (Inhalt steht am Ende des Elternelements)
  • center (Inhalt im Elternelement zentrieren)
  • stretch (auf volle verfügbare Größe des Elternelements ausdehnen)

Zusätzlich kann der Container für sich selbst noch drei weitere Werte annehmen:

  • space-around (Übriger Platz im Elternelement wird gleichmäßig zwischen den Items untereinander und zwischen **Items* und Containerrand verteilt.)
  • space-between (Übriger Platz im Elternelement wird zwischen den Items untereinander verteilt.)
  • space-evenly (Wie space-around, aber das Verhältnis der Platzverteilung wird angepasst, um insgesamt die Abstände zwischen Items untereinander und am Rand einheitlicher zu gestalten.)

Ausrichtungs-Möglichkeiten für Grid-Container und Grid-Items

Demo Ausrichtung

Weitere Beispiele

Flexibles Card-Layout (implizite Zeilen und fr)

Hier ein Card-Layout, welches alle Cards auf die selbe Größe bringt, orientiert an der größten Card in der gesamten Grid. Dies funktioniert mit grid-auto-rows in der Größe von 1fr (da die größte Card automatisch die Verteilung der fr festlegt).

Zieht man die mittlere Card im Beispiel größer, passt sich die komplette Grid daran an:

Demo Grid Implizit / Fr / Card-Layout

Responsive Umsortierung benannter Areas

Wie bereits oben ausgeführt, lassen sich mit grid-template-areas leicht Areas benennen und gleichzeitig dabei das Layout festlegen.

Nichts spricht dagegen, das auch einfach mehrmals für die gleiche Grid zu machen — z. B. einmal für jeden Breakpoint, um so das Layout für alle Größen optimal zu steuern. Beispielsweise:

Mobil:

grid-template-areas: "header header"
                     "nav    nav"
                     "main   main"
                     "footer footer";

Tablet:

grid-template-areas: "header header header"
                     "nav    main   main"
                     "nav    footer footer";

Hier würde ab Tabletgröße die Navigation links vom Hauptinhalt stehen. Zuvor stand sie zwischen Header und Footer.

Sinnvolle Fallbacks für ältere Browser

Die CSS Grid ist ja doch noch ein sehr junges Feature. Auch wenn die Verbreitung mit rasender Geschwindigkeit ansteigt, muss eben doch noch berücksichtigt werden, wie ältere Browser das Layout darstellen sollen ohne Grid.

Eine Zusammenfassung sinnvoller Flexbox-basierter Fallbacks für häufige Grid-Layouts wurden auf www.gridtoflex.com zusammengetragen.