Schau mal, keine Media Queries! Responsive Layouts mit CSS Grid

Avatar of Juan Martín García
Juan Martín García am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

CSS Grid hat nicht nur die Art und Weise, wie wir Layouts für das Web denken und erstellen, neu gestaltet, sondern auch dazu beigetragen, resilienteren Code zu schreiben, indem es „hacky“ Techniken ersetzt, die wir zuvor verwendet haben, und in einigen Fällen die Notwendigkeit, sich auf Code für bestimmte Auflösungen und Viewports zu verlassen, beseitigt hat. Das Coole an dieser Ära der Webentwicklung ist, dass wir immer mehr mit weniger Codezeilen erreichen können.

In diesem Artikel werden wir uns mit der Leistungsfähigkeit von CSS Grid vertraut machen, indem wir ein paar gängige responsive Navigationslayouts erstellen. Es ist einfacher als Sie vielleicht denken, und da CSS Grid mit Blick auf Responsivität entwickelt wurde, wird es weniger Code benötigen als das Schreiben von Media Queries überall. Legen wir los!

Layout #1: Hero-Inhalt und Liste von Artikeln

Siehe den Pen
Hero Content and List of Articles
von Juan Martín García (@imjuangarcia)
auf CodePen.

Wir beginnen diese Reihe von Beispielen mit der Erstellung eines gängigen Website-Layouts: Ein Hero-Bereich über die gesamte Breite, darunter ein Raster von Karten.

Beide Elemente reagieren auf die Größenänderung des Fensters und passen sich entsprechend an. Obwohl dies auf den ersten Blick viel Code zu sein scheint, wird das responsive Verhalten mit nur sechs Zeilen CSS Grid-Code realisiert, und das ohne eine einzige @media-Regel. Zerlegen wir den Code, um zu sehen, was passiert.

Der Hero-Bereich

Werfen wir einen Blick auf den Code für das .hero-Element.

<section class="hero">
  <h1>You thirsty?</h1>
  <article>
    <p>Explore local breweries with just one click and stirred by starlight across the centuries light years great turbulent clouds circumnavigated paroxysm of global death.</p>
    <a href="#breweries">Browse Breweries</a>
  </article>
