Rey’s CSS Haxx: CSS-Grid

Veröffentlicht von Florian - 17. November 2017

Aller guten Dinge sind drei. Das gilt auch für CSS. Denn CSS 3 bringt eine weitere Errungenschaft der Webentwicklung mit sich: das CSS-Grid. Warum dies eine so revolutionäre Technologie ist, sehen wir, wenn wir uns vor Augen halten, wie Websiten bisher designed wurden. Um den immer komplexeren Designs unserer Webdesigner-Kollegen gerecht zu werden, ist es

CSS-Grid

Die Float-Methode.

Denn wie hat man die Positionierung von Elementen bisher gelöst? Und wer jetzt von Tabellen anfängt, der darf gerne auch weiterhin seine Webseiten mit Flash bauen. Die bisher übliche Vorgehensweise war die Float-Methode. Denn wenn man zwei Boxen nebeneinander platzieren möchte, reicht ein einfaches float:left.

.box {
  background-color:red;
  float:left;
  width:200px;
  height:200px;
  margin:10px;
}
<div class="box">Box 1</div>
<div class="box">Box 2</div>

So weit, so gut. Damit hätte man zunächst keine Probleme. Was passiert aber, wenn wir um diese beiden Boxen noch einen Wrapper mit einer blauen Hintergrundfarbe bauen wollen?

.wrapper {
  background-color:blue;
}
<div class="wrapper">
  <div class="box">Box 1</div>
  <div class="box">Box 2</div>
</div>

Um an dieser Stelle ein altes Sprichwort zu verwenden: „Wie Sie sehen, sehen Sie nichts.“

What just happened?

Nun, warum ist das so? Wir wissen, dass ein HTML Dokument immer von oben nach unten gerendert wird. Wenn wir nun auf ein Element das float Attribut setzen, so wird es aus der bestehenden Struktur herausgebrochen, da der restliche Inhalt um diese Boxen fließt. Daher weiß der umschließende Wrapper nicht, wie hoch er sein soll, da die beiden Boxen nicht mehr zur eigentlichen Layout-Struktur gehören. Er stellt sich also mit einer Höhe von 0 Pixeln dar, da er technisch gesehen keinen Inhalt hat. Für uns gilt es nun, den Float-Prozess der beiden Boxen wieder zu beenden. Ein schlechter Webentwickler würde nun folgendes tun:

<div class="wrapper">
  <div class="box">Box 1</div>
  <div class="box">Box 2</div>
  <div style="clear:both;"></div>
</div>

Ja, es führt letztendlich zum gewünschten Ergebnis. Aber nein, es ist aus zwei Gründen sehr, sehr böse. Zum Einen wird hier Inline-Style verwendet, was nie eine gute Lösung ist. Zum Anderen wird hier ein leerer <div> eingebaut, um ein Layout-Problem zu beheben. Die weitaus üblichere Variante ist der berühmte

Clearfix

Hierbei nutzen wir eine Kombination aus dem Pyseudo-Selektor :after und dem content Attribut, um dem Wrapper vorzugaukeln, es würde noch weiterer Inhalt folgen. Dieser Pseudo-Inhalt klärt dann den Float der beiden Boxen und stellt die normale Layout-Struktur wieder her. Frei nach dem Motto: „Wer floatet muss auch clearen!“

.clearfix:after {
  content:'';
  clear:both;
  display:block;
}
<div class="wrapper clearfix">
  <div class="box">Box 1</div>
  <div class="box">Box 2</div>
</div>

Flex-Box

Reisen wir nun in paar Jahre in der Zeit weiter, dann treffen wir auf die Flex-Box-Lösung. Die „aktuelle“ Methode, um Elemente auf einer Seite zu positionieren. Und Flex-Box ist nicht nur eleganter als Float, sondern auch stabiler, da es die Layout-Struktur nicht durchbricht. Sehen wir uns also wieder unsere beiden Boxen an:

.wrapper {
  display:flex;
}
.box {
  background-color:red;
  width:200px;
  height:200px;
  margin:10px;
}
<div class="wrapper">
  <div class="box">Box 1</div>
  <div class="box">Box 2</div>
</div>

Diese werden wieder in Höhe und Breite definiert und bekommen eine Hintergrundfarbe und einen Abstand, damit das schöner aussieht. Webdesigner wären hier bestimmt sehr stolz. Die Boxen werden nun aber nicht gefloatet, sondern der umschließende Wrapper erhält ein display:flex. Dies bewirkt, dass die direkten Kindelemente automatisch als Flex-Items behandelt und „in Reihe geschaltet“ werden. Sieht zwar gleich aus wie bei Float, ist aber besser. Hier muss man jedoch beachten, dass sich display:flex immer nur auf die direkten Kindelemente bezieht. Möchten wir aber verschachtelte Layouts erstellen, so müssen wir unseren Code ein wenig umstellen:

