Coole kleine CSS Grid Tricks für deinen Blog

Avatar of Ana Tudor
Ana Tudor am

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

Ich habe CSS vor etwa einem Jahrzehnt entdeckt, als ich versuchte, das Aussehen eines Blogs zu ändern, den ich erstellt hatte. Schon bald konnte ich coole Dinge mit mathematischeren und daher leichter verständlichen Funktionen wie Transformationen codieren. Andere Bereiche von CSS, wie das Layout, blieben jedoch eine ständige Quelle des Leidens.

In diesem Beitrag geht es um ein Problem, auf das ich vor etwa einem Jahrzehnt gestoßen bin und das ich bis vor kurzem nicht auf intelligente Weise lösen konnte. Speziell geht es darum, wie ich eine Lösung für ein langwieriges Problem mit einer modernen CSS Grid-Technik gefunden habe, die mir dabei sogar noch coolere Ergebnisse als ursprünglich vorgestellt lieferte.

Beachten Sie, dass dies kein Tutorial zur optimalen Nutzung von CSS Grid ist, sondern eher ein Durchgang durch meinen eigenen Lernprozess.

Das Problem

Eines der ersten Dinge, die ich auf diesem Blog untergebracht habe, waren zufällige Fotos aus der Stadt. Daher hatte ich die Idee, ein Raster von Thumbnails mit fester Größe zu haben. Für ein schöneres Aussehen wollte ich, dass dieses Raster in Bezug auf die darüber und darunter liegenden Absätze mittig ausgerichtet ist, aber gleichzeitig wollte ich, dass die Thumbnails in der letzten Zeile relativ zum Raster linksbündig ausgerichtet sind. Die Breite des Beitrags (und die Breite des Rasters darin) würde von der Ansicht abhängen.

Der HTML-Code sieht etwa so aus

<section class='post__content'>
  <p><!-- some text --></p>
  <div class='grid--thumbs'>
    <a href='full-size-image.jpg'>
      <img src='thumb-image.jpg' alt='image description'/>
    </a>
    <!-- more such thumbnails -->
  </div>
  <p><!-- some more text --></p>
</section>

Das mag einfach erscheinen, erwies sich aber als eines der schwierigsten CSS-Probleme, mit denen ich je konfrontiert war.

Weniger als ideale Lösungen

Das sind Dinge, die ich im Laufe der Jahre ausprobiert oder vorgeschlagen gesehen habe, die mich aber nie wirklich weitergebracht haben.

Floating-Unmöglichkeit

Floats erwiesen sich als Sackgasse, da ich nicht herausfinden konnte, wie ich das Raster auf diese Weise mittig ausrichten kann.

.grid--thumbs { overflow: hidden; }

.grid--thumbs a { float: left; }

Die folgende Demo zeigt den Float-Versuch. Ändern Sie die Größe des Einbettungsfeldes, um zu sehen, wie sie sich bei verschiedenen Ansichtsfensterbreiten verhalten.

inline-block Wahnsinn

Zuerst schien dies eine bessere Idee zu sein

.grid--thumbs { text-align: center }

.grid--thumbs a { display: inline-block }

Außer, dass es sich herausstellte, dass es das nicht war

Die letzte Zeile ist in diesem Fall nicht linksbündig.

Irgendwann, dank eines zufälligen CSS-Auto-Completes auf CodePen, erfuhr ich von einer Eigenschaft namens text-align-last, die bestimmt, wie die letzte Zeile eines Blocks ausgerichtet wird.

Leider war das Setzen von text-align-last: left auf dem Grid auch nicht die Lösung, nach der ich gesucht habe

Zu diesem Zeitpunkt erwog ich tatsächlich, die Idee eines mittig ausgerichteten Grids aufzugeben. Könnte eine Kombination aus text-align: justified und text-align-last: left auf dem Grid ein besseres Ergebnis erzielen?

Nun, es stellt sich heraus, dass es das nicht tut. Das heißt, es sei denn, es gibt nur ein Thumbnail in der letzten Zeile und die Abstände zwischen den Spalten sind nicht zu groß. Ändern Sie die Größe des folgenden Einbettungsfeldes, um zu sehen, was ich meine.

Das ist ziemlich genau, wo ich vor zwei Jahren war, nach neun Jahren des Versuchens und Scheiterns, eine Lösung für dieses Problem zu finden.

Unordentliche Flexbox-Hacks

Eine Flexbox-Lösung, die zunächst zu funktionieren schien, war das Hinzufügen eines `::after`-Pseudo-Elements zum Grid und das Setzen von `flex: 1` sowohl auf die Thumbnails als auch auf dieses Pseudo-Element

.grid--thumbs {
  display: flex;
  flex-wrap: wrap;
	
  a, &::after { flex: 1; }
	
  img { margin: auto; }
	
  &:after { content: 'AFTER'; }
}

