CSS/Tutorials/Grid/responsive Raster ohne Media Queries

Aus SELFHTML-Wiki
< CSS‎ | Tutorials‎ | Grid
Wechseln zu: Navigation, Suche

Im Printdesign und auch bei „CSS-Frameworks“ wie bootstrap wird zuerst ein Gestaltungsraster erstellt, in dem die Rasterelemente dann fest platziert werden.
Im Webdesign mit seiner Unzahl von verschiedenen Viewportgrößen ist dies zwar auch möglich, oft aber gar nicht nötig.

In diesem Kapitel soll gezeigt werden, wie Sie mit wenigen CSS-Festlegungen Raster implizit erzeugen, deren Rasterelemente sich mit dem auto-placement-Algorithmus ohne den Einsatz von media queries responsiv anordnen.

Grundlagen

Blockelemente wie Überschriften oder Absätze nehmen im Standardverhalten der Browser stets die verfügbare Breite ein. Mit der Einführung von CSS wurde es möglich, Positionierungen und Größenangaben auch außerhalb des Elementflusses festzulegen.

Dies ermöglichte einerseits ein pixelgenaues Layout, wurde andererseits bei einer Änderung des Viewports zum Bumerang, da sich Inhalte außerhalb des sichtbaren Bereichs oder unter anderen Elementen befanden oder durch overflow: hidden; abgeschnitten wurden. Media queries bieten hier eine Lösung, doch die immer wiederkehrende Frage nach den richtigen Breakpoints zeigt, dass aber auch dies nicht die einfachste Lösung ist.

Grid Layout beginnt wieder von vorne: Der verfügbare Raum wird aufgeteilt, weitere Inhalte werden innerhalb des Rasters in weiteren Reihen dargestellt und wandern so nach unten, wo man sie durch Scrollen erreichen kann.

Auch hier kann die Anzahl der Spalten mit media queries an die verfügbare Viewportbreite angepasst werden, wie es ausführlich in Kapitel 6 gezeigt wird.

responsives Raster mit media queries
body{
  display: grid;
  grid-template-columns: repeat(2, 1fr);
}

@media (min-width: 30em) { 
  body{
    grid-template-columns: repeat(3, 1fr);
}

@media (min-width: 50em) { 
  body{
    grid-template-columns: repeat(4, 1fr);
  }
}

Fazit: Mit media queries können Sie ein Raster mit einer festen Spaltenanzahl, deren Breite sich flexibel anpasst, erzeugen. Könnte man dies so umdrehen, dass man als festen Wert die Breite der Spalten verwendet, die sich entsprechend der Viewportbreite unterschiedlich oft nebeneinander anordnen? Ja, das geht!

Responsivität ohne Media queries

Im Folgenden soll gezeigt werden, wie Raster mit flexibler Spaltenanzahl angelegt werden, die sich auch ohne Media Queries responsiv an den Viewport anpassen.

auto-fill

Bequemer als die Lösung mit media queries ist es allerdings, mittels des Schlüsselworts auto-fill den Browser die Anzahl der Spalten automatisch festlegen zu lassen:

ein gefülltes Raster So sieht's aus
body{
  display: grid;
  grid-template-columns: repeat(auto-fill, 20em);
}

article {
  grid-column: 1 / -1;
  grid-row: span 2;
}

Erinnern Sie sich noch an unser erstes Beispiel?
Mit grid-template-columns: auto auto auto; wurden drei Spalten, deren Breite vom Inhalt abhängig war, erstellt.
In diesem Beispiel wurde die repeat()-Funktion verwendet, die nun über den ersten Parameter auto-fill so viele Spalten wie möglich erzeugt.
Der zweite Parameter gibt die Breite der Spalten (20em) vor.

Das article-Element soll, wenn der Platz ausreicht, breiter werden.

  • grid-column: 1 / -1; legt fest, dass der article an der ersten Gridlinie beginnt und an der letzten endet (negative Werte beginnen die Zählung rechts)
  • grid-row: span 2; das Element soll sich in der Höhe über 2 Reihen (Rasterfelder) erstrecken


Empfehlung: Öffnen Sie das Beispiel mit Rechtsklick in einem neuen Tab und machen Sie das Fenster so breit wie möglich (Sie sollten hierfür einen großen Bildschirm haben). Dann vergrößern Sie die Zoomstufe des Browsers auf mindestens 150% und machen das Fenster so schmal, wie es geht. Der body hat eine schwarze Randlinie.
Was fällt Ihnen auf?
Lösungshinweise anzeigenverbergen

Bei großen Viewports:

Das body-Element erhält mit max-width: 75em; eine Maximalbreite, die von den 3 je 20em breiten Rasterfeldern nicht völlig ausgefüllt wird.

Bei kleinen Viewports:

Das body-Element enthält 20em breite Raster, hat also dadurch eine Mindestbreite von 20em. Wenn Sie den Viewport schmaler machen, ragt der Inhalt hinaus und ein Scrollbalken wird angezeigt. Um das zu verhindern, sollten Sie ein solches Layout nur zusammen mit einer Media-Query anwenden, die das Grid erst bei genügender Breite aktiviert:

@media (min-width: 40em) {
  display: grid;
  grid-template-columns: repeat(auto-fill, 20em);
}

minmax()

Damit der autoplacement-Algorithmus seine volle Wirkung entfalten kann, muss die Breite der Spalten flexibel sein. Bisher wurde dies bei fester Spaltenanzahl mit der Einheit fr erledigt. Nun sollen die Spalten eine feste Mindestgröße, aber auch keinen überschüssigen Raum in Form eines leeren Rands haben.

Voilà: Die minmax()-Funktion erlaubt es anstelle von festen oder relativen Längenangaben einen Mindest- und einen Maximalwert anzugeben:

ein gefülltes Raster So sieht's aus
@media (min-width: 40em) {
  body {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(20em, 1fr));
  }
}