.content {
  height:410px;
}
<div class="wrapper">
  <div class="container">
    <div class="box">Box 1</div>
    <div class="box">Box 2</div>
  </div>
  <div class="box content">Box 3</div>
</div>

Die beiden Boxen werden nun also in einen zusätzlichen Container .container gepackt. Ergänzend bauen wir einen neuen Container .content ein, der gleichzeitig als dritte Box fungiert. Dadurch werden die beiden Kindelemente von .wrapper geflext und nebeneinander dagestellt. Die Boxen innerhalb von .container haben nun kein display:flex mehr und werden daher ganz normal untereinander gerendert.

Zwischenstand

Bisher ist keine der zuvor genannten Lösungen wirklich schick oder effektiv. Zwar hat Flex-Box einige Vorteile gegenüber Float, versagt dann aber auch bei komplexeren Layouts. Darüber hinaus benötigt man bei beiden Methoden zusätzlichen und meist unnötigen HTML-Code, um das Layout zu strukturieren. Und kein fauler Webentwickler will auch noch unnötigen Code schreiben. Es muss also eine bessere Lösung geben…

CSS-Grid

Und die gibt es in der Tat! Denn die Götter des W3C haben uns mit dem CSS-Grid beglückt. Und das funktioniert genau so, wie man sich das vorstellt. Wie ein „echtes“ Gridsystem, wie man es schon von klassischen Medien wie der Print-Zeitung kennt. Ja, liebe Kinder, früher hat man die Zeitung auf Papier gedruckt. Schauen wir uns das CSS-Grid doch einmal an:

header {
  background-color:red;
}
h1 {
  background-color:green;
  margin:0;
}
main {
  background-color:blue;
}
aside {
  background-color:yellow;
}
footer {
  background-color:cyan;
}
<div class="grid-container">
  <header>Header</header>
  <h1>H1</h1>
  <main>Main</main>
  <aside>Aside</aside>
  <footer>Footer</footer>
</div>

Von Reihen und Spalten

Wir haben hier ein paar Elemente, wie sie auf jeder modernen Webseite vorkommen. Diese sind von einem Container .grid-container umgeben und in verschiedenen Farben angelegt. Standardmäßig werden diese wieder alle untereinander dargestellt. Hier möchten wir nun unser CSS-Grid anwenden. Dazu geben wir dem Container .grid-container ein display:grid. Zusätzlich definieren wir die grid-template-columns und die grid-template-rows.

.grid-container {
  display:grid;
  grid-template-columns:2fr 1fr 1fr;
  grid-template-rows:auto 1fr 3fr;
}

Da man Columns (Spalten) und Rows (Reihen) definieren kann, wird hier schon deutlich, dass Grid – im Gegensatz zu Float und Flex-Box – zweidimensional arbeitet. Hierbei ist dann .grid-container der, wie der Name schon sagt, Grid-Container. Alle direkten Kindelemente sind dabei die Grid-Items, die im Grid positioniert werden können. Über grid-template-columns und grid-template-rows legen wir die Grid-Lines an. In unserem Fall haben wir jeweils drei Spalten und drei Reihen, demnach also jeweils 4 Grid-Lines. Diese Grid-Lines teilen das Grid dann in verschiedene Grid-Cells, also Zellen. In einer Matrix betrachtet, sieht das dann ungefähr so aus: Line Line Line Line Line Line Line Line Cell Line Cell Line Cell Line Line Line Line Line Line Line Line Line Cell Line Cell Line Cell Line Line Line Line Line Line Line Line Line Cell Line Cell Line Cell Line Line Line Line Line Line Line Line Die Größe der Grid-Cells haben wir bereits in unserem CSS definiert. Und zwar über die neue Einheit fr (Fraction). Diese Einheit nimmt den vorhandenen Platz und teilt ihn in gleichgroße Bereiche ein. Der genaue Wert für eine Fraction kann also variieren. Am Beispiel unserer grid-template-columns erkennen wir, dass die erste Spalte also genau so groß ist, wie die zweite und die dritte Spalte zusammen. Bei unseren Reihen ist die zweite Reihe eine Fraction groß. Die dritte Reihen also dreimal so viel. Und die erste Reihe nimmt über auto einfach den Platz, der noch übrig ist. Ergänzend sei gesagt, dass alle bisher verfügbaren Einheiten (px, %, em, etc.) in einem Grid Template verwendet und auch vermischt werden können.

Die Zellen im Grid