</section>
.hero {
  /* Photo by mnm.all on Unsplash */
  background: url('https://images.unsplash.com/photo-1518176258769-f227c798150e') center;
  background-size: cover;
  padding: 4rem 2rem;

  /* Grid styles */
  display: grid;
  align-items: center;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

Wir haben eine Reihe von Hintergrundstilen, um den Bierhintergrund zu aktivieren, etwas Padding, um den Inhalt vom Bildschirmrand zu trennen, und dann drei Zeilen Grid-Stile.

  1. Die erste Zeile (display: grid;) ändert das Verhalten des .hero-Elements zu einem Grid-Container. Das bedeutet, dass die Elemente innerhalb von .hero jetzt Grid-Elemente sind.
  2. Die zweite Zeile (align-items: center;) wird die Spalten unseres Grids vertikal zentrieren. Aber diese beiden Zeilen tun nichts allein, bis wir die Spalten unseres Grids festlegen.
  3. Und hier kommt die dritte Zeile ins Spiel. In dieser einen Eigenschaft passiert eine Menge, also gehen wir Schritt für Schritt vor.

Die repeat()-Funktion

Im Allgemeinen definieren wir normalerweise unsere Spalten und Zeilen in einem CSS Grid, indem wir den Wert für jede Spur nach der Definition der Eigenschaft hinzufügen, wie hier:

.element {
  /* This will result on four columns, each one of 1fr */
  grid-template-columns: 1fr 1fr 1fr 1fr;
  /* This will result on two rows, each one of 300px */
  grid-template-rows: 300px 300px;
}

Das ist ziemlich langweilig. Wir können die repeat()-Funktion verwenden, um das weniger umständlich und leichter verständlich zu machen. Die Funktion nimmt zwei Parameter an:

  1. Die Anzahl der Wiederholungen des Wertes.
  2. Der Wert selbst.

Nachdem wir unseren Code mit repeat() refaktorisiert haben, sollten wir die gleichen Ergebnisse von diesen Codezeilen erwarten:

.element {
  /* this is the same as grid-template-columns: 1fr 1fr 1fr 1fr; */
  grid-template-columns: repeat(4, 1fr);
  /* this is the same as grid-template-rows: 300px 300px; */
  grid-template-rows: repeat(2, 300px);
}

Viel sauberer, oder?

Die minmax()-Funktion

Nun definieren die obigen Beispiele explizit Größen für die Spuren (1fr und 300px). Das mag in manchen Szenarien funktionieren, aber für unser Bierbeispiel hier müssen wir die Größe der Spur automatisch berechnen können, basierend auf der Breite des Viewports, und die Anzahl der angezeigten Spalten automatisch anpassen. Um das tun zu können, definieren wir einen Wertebereich mit der minmax()-Funktion. Was werden wir definieren? Sie haben es wahrscheinlich schon erraten: Die *minimale* und *maximale* Größe, auf die sich diese Spalten ändern können.

Im Hero für unser Bierbeispiel oben haben wir unsere minmax()-Eigenschaft auf 240px als Mindestgröße und 1fr als Maximalgröße gesetzt. fr-Einheiten, falls Sie sie noch nie gehört haben, stehen für fraktionale Einheiten. Niemand kann sie besser erklären als Jen Simmons in diesem Video und Robin Rendle in diesem Beitrag.

Verwendung des Firefox Grid Inspectors, um die Größenänderung der Spur beim Ändern der Größe zu überprüfen

Dies führt dazu, dass unsere Spuren 1fr sind, wenn genügend Platz im Viewport vorhanden ist (alias Desktop-Auflösungen), und 240px, wenn nicht genügend Platz für beide Spalten vorhanden ist (wie auf Mobilgeräten). Deshalb wachsen sie schön, wenn wir unseren Browser breiter machen, da sie den verbleibenden Platz einnehmen und ihn gleichmäßig auf die vorhandenen Spalten verteilen. Nun kommen wir zum letzten Puzzleteil!

Das auto-fit-Schlüsselwort

Das Schlüsselwort auto-fit ermöglicht es uns, unsere Spalten in Zeilen umzubrechen, wenn nicht genügend Platz im Viewport vorhanden ist, um den Mindestwert von 240px ohne Überlauf des Inhalts unterzubringen. Sara Soueidan hat einen ausgezeichneten Artikel über das automatische Anpassen der Spaltengröße mit den Schlüsselwörtern auto-fill und auto-fit geschrieben, falls Sie tiefer in das einsteigen möchten, was unter der Haube passiert. Nun, mit dem letzten Codeabschnitt an Ort und Stelle, sollten wir dieses Ergebnis erzielen können:

Die Spalte wird automatisch umgebrochen, wenn im Viewport nicht genügend Platz ist

Die Artikelliste

Nachdem wir nun das Verhalten der Elemente in unserem Hero-Element gründlich überprüft haben, ist es wahrscheinlich, dass Ihnen die ersten beiden Zeilen CSS-Code für die darunter liegende Brauereiliste bereits vertraut erscheinen.

.breweries > ul {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  grid-gap: 1rem;
}

Das stimmt! Wir verwenden genau den gleichen Ansatz: In der ersten Zeile definieren wir unser Grid, in der zweiten skalieren wir unsere Spuren mit demselben magischen Einzeiler, und in der dritten Zeile legen wir einen Abstand für diese Spalten fest. Nichts Neues unter der Sonne, und was wirklich toll daran ist, ist, dass unser Code resilient genug ist, um die Anzahl der Spuren und ihre Größen entsprechend der Anzahl der Elemente in unserer ungeordneten Liste anzupassen.

Das Grid reagiert auf die Änderung der Anzahl der Spuren und passt das Layout an.

Das war's, Leute! Ein vollständig responsives Website-Layout mit nur sechs Zeilen CSS-Code. Nicht schlecht, oder? Stellen Sie sicher, dass Sie den Quellcode überprüfen und mit diesem Beispiel auf CodePen experimentieren.

Layout #2: Vollbreites Bildergallerie

Siehe den Pen
Full Width Image Gallery
von Juan Martín García (@imjuangarcia)
auf CodePen.

Im nächsten Beispiel nutzen wir die Kraft unserer neu erlernten Kombination aus repeat(), auto-fit und minmax(), um diese responsive Bildergallerie zu erstellen. Wir werden auch die Spuren mit grid-column und grid-row skalieren und über die praktische Eigenschaft:Wert-Kombination von grid-auto-flow: dense; lernen, die es uns ermöglicht, das Standardverhalten der Elemente zu ändern, die nicht in unsere expliziten Spuren passen: Anstatt sich in neue Zeilen oder Spalten umzubrechen, lassen wir sie in die ungenutzten Bereiche unseres Grids passen. Tauchen wir ein in den Code!

Die Grid-Einrichtung

Das Grid wird mit unserer bekannten display: grid;-Eigenschaft erstellt, wobei Spalten mit repeat(), auto-fit und minmax() definiert werden. Wir haben auch eine Reihe von Zeilen mit einer repeat()-Funktion hinzugefügt und einen Abstand für unsere Bilder mit grid-gap definiert. Aber der neue Spieler hier ist grid-auto-flow: dense;. Dazu kommen wir gleich.

.gallery > .gallery__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  grid-template-rows: repeat(6, 200px);
  grid-gap: 1rem;
  grid-auto-flow: dense;
}