article {
  grid-column: 1 / -1; 
  grid-row: span 2; 
}

#news {
  grid-column: span 2;
}

In diesem Beispiel wurde die repeat()-Funktion verwendet, die nun über den ersten Parameter auto-fill so viele Spalten wie möglich erzeugt.
Der zweite Parameter besteht aus der minmax()-Funktion, die als Minimum einen Wert von 20em und, damit es z. B. bei 50em keine leeren Räume gibt, einen Maximalwert von 1 fr hat. Dann würden die 50em Breite in 2 Spalten a 25em Breite aufgeteilt.

Empfehlung: Öffnen Sie das Beispiel mit Rechtsklick in einem neuen Tab und beobachten Sie.

Durch die Platzierung des article-Elements und der #news-Box über mehrere Spalten bleiben bei einem Dreispaltenlayout Rasterfelder frei.

Sie könnten hier eines der weiter hinten gelegenen Elemente mittels einer Angabe von Grid-column, Grid-row, etc. nach vorne bringen. Dies wäre bei kleineren Viewports aber nicht nötig und würde deshalb kontraproduktiv sein. Überlassen Sie dies dem autoplacement-Algorithmus!
Beachten Sie: Auch hier gilt: Testen Sie das Layout bei schmalen Viewports und aktivieren Sie das Grid nur ab einer bestimmten Mindestbreite. Auf schmaleren Viewports sollten Sie eine einfache „von oben nach unten“-Darstellung nutzen, die einfach die Anzeigebreite füllt.

grid-auto-flow

Wenn Sie Rasterelemente fest positionieren oder Gridbereiche festlegen, können im Raster wie im oberen Beispiel Lücken entstehen. Diese können mit im Markup nachfolgenden Elementen gefüllt werden.

Der autoplacement-Algorithmus kann durch die grid-auto-flow-Eigenschaft gesteuert werden.

Folgende Angaben sind möglich:

  • row: (Standardwert) Die Spalten werden mit grid-template-columns explizit erzeugt; je nach Menge der Inhalte werden Zellen implizit erzeugt und in weiteren Reihen angefügt.
  • columns: Weitere Inhalte werden in implizit erzeugten Spalten angehängt.
  • dense: optionales Schlüsselwort, das kleinere Zellen in vorher entstandene Lücken platziert, sodass es zu einem gefüllten Raster kommt.
    Ohne dieses Schlüsselwort kommt der sparse-Modus zum Zug, der Elemente, wenn kein Platz vorhanden ist, in die nächste Reihe schiebt.
  • row dense:
  • column dense:
ein gefülltes Raster So sieht's aus
body{
  display: grid;
  grid-template-columns: auto auto auto;
  grid-auto-flow: dense;			
}

article {
  grid-column: 1 / -1; 
  grid-row: span 2;
}

#news {
  grid-column: span 2;
}

Durch das Schlüsselwort dense wird der Algorithmus so geändert, dass die vorher leeren Bereiche durch im Markup hinten stehende Elemente aufgefüllt werden.

Beachten Sie, dass Sie das Schlüsselwort dense nur verwenden sollten, wenn es innerhalb des Grids keine fokussierbaren Elemente gibt, da eine Umordnung immer zu unlogischer Tab-Reihenfolge führt.

Anwendungsbeispiele