In diesem Standard-Aufbau werden unseren Elemente, unsere Grid-Items, jetzt einfach von links nach rechts in die verfügbaren Grid-Cells gepackt. Das ist schon mal nicht schlecht, aber noch nicht ganz das, was wir wollen. Was wir wollen ist, die Elemente manuell im Grid platzieren zu können. Und dazu haben wir zwei Möglichkeiten:

header {
  grid-column:1/4;
  grid-row:1/2;
}
h1 {
  grid-column:2/4;
  grid-row:2/3;
}
main {
  grid-column:1/2;
  grid-row:2/4;
}

Bei der ersten Möglichkeit richten wir die einzelnen Elemente über die Grid-Lines aus. Unser header würde sich in dem Fall über die komplette Breite einer Spalte ziehen, da in grid-column der Start bei Linie 1 und das Ende bei Linie 4 liegt. Wir erinnern uns: wir haben insgesamt vier vertikale Linien in unserem Grid. Die Höhe zieht sich über genau eine Reihe. Denn in grid-row verläuft sie von Linie 1 bis zu Linie 2. Bei der h1 und main wird es jetzt interessant. Die h1 ist laut grid-column auf der rechten Seite (Linie 2 bis 4) des Grids platziert. Der main auf der linken Seite (Linie 1 bis 2). Im Code selbst ist das allerdings direkt umgekehrt. Hier sollte eigentlich die h1 zuerst, also links, stehen und dann erst der main. Hier erkennt man die Flexibilität, die das CSS-Grid bietet. Alle weiteren Elemente die keine spezielle Definition haben, werden wie üblich in Leserichtung in die übrigen Grid-Cells gepackt.

Grid-Area

Natürlich ist das Ausrichten von Elementen anhand von Nummern ein wenig unübersichtlich. Und dazu gibt es Methode Nummer 2. Die Grid-Area. Diese muss zunächst mal für alle Elemente definiert werden:

header {
  grid-area:header;
}
h1 {
  grid-area:h1;
}
main {
  grid-area:main;
}
aside {
  grid-area:aside;
}
footer {
  grid-area:footer;
}

Jedes Element, das wir im Grid ausrichten möchten, erhält über grid-area eine Art ID. Diese kann beliebig gewählt werden und repräsentiert das jeweilige Element in der Grid-Matrix. Denn im umschließenden .grid-container können wir nun mittels grid-template-areas eine klassische ASCII-Matrix der Elemente anlegen:

.grid-container {
  grid-template-areas:
    'header header header'
    'main h1 h1'
    'main aside footer';
}

Hier beschreiben wir nun, wie die Elemente platziert werden sollen. Wir haben also unser Grid mit drei Spalten und drei Reihen. In der ersten Reihe wird nun in allen drei Spalten das header Element über seine grid-area platziert. Der main befindet sich in der ersten Spalte der zweiten Reihe, in den beiden weiteren die h1. In der dritten Reihe finden wir wieder den main in der ersten Spalte (dieser zieht sich in der ersten Spalte also über zwei Reihen) und dann noch das aside und den footer. Wichtig ist hier, dass alle Elemente, die eine grid-area definiert haben, auch zwingend in grid-template-areas untergebracht werden müssen.

Fazit

Und das ist auch schon der ganze Trick dabei. Über das zweidimensionale CSS-Grid ist es nun möglich, per ASCII-Matrix eine gesamte Website zu layouten. Auch ein Umstellen der Elemente für responsive Webseiten ist kein Problem mehr. Hier müssen wir in den Media Queries nur die Grid-Area umschreiben. In Sachen Browserkompatibilität sieht das Ganze auch nicht schlecht aus. Alle aktuellen Browser unterstützen das CSS-Grid bereits. Eine Ausnahme bildet wie immer der Internet Explorer, der in der 11er Version noch die alte Spezifikation des Grids unterstützt. Allerdings muss man der Fairness halber erwähnen, dass das CSS-Grid einst für den IE konzipiert wurde, die anderen Browser sich dann jedoch auf eine andere Spezifikation geeinigt haben. Der Edge arbeitet aber gerade an einer Adaption des aktuellen Grids. Weitere Informationen bietet hier übrigens der Ultimative CSS-Grid Guide.

Das könnte Dich auch interessieren

Responsive Webdesign erklärt mit der responsive Cat

Jeder von uns kennt die Frage: "Was ist responsive Webdesign?".

4/4: Aber trotzdem nicht das Ende

Die Wallfahrt zu meiner perfekten Routine neigt sich so langsam einem Ende zu. Doch wie ich in meinem ersten Blog Beitrag schon gesagt habe, sehe ich mein ganze...

Strategien zur langfristigen Mitarbeiter-Bindung

Die Bindung kompetenter und erfahrener Mitarbeiter an das Unternehmen ist keine Tagesaufgabe, sondern sollte eine ständige Priorität darstellen, denn: keine M...