Grid Layout/Benannte Linien und Rasterbereiche
Bei jedem Raster stellt sich die Frage nach der optimalen Anzahl der Zeilen und Spalten, und wie man die einzelnen Rasterelemente identifiziert. Im Schach (oder beim Schiffe Versenken Spiel) verwendet man Buchstaben und Zahlen für Zeilen und Spalten. Im Printbereich hat sich ein 12er-Raster (wie bei bootstrap) bewährt, da es durch 2,3,4,6 teilbar ist und so viele verschiedene Kombinationen erlaubt.
Im WebDesign geht es eben aber nicht um ein Design für eine bestimmte Größe mit einem bestimten Seitenverhältnis, sondern um ein Design für eine Vielzahl von Viewports. Je nach Viewportgröße kann es sinnvoll sein, Inhaltsbereiche anders anzuordnen – mal untereinander, mal nebeneinander. Verwendet man dafür Grid-Liniennummern, verliert man sehr schnell den Überblick.
Das Grid Layoutsystem bietet deshalb zwei Möglichkeiten an, Linien und Bereiche im Grid mit Namen zu versehen. Im besten Fall ist es möglich, mit Hilfe der grid-template-areas Eigenschaft das Raster mit seinen Bereichen als ASCII-Schema zu beschreiben und dieses Schema mit Hilfe von @media- oder @container-Abfragen dynamisch an die Größe von Viewport oder Container anzupassen.
Während Responsive Raster ohne Media Queries vor allem den auto-placement-Algorithmus vorstellte, zeigt dieses Tutorial, wie man feste Layouts mit wenig Aufwand im CSS festlegen und formatieren kann.
Inhaltsverzeichnis
Benannte Rasterbereiche
Wir wollen mit dem einfachsten Fall beginnen: Ein Gitter besteht aus einigen Bereichen, die sich nicht überlappen und die den Gitterbereich vollständig ausfüllen. Zum Beispiel so, wie das folgende Bild zeigt:
„Zeichnen“ eines Layouts
Um das in CSS zu übersetzen, muss man sich für die Bereiche kurze und inhaltlich passende Namen ausdenken und diese so aufschreiben, dass der Browser daraus ablesen kann, welcher Bereich wo beginnt und wo endet. Dafür notiert man für jede Gitterzeile einen String, der so viele Bereichsnamen enthält, wie das Grid Spalten haben soll. Überdeckt ein Bereich mehrere Spalten, notiert man seinen Namen für jede Spalte, die er belegt, einmal. Überdeckt ein Bereich mehrere Zeilen, notiert man seinen Namen in mehreren Zeilen.
Wichtig ist dabei nur, dass die so „gemalten“ Bereiche Vierecke bilden.
Bitte wundern Sie sich nicht, dass der main-Bereich über zwei Grid-Spalten geht. Man hätte auf die senkrechte Grid-Linie 3 verzichten und dafür 2fr als Spaltenbreite setzen können. Aber wir werden diese Grid-Linie später noch verwenden.
body {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-areas:
"head head head head"
"nav main main news"
"nav main main links"
"foot foot foot foot";
}
Die gezeigte CSS-Regel legt das Grundlayout einer Webseite mit Hilfe von benannten Bereichen fest. Sie können sich das wie einen Wireframe-Entwurf oder eine ASCII-Zeichung vorstellen.
Zum einen wird durch grid-template-areas festgelegt, wo sich die Bereiche befinden:
- der
head-Bereich befindet sich in Zeile 1 und erstreckt sich über alle vier Spalten -
navnimmt in der linke Spalte die Zeilen 2 und 3 ein -
mainist der große zentrale Bereich in der Mitte (2 Spalten breit, 2 Zeilen hoch) -
newsundlinkssitzen rechts daneben, in Zeile 2 und 3. - Darunter befindet sich der
foot-Bereich, der wieder über alle Spalten geht.
Die Spaltenbreiten werden davon getrennt mit grid-template-columns: repeat(4, 1fr); festgelegt, diese Angabe unterteilt das Raster in 4 gleich breite Spalten. Die repeat()-Funktion kennen wir bereits aus dem vorigen Kapitel. Die Zeilenhöhen sind nicht angegeben, die Grid-Zeilen werden damit nach Bedarf automatisch erzeugt und sind so hoch wie die darin platzierten Elemente.
Auf diese Weise zeichnen wir das Grundlayout der Webseite direkt im CSS.
Das bedeutet:
- Struktur wird sichtbar im Code
- Positionierung erfolgt über Namen statt Zahlen
- das Layout ist sofort als „Bild“ lesbar
Die Namen bewirken für sich erst einmal noch nichts. Sie liefern uns aber ein Gerüst, in das wir unsere HTML-Elemente einhängen können. Dazu verwenden wir wieder die grid-area-Eigenschaft, verwenden nun aber die Variante mit nur einem Wert. Findet der Browser bei grid-area einen Bereichsnamen statt einer Zahl vor, lässt er das Element an den Grenzen dieses Bereichs beginnen und enden. Wie diese Zuordnung genau geschieht, werden wir beschreiben, wenn es um benannte Rasterlinien geht.
header { grid-area: head; }
nav { grid-area: nav; }
main { grid-area: main; }
#links { grid-area: links; }
#news { grid-area: news; }
Die beiden aside-Boxen müssen anhand ihrer Ids angesprochen werden, um den Bereich eindeutig zuordnen zu können.
Auch im Seiteninspektor (hier Firefox 151) können die Rasterbereiche angezeigt werden:
In Chromium-Browsern muss man zunächst auf der Elementansicht das grid-Badge des Grid-Containers anklicken, damit die Rasterbereiche angezeigt werden, und dann im rechten Info-Bereich das Layout-Tab anzeigen. Dort kann man die Anzeige der Bereichsnamen aktivieren.
Anpassungen an andere Viewports
Mit dieser Methode können Anpassungen an andere Viewports sehr schnell vorgenommen werden:
body {
display: grid;
gap: 0.5em;
/* Standard: alles in eine Spalte */
grid-template-areas:
"head"
"main"
"nav"
"news"
"links"
"foot";
}
@media (min-width: 35em) {
body {
/* Etwas mehr Platz: 3 Spalten, news und links */
grid-template-columns: repeat(3, 1fr);
grid-template-areas:
"head head head "
"nav news links"
"main main main"
"foot foot foot";
}
}
@media (min-width: 45em) {
body {
/* Reichlich Platz: 4 Spalten, news und links rechts neben main */
grid-template-columns: repeat(4, 1fr);
grid-template-areas:
"head head head head"
"nav main main news"
"nav main main links"
"foot foot foot foot";
}
}
Standardmäßig (Mobile First!) werde alle Elemente untereinander dargestellt – allerdings wird die Navigation nach unten geschoben, damit der Hauptinhalt sicher im Viewport ist.
Bei einer Viewportgröße von mindestens 35em greift die erste @media-Query, erzeugt 3 gleichbreite Spalten und schaltet die entsprechenden Werte für grid-template-areas um, sodass die Navigation und die Aside-Boxen nebeneinander dargestellt werden. Der Hauptinhalt kommt darunter.
Ab 45em Breite werden vier Spalten und das „normale“ Desktop-Layout erzeugt.
Leerstellen
Manchmal hat man aber auch das Problem, dass für einen Bereich im Grid kein Inhalt verfügbar ist. Oder man braucht dafür einfach keinen Namen. Solche anonyme Zellen werden durch einen Punkt markiert.
body {
display: grid;
gap: 0.5em;
grid-template-columns: 1fr 2fr 1fr;
grid-template-areas:
"head head head"
". main nav"
". main news"
"foot foot foot";
grid-template-rows:
min-content
[nav-start main-top] 1fr
[aside-start] 1fr
[footer-start main-bottom] min-content;
}
Der Header (head) und der Footer (foot) erstrecken sich jeweils über die gesamte Breite des Rasters.
Der Hauptinhalt (main) belegt in der Mitte einen Bereich über zwei Zeilen. Rechts daneben befinden sich die Bereiche nav und news.
Die Punkte (.) links kennzeichnen leere Rasterzellen, denen kein Rasterbereich zugeordnet ist. Sie bilden Weißraum, der den Focus auf den Inhalt in der Mitte legt.
Kurzfassung: grid-template
Es ist unübersichtlich, Bereichsnamen und Bereichsgrößen voneinander getrennt aufzuschreiben. Deshalb gibt es die Shorthand-Eigenschaft grid-template, mit deren zweiter Syntax sich Layoutzeichnung und -bemaßung kombinieren lassen. grid-template erweitert dafür die grid-template-areas-Syntax. Dort haben wir für jede explizite Grid-Zeile einen String, der die Bereichsnamen definiert. Um die Zeilenhöhen anzugeben, notieren wir sie einfach hinter dem entsprechenden String. Der Wert für grid-template-columns wird, abgetrennt durch einen Schrägstrich, einfach dahinter notiert:
grid-template:
"head head head head" auto
"nav main main news" 1fr
"nav main main links" 1fr
"foot foot foot foot" auto
/ 1fr 1fr 1fr 1fr;
Auf diese Weise erhält man eine hübsche Übersichtstabelle für die verwendeten Abmessungen.
grid mit der Syntax von grid-template verwendest, werden die grid-auto-*-Eigenschaften auf ihre Standardwerte zurückgesetzt!Benannte Linien
Benannte Bereiche mit grid-template-areas sind eine elegante Möglichkeit, ein Layout zu beschreiben. Sie hat allerdings den Nachteil, dass sich diese Bereiche nicht überlappen dürfen. So etwas kann zum Beispiel interessant sein, wenn die Navigation auf schmalen Viewports zu einem Button kollabiert und das Navigationsmenü so geöffnet werden soll, dass es einen Teil des main-Bereichs überdeckt. Das ist aber etwas, was wir dem Leser zur Übung überlassen wollen.
Implizit erzeugte Liniennamen
Es ist so, dass ein benannter Bereich, der mit grid-template-areas eingeführt wird, für den Browser gar nicht existiert. Statt dessen weist er den Rasterlinien, die den Bereich begrenzen, Namen zu. Wenn beispielsweise der main-Bereich die Grid-Zeilen 2 bis 4 und die Grid-Spalten 2 bis 3 belegt, dann wird er durch die senkrechten Rasterlinien 2 und 4 und durch die horizontalen Rasterlinien 2 und 5 begrenzt. Der Browser ordnet darum diesen Rasterlinien Namen zu:
Wie man sieht, kann eine Rasterlinie auch mehr als einen Namen tragen, und die Namen für horizontale und vertikale Linien müssen nicht verschieden sein. Der Browser weiß aus den Kontext, ob mit einem Namen eine vertikale oder eine horizontale Linie gemeint ist.
Wenn wir, wie im Abschnitt über benannte Bereiche, eine Angabe wie grid-area: main; machen, dann interpretiert der Browser das zunächst als
grid-area: main / main / main / main;
Um besser mit grid-template-areas zusammenzuarbeiten, sucht der Browser bei Antreffen eines Namens zunächst nicht nach main, sondern für start-Angaben (grid-row-start, grid-column-start) nach main-start und für end-Angaben nach main-end, so dass schlussendlich die folgenden Werte benutzt werden (aufgeführt in der Reihenfolge, wie sie bei grid-area anzugeben sind).
grid-row-start: main-start; grid-column-start: main-start; grid-row-end: main-end; grid-column-end: main-end;
Nur dann, wenn er den um -start oder -end erweiterten Namen nicht findet, sucht der Browser nach einer Rasterlinie, die exakt den angegebenen Namen trägt.
Linien explizit benennen
Außer durch grid-template-areas können Gridlinien auch explizit durch grid-template-rows und grid-template-columns benannt werden. Um die Syntax besser verstehen zu können, möchten wir daran erinnern, dass die grid-template-* Eigenschaften vor allem die Zeilenhöhen und Spaltenbreiten beschreiben, also das, was sich zwischen den Rasterlinien befindet. Stellen wir uns vor, in der CSS-Eigenschaft würde die Stelle, wo sich die Rasterlinien befinden, durch eckige Klammern symbolisiert:
grid-template-columns: [links] 1fr [inhalt-start] 3fr [inhalt-end] 1fr [rechts];
Das wäre ein Grid mit 3 Spalten, die durch vier Rasterlinien abgegrenzt werden. Und tatsächlich ist die Syntax genau so: Man schreibt vor, zwischen oder nach den Breitenangaben ein paar eckige Klammern und dort hinein die Namen, die man für diese Rasterlinie vergeben möchte, durch Leerzeichen getrennt.
Um in diesem Grid ein Element über alle drei Spalte zu platzieren, kann man
grid-column: links / rechts;
schreiben.
Um es in der mittleren Spalte zu platzieren, würde man
grid-column: inhalt-start / inhalt-end;
notieren. Dabei haben die grid-row- und grid-column-Eigenschaften eine besondere Fähigkeit: Ist die Start-Angabe ein Name und die Ende-Angabe fehlt, dann wird die Start-Angabe auch für die Ende-Angabe verwendet. Zusammen mit der automatischen Ergänzung von -start und -end für Liniennamen bewirkt das, dass man auch einfach
grid-column: inhalt;
schreiben kann.
Oder, wenn das Element in Zeile 3 sein soll und man grid-area verwenden möchte:
grid-area: 3 / inhalt-start / 4 / inhalt-end;
Auch hier gibt es wieder Verkürzungen: Lässt man den vierten Wert (Linie für das Spaltenende) weg und ist der zweite Wert (Linie für den Spaltenanfang) ein Name, übernimmt der Browser den Startwert für das Spaltenende. Man kann die Schreibweise also zu
grid-area: 3 / inhalt / 4;
verkürzen.
Benannte Linien in grid-template und Overlay-Bereiche
Benannte Linien sind auch mit der grid-template-Eigenschaft möglich. Hier gibt es zwei Varianten zu beachten
Liniennamen können – in eckigen Klammern – vor oder hinter einer Zeilendefinition notiert werden, um horizontale Linien zu benennen. Zur Erinnerung: eine Zeilendefinition in grid-template besteht aus dem String mit den Spaltennamen und der optionalen Höhenangabe für diese Zeile.
grid-template:
"head head head head" auto
[content-top] "nav main main news" 1fr
"nav main main links" 1fr [content-bottom]
"foot foot foot foot" auto
/ 1fr 1fr [main-center] 1fr 1fr [right];
Dieses Beispiel fügt dem Grid Namen für die horizontalen Linien 2 und 4 und für die vertikale Linie 3 hinzu. Wollte man das Layout mit einem Element überlagern, dass die rechte Hälfte des main-Bereichs und die beiden asides (news und links) überdeckt, dann könnte man es mit
grid-area: content-top / main-center / content-bottom / right;
platzieren. Allerdings wäre das nicht sehr clever und vor allem drückt es nicht aus, was man damit vorhat. Besser ist es, content-top und main-center in overlay-start umzubenennen, sowie content-bottom und right in overlay-end. Damit erhält man einen überlappenden Grid-Bereich so, als ob er von grid-template-areas erstellt worden wäre, und kann diesen Namen einfach bei grid-area einsetzen:
body {
grid-template:
"head head head head" auto
[overlay-start] "nav main main news" 1fr
"nav main main links" 1fr [overlay-end]
"foot foot foot foot" auto
/ 1fr 1fr [overlay-start] 1fr 1fr [overlay-end];
}
#overlay {
grid-area: overlay;
}
Natürlich müssen Sie nicht zwingend das grid-template Shorthand verwenden, um Overlay-Bereiche zu definieren. Wir haben das lediglich hier als Beispiel verwendet; Sie können die Liniennamen auch mit grid-template-rows und grid-template-columns definieren.
Mehrere Rasterlinien mit dem gleichen Namen
Dass eine Rasterlinie mehrere Namen haben kann, wurde schon gezeigt. Dass der gleiche Name für horizontale und vertikale Rasterlinien verwendet werden kann, hatten wir auch schon.
Aber es ist auch möglich, dass der gleiche Name auf einer Achse mehrfach verwendet wird. Über eine grid-template-areas-Definition gelingt das nicht, wenn man aber auf die areas-Syntax verzichtet und alle Liniennamen mit grid-template-rows und grid-template-columns erstellt, hat man freie Hand. Um es noch schlimmer zu machen: Innerhalb der repeat()-Funktion können ebenfalls Liniennamen erzeugt werden.
body {
display: grid;
grid-template-columns:
[nav-start] 10em
repeat(3, [content] 1fr)
grid-template-rows: auto;
}
section:nth-of-type(1) {
grid-column: content 1 / span 1;
}
section:nth-of-type(2) {
grid-column: content 2 / span 1;
}
section:nth-of-type(3) {
grid-column: content 3 / span 1;
}
Die Lösung ist, dass die Namens-Syntax um die laufende Nummer des Namens erweitert werden kann. Im Beispiel bekommen die vertikalen Rasterlinien 2, 3 und 4 den Namen content zugewiesen. Welche gemeint ist, ergibt sich aus der Nummer. Diese Nummer muss ganzzahlig und größer als 0 sein.
content. Wenn ich für eine Section content 4 angebe, dann könnte man erwarten, dass das Grid um implizite Spalten erweitert wird und die Section in der Grid-Spalte 5 erscheint. Das ist aber nicht der Fall, sie erscheint in Grid-Spalte sechs. Zum expliziten Grid gehört nämlich auch noch die Rasterlinie 5, rechts von der content-Spalte 3, und die erste implizite Rasterlinie ist die Nummer 6, wodurch eine leere Spalte entsteht. Um diesen Sprung zu vermeiden, muss man hinter der repeat()-Gruppe ein weiteres Mal [content] angeben, damit die vertikale Rasterlinie 5 nicht namenlos ist.Anwendungsbeispiele
Im CSS-Einstieg gibt es ein Beispiel, in dem als Dekoration ein Querbalken (engl. rule) eingefügt werden soll:
:root {
--navWidth: 12em;
--mainWidth: min(48em, calc(100% - var(--navWidth)));
--headerLineHeight: 3em;
}
@media (width > 30em) {
body {
display: grid;
grid-template: ". nav head ." auto
". nav rule ." var(--headerLineHeight)
". nav main ." auto
". nav foot ." auto /
1fr var(--navWidth) var(--mainWidth) 1fr;
}
body::before {
content: '';
grid-row: rule;
grid-column: 1 / 1;
background: linear-gradient(to right, var(--lila2), var(--lila1));
}
body::after {
content: '';
grid-row: rule;
grid-column: rule-start / -1;
background-image: linear-gradient(to right, var(--lila), var(--lila-darker));
}
}
Dieser Querbalken soll aber erst auf größeren Viewports sichtbar werden.
Im :root-Selektor werden die entsprechenden Werte schon in custom properties notiert. Ab einer Breite von 30em erhält der body zwei Pseudoelemente, die im Grid entsprechend platziert werden.
Formular
Formulare müssen klar und übersichtlich sein. Häufig wurde die Gestaltung durch zahlreiche wrapper-Elemente und Zeilenumbrüche erreicht. Mit dem Grid Layout können wir nun alle Formular-Elemente an einem Raster ausrichten:
form {
display: grid;
grid-template-columns: [labels] auto [controls] auto [oversized] auto;
grid-auto-flow: row dense;
}
form > label {
grid-column: labels;
grid-row: auto;
}
form > input, form > select {
grid-column: controls;
grid-row: auto;
}
#department-block {
grid-row: span 3;
grid-column: oversized;
}
#buttons {
grid-row: auto;
grid-column: 1 / -1;
text-align: end;
}
Das Formular form wird zu einem Raster mit drei Spalten, deren Größe inhaltsbasiert ist. Die entsprechende Rasterlinien erhalten aussagekräftige Namen.
Alle label-Elemente werden in die label-Spalte platziert; mit grid-row: auto; werden sie automatisch auf die Zeilen verteilt.
Analog wird mit den Eingabeelementen verfahren, die in der controls-Spalte platziert werden.
Das select-Menü mit der id department-block wird in die oversized-Spalte positioniert und umspannt dort 3 Zeilen (Auf mobilen Geräten wird das select-Menü durch ein Popup mit radio-Buttons ersetzt.)
Die Buttons werden von einem div umschlossen, das sich wegen grid-column: 1 / -1; über die gesamte Breite des Rasters erstreckt.
Periodensystem der Elemente
Die chemischen Elemente sind in einer Liste, die im Periodensystem nach steigender Kernladungszahl angeordnet sind. Dabei gibt es je nach Anzahl der Elektronen auf der Schale bestimmte Abstände.
Dies hat Gunnar Bittersmann in einem Pen mit grid-row und grid-column positioniert. Die Zahlen innerhalb der nth-of-type-Selektoren entsprechen der jeweiligen Ordnungszahl des Elements.
#periodic-table li:nth-of-type(2)
{
grid-column: 18;
}
#periodic-table li:nth-of-type(5),
#periodic-table li:nth-of-type(13)
{
grid-column: 13;
}
#periodic-table li:nth-of-type(n+58):nth-of-type(-n+71)
{
grid-row: lanthanides;
}
#periodic-table li:nth-of-type(n+90):nth-of-type(-n+103)
{
grid-row: actinides;
}
- codepen.io: Periodic table von Gunnar Bittersmann
Fazit
Mit den benannten Linien und mehr noch, mit den ASCII-Rastern kann man das Layout einer Webseite mit wenigen Zeilen CSS festlegen und über Media Queries entsprechend aktivieren. Allerdings müssen bei komplexeren Layouts oder anders aufgebauten Unterseiten neue Raster angelegt werden.