Wir haben auch ein Wiederholungsmuster mit dem Pseudo-Selektor nth-child() erstellt, um unterschiedliche Größen für unsere Spuren mit grid-column und grid-row festzulegen. Beachten Sie hier, dass wir das Schlüsselwort span verwenden, um dem ausgewählten Element zu erlauben, mehr als eine Spalte oder Zeile einzunehmen.

/* This will create 2x images every 4 elements */
.gallery > .gallery__list > li:nth-child(4n) {
  grid-column: span 2; /* Spans two columns */
  grid-row: span 2; /* Spans two rows */
}

/* This will create 3x images every 8 elements */
.gallery > .gallery__list > li:nth-child(8n) {
  grid-column: span 3;
  grid-row: span 3;
}

Und schließlich stellen wir sicher, dass unsere Bilder den gesamten Bereich ihres Containers abdecken, egal ob es 1x, 2x oder 3x ist, mit object-fit: cover;. Wenn Sie noch nie von object-fit gehört haben, funktioniert es ziemlich ähnlich wie background-image, aber mit HTML <img>-Tags.

.gallery > .gallery__list > li > figure > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Nun, das eigentliche Ding hier ist grid-auto-flow: dense;. Schauen Sie, was passiert, wenn wir das aus unserem Code entfernen.

Das Entfernen von grid-auto-flow: dense; führt zu inkonsistenten Platzierungen der Elemente im Grid.

Sehen Sie diese Lücken in unserem schön gestalteten Grid? Das liegt daran, dass einige der Elemente darauf 2x oder 3x Felder belegen, und wenn nicht genügend Platz in unseren Spuren ist, um sie unterzubringen, werden sie in eine neue Zeile umgebrochen, da dies das Standardverhalten ist. Indem wir es von row auf dense ändern, sagen wir dem Grid, dass es alle Lücken füllen soll, die wir haben, mit Elementen, die hineinpassen könnten, unabhängig von ihrer Reihenfolge im DOM.

Deshalb ist diese Technik besonders nützlich für Dinge wie Bildergallerien, aber vielleicht nicht für andere Anwendungsfälle geeignet, bei denen Sie die Reihenfolge des Markups beibehalten müssen. Fühlen Sie sich frei, mit der CodePen-Demo zu spielen, um die Unterschiede zwischen der Platzierung der Elemente zu sehen.

Layout #3: Trello-ähnliches Kartenlayout

Siehe den Pen
Trello-Style Card Layout
von Juan Martín García (@imjuangarcia)
auf CodePen.

Nun, zum letzten Demo, wo wir die Fähigkeit nutzen, Grids zu verschachteln, um dieses Trello Board nachzubilden. Wir werden ein Grid erstellen, um unsere vier verschiedenen Spalten zu halten, und darin werden wir ein Kind-Grid für unsere Karten erstellen. Auch wenn dieses Beispiel keine neuen Eigenschaften oder revolutionären Methoden erforscht, hilft es uns, ein Gefühl dafür zu bekommen, wie einfach es ist, komplexe Layouts mit wenigen Zeilen CSS-Code zu erstellen. Dieses Demo hat viel zusätzlichen Code, um das Styling des Trello-Layouts zu erreichen, daher konzentrieren wir uns ausschließlich auf die Grid-Stile.

Die Spalten

Um die vier Spalten zu erstellen, verwenden wir display: grid; auf dem Container und unseren magischen Einzeiler für grid-template-columns. Wir definieren auch einen Abstand zwischen ihnen und verwenden align-items: flex-start;, um sicherzustellen, dass unsere Spalten nicht bis zum unteren Rand des Bildschirms gestreckt werden.