Ein solch flexibles Raster eignet sich vor allem für gleichförmige Elemente, bei denen unter Umständen die Reihenfolge verändert werden kann, ohne dass es zu Irritationen wie einem weit oben stehenden Seitenfooter kommt.

Responsives Grid

Die 30 Artikel der Erklärung der Menschenrechte sollen sich flexibel über den verfügbaren Viewport verteilen. Dabei soll keine Spaltenanzahl vorgegeben werden:

flexibles Grid Layout ohne media queries So sieht's aus
main {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(min(20em,100%), 1fr));
  color: black;
  font: 1em/1.3 Cambria, serif;
}

main > section {
  padding: 1rem;
  background: hsl(220 80% 90%);
}

Für die 30 section-Blöcke werden mit grid-template-columns Spalten erzeugt. Dabei werden mit der repeat()-Funktion über das autofill-Schlüsselwort so oft wie nötig Spalten erzeugt. Als Mindestbreite für diese Spalten setzen wir hier aber nicht den festen Wert 20em, sondern sorgen mit min(20em, 100%) dafür, dass die Artikelzellen nicht breiter als 100% des nächsten Elternelements mit limitierter Breite werden. In unserem Fall ist das das <body>-Element, das die Fensterbreite hat. Die Maximalbreite ist wieder 1fr.

Das Inhalt wird jetzt zwar immer noch scrollen, wenn der Viewport sehr schmal ist, aber nicht für die Artikel selbst.

Bildergalerie

Der auto-placement-Algorithmus eignet sich besonders für Listen und Galerien, bei denen die genaue Reihenfolge unwichtig ist. In dieser Bildergalerie werden die unterschiedlich großen Bilder auf den verfügbaren Platz verteilt. Durch grid-auto-flow: dense; wird das Raster lückenlos gefüllt.

Bildergalerie So sieht's aus
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8em, 1fr));
  gap: .5em;
  grid-auto-flow: dense;
}

.gallery figure.landscape {
  grid-column-end: span 2;
}

.gallery figure.panorama {
  grid-column-end: span 3;
}

In diesem Beispiel findet das entscheidende CSS nur im Elternelement statt:

  • display: grid; macht das div mit der id gallery zum Grid Container
  • grid-template-columns: repeat(auto-fill, minmax(8em, 1fr)); erzeugt Spalten, die mindestens 8em breit sind. Wenn die Gesamtbreite größer als ein Vielfaches der Mindestbreite ist, sodass normalerweise rechts ein Rand bliebe, verteilen die Spalten sich jedoch gleichmäßig über den verfügbaren Platz.
  • grid-auto-flow: dense; füllt das Raster lückenlos mit weiter hinten im Markup stehenden Elementen auf.

Neben dem Portrait-Modus, der eine Zelle umfasst, gibt es noch ein landscape-Format, das sich durch grid-column-end: span 2; über zwei, sowie ein Panorama-Format, das sich über 3 Zellen erstreckt.

Bildergalerie So sieht's aus
.gallery figure {
  border: thin solid #ccc;
  position: relative;	
  margin: 0;
  counter-increment: posMarkup;	
}

.gallery figure img {
  display: block;
  object-fit: cover;
  width: 100%;
  height: 100%;
}

.gallery figure::after {
  content: counter(posMarkup, decimal);
  position: absolute;
  bottom: 0;
  color: red;
}

Die Bilder werden in ein figure-Element verpackt, das den browsereigenen Abstand durch margin: 0; normalisiert.
Die Bilder werden durch object-fit: cover; passend skaliert, ohne das Seitenverhältnis zu ändern.
Damit sichtbar wird, wie durch die Verwendung von dense kleinere Zellen in vorher entstandene Lücken platziert werden, wird in einem absolut positioniertem Pseudoelement die Reihenfolge jedes Bildes gekennzeichnet. Dabei werden zur Bildnummerierung CSS-Counter verwendet:

  • counter-increment erzeugt eine Variable, die dann im Pseudoelement ::after über content: counter(posMarkup, decimal); wieder ausgegeben wird.


Siehe auch Ein 3x3 (oder 4x4) Raster mit Großansicht und mehreren thumbnails.

Bilderwand aus Quadraten

Ist es möglich, eine Bilderwand mit Quadraten zu erzeugen, bei denen sich die Höhe an die flexible Breite anpasst?[1]

Mit dem padding-bottom height fix können Sie einem Element einen unteren Rand von 100% bei einer Höhe von 0 geben – der Prozentwert bezieht sich auf die Breite des Elements.

Dies können wir uns im Grid Layout zunutze machen. Das HTML-Markup unterscheidet sich nicht von anderen Galerien:

Bilderwand aus Quadraten - HTML So sieht's aus
<div class="square-container">
	<a id="kontakt" href="#">
		Kontakt
	</a>
	
	<a id="weltwunder" href="#">
		<img src="#" alt="Weltwunder" >
	</a>
       ...

Das CSS wird nur über den .square-container gesteuert:

Bilderwand aus Quadraten - CSS So sieht's aus
.square-container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  grid-auto-rows: 1fr;
  grid-auto-flow: dense;
}

.square-container::before {
  content: '';
  height: 0;
  padding-bottom: 100%;
  grid-row: 1 / 1;
  grid-column: 1 / 1;
}

.square-container > *:first-child {
  grid-row: 1 / 1;
  grid-column: 1 / 1;
}

Es wird mit .square-container::before ein Pseudoelement erzeugt, das mit dem padding-bottom height fix die gleiche Höhe wie die Breite erhält (Die Breite wird durch grid-template-columns festgelegt!). Als Rasterelement innerhalb des grid bestimmt es nun die Höhe aller folgenden Rasterelemente. Es wird durch grid-row: 1/1 und grid-column: 1/1 ins erste Feld positioniert.

Das erste Kindelement des .square-container wird mit *:first-child selektiert und mit den gleichen Werten darüber gelegt.

Die Kästchen werden 12rem (oder etwas mehr breit) – es gibt keinen Rand. Die Höhe ist identisch!

Dies funktioniert solange, wie die einzelnen Rasterelemente nur Text enthalten. Es scheint nicht möglich Bilder so zu positionieren, dass sie den verfügbaren Raum einnehmen ohne die Dimensionen zu ändern. Hier hilft eine absolute Positionierung:

absolute Positionierung der img-Elemente So sieht's aus
.square-container > a {
  position: relative;
}

.square-container img { 
  position:absolute;
  top:0;
  left:0;
  object-fit: cover; 
  width:100%; 
  height: 100%;  
}

Damit die einzelnen Bilder, bzw. deren a-Elternelemente beim :hover oder dem Durchtabben mit der -Taste sichtbar werden, erhalten sie einen blauen Rand und eine verringerte Deckung:

Kennzeichnung des Fokus So sieht's aus
.square-container > a {
  border: thin solid transparent;
  position: relative;
}

.square-container > *:focus,
.square-container > *:hover {
  border: thin solid blue;
  opacity: .75;
}

Innerhalb der Bilderwand soll es zwei quadratische Links auf den Blog und die Kontaktseite geben:

(Fest) positionierte Links So sieht's aus
#blog,
#kontakt {
  background: url(handwriting.jpg);
  background-size:cover;
  display:flex; 
  align-items:center; 
  justify-content:center;
  font: 3rem bold; 
  text-decoration: none;
  color: red;
 }
 
#kontakt {
  grid-column: -2 / -1;  
 }

Während der erste Link aufgrund seiner Reihenfolge im HTML oben links im Raster positioniert ist, wird der Link mit der #kontakt mit grid-column oben rechts positioniert. Die negativen Werte zählen die Rasterlinien von rechts.

Masonry Tiles

Eine moderne Variante einer Bildergalerie ist das Masonry Layout, indem Bilder sich innerhalb von flexiblen Spalten passend anordnen. Bis jetzt musste der Platzbedarf mit JavaScript verteilt werden – mit Grid Layout wird dies auch nur mit CSS möglich.

Dabei erhält die grid-template-rows-Eigenschaft einen (bis jetzt nur im FF79+ unterstützten) masonry-Wert.[2][3]

Masonry Effekt mit Grid Layout
.masonry {
  display: grid;
  gap: .5em;
  grid-template-columns: repeat(auto-fill, minmax(7em, 1fr));
  grid-template-rows: masonry;
}
Beachten Sie: Im Firefox müssen Sie in about:config noch layout.css.grid-template-masonry-value.enabled einstellen!

Der Webdesigner Kseso stellt in einem CodePen ein solches Layout vor:

Hexagonales Sechseck-Grid

Ein solches Raster ist nicht nur mit rechteckigen Formen möglich. Gunnar Bittersmann stellt in diesem Pen ein sechseckiges Wabenmuster vor, das ebenfalls im Grid Layout aufgebaut ist:

Eine Liste von Grafiken wird als Grid dragestellt. Die Grafiken werden mit clip-path zu einem Sechseck geschnitten. Die Ränder und Postionen innerhalb des Grid werden mit CSS-Variablen jeweils neu berechnet.

Weblinks

  1. SELF-Forum: flexible Bilderwand
  2. MDN: Masonry Layout
  3. css-tricks: A Lightweight Masonry Solution von Ana Tudor, 03.08.2020