Die folgende Demo zeigt, wie diese Methode funktioniert. Ich habe den Thumbnails und dem `::after`-Pseudo-Element lila Umrandungen gegeben, um zu verdeutlichen, was vor sich geht.

Das ist nicht ganz das, was ich wollte, weil das Raster der Thumbnails nicht mittig ausgerichtet ist. Das gesagt, es sieht nicht schlecht aus ... solange die letzte Zeile genau ein Element weniger Bild hat als die anderen. Sobald sich das ändert, bricht das Layout jedoch zusammen, wenn mehr oder keine Elemente fehlen.

Screenshot collage. Shows how the layout breaks when the last row is not missing exactly one item to be full.
Warum der `::after`-Hack nicht zuverlässig ist.

Das war eine hackige Idee. Eine andere ist, kein Pseudo-Element zu verwenden, sondern so viele leere Divs nach den Thumbnails hinzuzufügen, wie Spalten erwartet werden.

Die erwartete Anzahl von Spalten ist etwas, das wir abschätzen können, da die Größe der Thumbnails fest ist und wir wahrscheinlich eine maximale Breite für den Beitrag festlegen möchten, da Text, der sich über die gesamte Bildschirmbreite erstreckt, für die Augen anstrengend zu lesen sein kann. Das Teilen der maximalen Breite durch die feste Thumbnail-Breite sollte uns in diesem Fall die maximale Anzahl von Spalten liefern.

Die ersten leeren Elemente nehmen die volle Breite der Zeile ein, die nicht vollständig mit Thumbnails gefüllt ist, während die restlichen in andere Zeilen überlaufen. Da ihre Höhe jedoch null ist, spielt es visuell keine Rolle.

Das erfüllt irgendwie seinen Zweck, ist aber wieder hackig und liefert immer noch nicht das exakte Ergebnis, das ich möchte, da es manchmal zu großen und eher hässlich aussehenden Lücken zwischen den Spalten kommt.

Eine Grid-Lösung?

Das Grid-Layout klang aufgrund seines Namens immer nach der Antwort. Das Problem war, dass alle Beispiele, die ich bis dahin gesehen hatte, eine vordefinierte Anzahl von Spalten verwendeten, und das funktioniert nicht für dieses spezielle Muster, bei dem die Anzahl der Spalten von der Ansichtsbreite abhängt.

Letztes Jahr, während ich an einer Sammlung von reinen CSS-Hintergrundmustern mit einem Element arbeitete, kam mir die Idee, eine Reihe von Media Queries zu generieren, die eine CSS-Variable, `--n`, ändern, die der Anzahl der für `grid-template-columns` verwendeten Spalten entspricht.

$w: 13em;
$h: 19em;
$f: $h/$w;
$n: 7;
$g: 1em;

