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.

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.
Das einzige Problem, auf das ich bei Grid-basierten Layouts gestoßen bin, ist, dass es keinen Weg zu geben scheint, eine Art von Rand oder Trennlinie zwischen den Zellen hinzuzufügen (d.h. den Raum im Grid-Gap für das Einfügen von Linien oder Dekorationen zu nutzen). Wenn jemand einen Workaround kennt, würde ich mich freuen, davon zu erfahren!
Ja, das ist ein ziemliches Problem. Ich habe es persönlich immer auf verschiedene Weise umgangen, je nach Situation. Manchmal habe ich lineare Gradienten auf den Containern verwendet, manchmal Pseudoelemente auf den Grid-Elementen. Ich sehe dieses Problem nicht als eines mit einer universellen Lösung.
Hallo saltymouse,
Wenn Sie einen Rand zwischen den Elementen wünschen, würde ich empfehlen, das Grid in einen Container mit `overflow: hidden` zu verpacken und die Breite des Grids auf 100% + 1px zu setzen und jedem Element einfach `border-right` und `border-bottom` zu geben. Das einzige Problem, das ich jemals damit hatte, ist der Rand am unteren Rand.
Tim
Wow! Vielen Dank für diesen fantastischen Artikel und dafür, dass Sie Ihre Erfahrungen teilen.
Mit freundlichen Grüßen.
Interessanter Artikel, danke für das Teilen Ihrer Ideen. Ihre Lösung kann jedoch nur dann als ideal bezeichnet werden, wenn Sie nicht verdammtes IE11 unterstützen müssen. Persönlich verwende ich für dieses Layout eine Art von Flexbox-Ansatz mit der Anzahl leerer Divs am Ende des Containers, aber etwas anders. Sie müssen `justify-content: center;` zu Ihrem Grid-Thumbs-Element hinzufügen und die Regel `a, div:empty { ... }` komplett entfernen.
Ich unterstütze IE weit über 11 hinaus auf meinem Blog (ich habe sogar IE7 im Emulationsmodus getestet), aber Unterstützung bedeutet nicht, ihm das exakt gleiche schicke Aussehen zu geben. Es bedeutet, dass die Sache perfekt nutzbar ist und ein sauberes, nicht kaputtes Aussehen hat.
Das Grid-Layout und die schicken visuellen Effekte sind alle in einem `@supports (mix-blend-mode: screen) {}`, was bedeutet, dass Browser, die dies nicht unterstützen (einschließlich IE und vor-Chromium Edge), einfach die Float-Version erhalten. Das Grid ist in IE/vor-Chromium Edge nicht mittig ausgerichtet, aber wen kümmert's? Es ist nicht so, dass das Layout kaputt ist oder so. Die Seite ist immer noch funktional und schnell.
Kurz darauf stieß ich auf ähnliche Probleme auf meinem Mondrian-Pen (https://codepen.io/dschoni/pen/GRovoxb), aber ich habe es gelöst, indem ich einigen Artikeln von Jen Simmons @jensimmons gefolgt bin.
Das Raster ist zentriert und die Kacheln am Ende sind meist linksbündig, wenn genug Platz dafür ist.
Die Kacheln können unterschiedliche Größen haben und werden für die beste Passform sortiert.
Sicher, der Code ist nicht wirklich der beste, aber er funktioniert für die Praxis.