.column__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  grid-gap: .5rem;
  align-items: flex-start;
}

Nun ist das ursprüngliche Trello nicht standardmäßig responsiv: Wenn Sie Ihren Browser auf einem Trello Board vergrößern, werden Sie feststellen, dass Sie am Ende eine horizontale Scrollleiste für Ihre Spalten haben, anstatt sie in eine neue Zeile umzubrechen. Wir folgen diesem Verhalten hier nicht, da wir responsive Layouts erstellen wollen, aber falls Sie neugierig sind und Trello's Funktionalität nachahmen möchten, können Sie dies erreichen, indem Sie zwei weitere Zeilen CSS-Code hinzufügen.

.column__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  grid-gap: .5rem;
  align-items: flex-start;
  /* Uncomment these lines if you want to have the standard Trello behavior instead of the column wrapping */
  grid-auto-flow: column;
  grid-auto-columns: minmax(260px, 1fr);
}

Wir haben grid-auto-flow in unserem vorherigen Demo gelernt und festgestellt, dass es uns ermöglicht, die Funktionsweise des Auto-Placement-Algorithmus zu steuern und wie implizite Elemente in den Fluss des Grids eingefügt werden sollten. Das Standardverhalten ist row, was bedeutet, dass jedes zusätzliche Element, das nicht in unser Grid passt, in eine neue Zeile umbrochen wird. Wir haben dies in unserem vorherigen Demo auf dense geändert, und wir werden es in diesem auf column ändern: Auf diese Weise landet jede neue Spalte hier in einer impliziten Spalte und hat eine horizontale Scrollleiste. Wir werden auch eine Breite für diese automatisch generierten Spalten mit der Eigenschaft grid-auto-columns definieren.

Die Änderung der Eigenschaft grid-auto-flow lässt dieses Demo wie das reale Trello funktionieren.

Die Karten

Für das Karten-Grid verwenden wir einen ähnlichen Ansatz. Wir display: grid; auf dem Container. Wir werden hier keine Spalten definieren, da wir keine wollen, und wir setzen grid-template-rows: auto;, um zu vermeiden, dass alle Karten die gleiche Höhe haben – wir wollen, dass einige größer und andere kleiner sind, basierend auf der Art des Inhalts, der ihnen hinzugefügt wird.

.card__list {
  display: grid;
  grid-template-rows: auto;
  grid-gap: .5rem;
  margin: .5rem 0;
}

Und, wieder einmal, das war's, Leute! Zwei weitere Zeilen, um einen Abstand und einen Rand zu den Karten hinzuzufügen, und wir sind fertig! Alles andere im Pen ist Standard-CSS, um das Trello-Aussehen und -Gefühl zu erzielen.

Sind Media Queries also tot?

Früher, als wir Layouts mit display: inline-block oder Floats erstellten, machten Media Queries viel Sinn, um die Größe unserer Elemente zu ändern, wenn der Viewport kleiner wurde. Aber jetzt, mit den unglaublich leistungsstarken Layouts, die wir mit ein paar CSS-Zeilen erstellen können, sind Sie vielleicht versucht zu denken, dass Media Queries dem Untergang geweiht sind. Ich bin stark anderer Meinung: Ich glaube, wir sollten die Art und Weise, wie wir über sie denken, ändern und sie daher anders einsetzen.

Wie Rachel Andrew vor einem Jahr feststellte, sollten wir Media Queries verwenden, um unser Layout zu reparieren, wenn es bricht, anstatt Geräte anzuzielen: Davon gibt es so viele! Mit dem Aufkommen von Media Queries Level 4 und 5 können wir jetzt nicht nur Bildschirmgrößen erkennen, sondern auch Zeigertypen. Infolgedessen können wir in die Systemeinstellungen eines Benutzers eintauchen und unseren Code für diejenigen anpassen, die reduzierte Bewegung bevorzugen oder ob wir invertierte Farben verwenden sollen. Das bedeutet, dass Media Queries nicht tot sind; andererseits würde ich sagen, dass es eine aufregende Zeit ist, Media Queries zu verwenden, aber wir müssen lernen, sie richtig einzusetzen. In der Zwischenzeit sparen Ihnen die Erstellung robuster Layouts mit modernen Techniken wie Flexbox oder CSS Grid viel Zeit, Code und Kopfschmerzen.