--h: #{$f*$w};
display: grid;
grid-template-columns: repeat(var(--n, #{$n}), var(--w, #{$w}));
grid-gap: $g;
place-content: center;
	
@for $i from 1 to $n {
  @media (max-width: ($n - $i + 1)*$w + ($n - $i + 2)*$g) {
    --n: #{$n - $i}
  }
}

Ich war damals sogar sehr stolz auf diese Idee, auch wenn ich mich jetzt beim Zurückblicken davor grusle. Eine Media Query für jede mögliche Spaltenanzahl ist nicht gerade ideal, ganz zu schweigen davon, dass sie nicht gut funktioniert, wenn die Grid-Breite nicht gleich der Ansichtsbreite ist, aber dennoch einigermaßen flexibel ist und auch von der Breite ihrer Geschwister abhängt.

Eine magische Lösung

Ich stieß schließlich auf eine bessere Lösung, als ich mit CSS Grid arbeitete und nicht verstehen konnte, warum die `repeat()`-Funktion in einer bestimmten Situation nicht funktionierte. Es war so frustrierend und veranlasste mich, zu MDN zu gehen, wo ich zufällig auf das Schlüsselwort `auto-fit` stieß, und obwohl ich die Erklärung nicht verstand, hatte ich eine Ahnung, dass es bei *diesem* anderen Problem helfen könnte, also ließ ich alles andere fallen, was ich tat, und probierte es aus.

Hier ist, was ich bekommen habe

.grid--thumbs {
  display: grid;
  justify-content: center;
  grid-gap: .25em;
  grid-template-columns: repeat(auto-fit, 8em);
}

Ich habe auch die Funktion `minmax()` entdeckt, die anstelle von festen Größen für Grid-Elemente verwendet werden kann. Ich konnte immer noch nicht genau verstehen, wie `minmax()` funktioniert - und je mehr ich damit spiele, desto weniger verstehe ich es - aber was es in dieser Situation zu tun scheint, ist, das Grid zu erstellen und dann seine Spalten gleichmäßig zu dehnen, bis sie den gesamten verfügbaren Platz ausfüllen.

grid-template-columns: repeat(auto-fit, minmax(8em, 1fr));

Eine weitere coole Sache, die wir hier tun können, ist zu verhindern, dass das Bild überläuft, wenn es breiter als das Grid-Element ist. Wir können dies tun, indem wir das Minimum von `8em` durch `min(8em, 100%)` ersetzen. Das stellt im Wesentlichen sicher, dass Bilder nie mehr als 100% überschreiten, aber nie unter 8em liegen. Danke an Chris für diesen Vorschlag!

Beachten Sie, dass die Funktion `min()` im vor-Chromium-Edge nicht funktioniert!

Beachten Sie, dass dies nur ein schönes Ergebnis liefert, wenn alle Bilder das gleiche Seitenverhältnis haben - wie die quadratischen Bilder, die ich hier verwendet habe. Für meinen Blog war das kein Problem, da alle Fotos mit meinem Sony Ericsson W800i Handy aufgenommen wurden und alle das gleiche Seitenverhältnis hatten. Aber wenn wir Bilder mit unterschiedlichen Seitenverhältnissen fallen lassen würden, würde das Raster nicht mehr so gut aussehen.

Wir können natürlich die Bildhöhe auf einen festen Wert setzen, aber das verzerrt die Bilder ... es sei denn, wir setzen `object-fit` auf `cover`, was unser Problem löst!

Eine weitere Idee wäre, das erste Thumbnail in eine Art Banner zu verwandeln, das sich über alle Grid-Spalten erstreckt. Das einzige Problem ist, dass wir die Anzahl der Spalten nicht kennen, da diese von der Ansicht abhängt. Aber *es gibt* eine Lösung - wir können `grid-column-end` auf `-1` setzen!

.grid--thumbs {
  /* same styles as before */
	
  a:first-child {
    grid-column: 1/ -1;
		
    img { height: 13em }
  }
}

Das erste Bild erhält eine größere Höhe als alle anderen.

Natürlich, wenn wir möchten, dass das Bild alle Spalten bis auf die letzte einnimmt, setzen wir es auf `-2` und so weiter ... negative Spaltenindizes sind eine Sache!

`auto-fill` ist ein weiteres Grid-Eigenschaftsschlüsselwort, das ich auf MDN bemerkt habe. Die Erklärungen für beide sind lange Textwände ohne Bilder, daher fand ich sie nicht besonders nützlich. Noch schlimmer, das Ersetzen von `auto-fit` durch `auto-fill` in einer der obigen Grid-Demos hat absolut keine Auswirkung. Wie sie wirklich funktionieren und wie sie sich unterscheiden, bleibt auch nach dem Lesen von Artikeln oder dem Experimentieren mit Beispielen ein Rätsel.

Das Ausprobieren verschiedener Dinge und das Beobachten dessen, was in verschiedenen Szenarien passiert, führte mich jedoch irgendwann zu der Schlussfolgerung, dass es wahrscheinlich besser ist, `auto-fill` anstelle von `auto-fit` zu verwenden, wenn wir eine `minmax()`-Spaltenbreite und keine feste (wie `8em`) verwenden, da das Ergebnis besser aussieht, wenn wir nur wenige Bilder haben, wie die folgende interaktive Demo zeigt.

Ich denke, was mir persönlich am besten gefällt, ist die ursprüngliche Idee eines Thumbnail-Rasters, das mittig ausgerichtet ist und eine meist feste Spaltenbreite hat (aber dennoch `min(100%, 15em)` anstelle von nur `15em` verwendet). Letztendlich ist es eine Frage der persönlichen Vorliebe und das, was in der folgenden Demo zu sehen ist, sieht für mich einfach besser aus.

Ich verwende `auto-fit` in dieser Demo, da sie das gleiche Ergebnis wie `auto-fill` liefert und ein Zeichen kürzer ist. Was ich jedoch bei der Erstellung nicht verstanden habe, ist, dass beide Schlüsselwörter das gleiche Ergebnis liefern, da mehr Elemente in der Galerie sind, als wir zum Füllen einer Zeile benötigen.

Aber sobald sich das ändert, liefern `auto-fit` und `auto-fill` unterschiedliche Ergebnisse, wie unten gezeigt. Sie können den `justify-content`-Wert und die Anzahl der auf dem Grid platzierten Elemente ändern.

Ich bin mir nicht wirklich sicher, welche die bessere Wahl ist. Ich denke, das hängt auch von der persönlichen Präferenz ab. Gekoppelt mit `justify-content: center` scheint `auto-fill` die logischere Option zu sein, aber gleichzeitig liefert `auto-fit` ein besser aussehendes Ergebnis.