grid-auto-flow: dense im Detail: Den Packing-Algorithmus wirklich verstehen
AI generated
{ }
@
CSS · Grid Layout · Barrierefreiheit
grid-auto-flow: dense
Den Packing-Algorithmus verstehen, statt ihn nur zu benutzen

grid-auto-flow: dense füllt Lücken in einem CSS Grid automatisch, indem es bei jeder Platzierung rückwärts sucht. Das erzeugt ein dichtes, lückenloses Layout, kann dabei aber die visuelle Reihenfolge der Elemente deutlich von ihrer Reihenfolge im Markup trennen, mit direkten Folgen für Tastatur-Navigation und Screenreader.

14 Min. Lesezeit CSS Grid Level 1 Chrome · Firefox · Safari · Edge

1. grid-auto-flow: die Grundlagen des automatischen Item-Placements

grid-auto-flow steuert, wie der Browser Grid-Items platziert, die keine explizite grid-column- oder grid-row-Position bekommen haben. Standardmäßig steht die Property auf row: Der Algorithmus füllt eine Zeile von links nach rechts auf und springt erst in die nächste Zeile, wenn die aktuelle voll ist. Mit dem Wert column kehrt sich die Richtung um, die Items werden spaltenweise verteilt, bevor der Browser zur nächsten Spalte wechselt.

Der Zusatz dense schaltet einen zweiten Modus ein, der Lücken im bereits aufgespannten Grid aktiv sucht und auffüllt, statt sie stehen zu lassen. In der Praxis begegnet man dense fast immer bei Grids mit unterschiedlich großen Kacheln, etwa Foto-Galerien, in denen einzelne Elemente über zwei Spalten oder zwei Zeilen reichen. Ohne dense entstehen bei solchen Layouts fast zwangsläufig leere Zellen, weil der Standardalgorithmus ein Item niemals rückwärts platziert, selbst wenn davor noch Platz frei wäre.

2. Der Sparse-Algorithmus im Detail: warum ohne dense Lücken entstehen

Der Standardalgorithmus, in der Spezifikation sparse genannt, merkt sich für jede automatische Platzierung nur eine einzige Cursor-Position im Grid und bewegt diese ausschließlich vorwärts. Für jedes neue Item sucht der Browser ab dieser Position die erste freie Zelle, die groß genug für das Item ist, platziert es dort und rückt den Cursor entsprechend weiter. Eine bereits passierte freie Zelle wird danach nicht mehr angefasst, selbst wenn ein späteres, kleineres Item exakt hineinpassen würde.

Dieses Verhalten ist bewusst so gewählt, weil es die DOM-Reihenfolge unangetastet lässt: Item Nummer fünf im Markup landet garantiert nach Item Nummer vier im Grid, niemals davor. Der Preis dafür ist visuelle Ineffizienz. Bei einem Grid mit gemischten ein- und zweispaltigen Kacheln bleiben mit sparse regelmäßig einzelne Zellen leer, weil der Cursor eine kleinere Lücke bereits hinter sich gelassen hat, bevor ein passendes Item folgt. Bei Formular- oder Dashboard-Layouts mit gleich großen Zellen fällt der Effekt kaum auf, bei Kachel-Layouts dagegen deutlich.

3. Der Dense-Packing-Algorithmus: wie er rückwärts sucht und Lücken füllt

Mit grid-auto-flow: row dense wechselt der Browser für jedes neu zu platzierende Item in einen Rückwärts-Suchmodus. Statt nur ab der letzten Cursor-Position weiterzusuchen, prüft der Algorithmus das gesamte bisher aufgespannte Grid von vorne, findet die erste freie Zelle, die für das aktuelle Item groß genug ist, und platziert es dort, auch wenn das bedeutet, dass es vor einem bereits platzierten, später im Markup stehenden Item landet.

Dieser Mechanismus erklärt, warum dense-Grids fast immer weniger Lücken zeigen als sparse-Grids mit identischem Markup: Jede neu entstehende Lücke wird bei der nächsten Platzierung sofort als Kandidat geprüft. Der Nachteil ist derselbe Mechanismus in umgekehrter Wirkung, ein Item kann optisch deutlich vor seiner Markup-Position erscheinen, wenn irgendwo weiter vorne im Grid eine passende Lücke frei war. Für rein dekorative Layouts ist das unkritisch, für Inhalte mit Bedeutung in ihrer Reihenfolge ein echtes Problem.


/* Ohne dense: Item 4 wartet auf die naechste freie Zeile,
   obwohl in Zeile 1 nach dem breiten Item noch Platz waere. */
.gallery {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 120px;
  grid-auto-flow: row;
  gap: 1rem;
}

/* Mit dense: der Algorithmus sucht rueckwaerts und
   fuellt genau diese Luecke sofort mit dem naechsten
   passenden Item. */
.gallery--dense {
  grid-auto-flow: row dense;
}

.gallery__item--wide { grid-column: span 2; }
.gallery__item--tall  { grid-row: span 2; }

4. Praktisches Rezept: eine Foto-Galerie mit gemischten Kachelgrößen

Für eine typische Foto-Galerie definiert man zunächst ein Grundraster mit fester Spaltenzahl und einer festen grid-auto-rows-Höhe, damit alle Zellen dieselbe Basiseinheit haben. Einzelne Kacheln bekommen über Modifier-Klassen wie wide (span 2 Spalten) oder tall (span 2 Zeilen) eine größere Ausdehnung. Ohne dense entstehen an den Rändern dieser größeren Kacheln fast zwangsläufig kleine Löcher, die visuell wie ein Layout-Fehler wirken, obwohl der Algorithmus technisch korrekt arbeitet.

Sobald grid-auto-flow auf row dense gesetzt ist, wandern nachfolgende kleinere Kacheln automatisch in diese Löcher, und das Grid wirkt geschlossen, ohne dass für jede Kombination aus großen und kleinen Kacheln eine manuelle Position gepflegt werden müsste. Wichtig ist, grid-auto-rows explizit zu setzen, sonst berechnet der Browser die Zeilenhöhe aus dem Inhalt der jeweils höchsten Zelle pro Zeile, was bei dense-Layouts zu ungleichmäßig aussehenden Reihen führen kann, weil verschiedene Durchläufe unterschiedliche Items in dieselbe Zeile packen.


.gallery {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 140px;
  grid-auto-flow: row dense;
  gap: 1rem;
}

.gallery__item--wide { grid-column: span 2; }
.gallery__item--tall  { grid-row: span 2; }

@media (max-width: 640px) {
  .gallery {
    grid-template-columns: repeat(2, 1fr);
  }
}

5. Visuelle Reihenfolge vs. DOM-Reihenfolge: wenn dense die Anordnung verändert

Der entscheidende Nebeneffekt von dense ist, dass die visuelle Position eines Items nicht mehr zwangsläufig seiner Position im Markup entspricht. Ein Item, das im HTML als zehntes steht, kann durch eine passende Lücke visuell an dritter Stelle im Grid erscheinen, wenn vorherige, größere Items an dieser Stelle Platz gelassen haben. Für sehende Nutzer mit Maus wirkt das oft charmant kompakt, weil das Layout keine Löcher zeigt und optisch aufgeräumt bleibt.

Problematisch wird es, sobald die Reihenfolge der Inhalte eine Bedeutung trägt, etwa bei einer nummerierten Artikel-Übersicht, einem Ranking oder einer chronologischen Liste. Die Spezifikation warnt ausdrücklich davor, dense für inhaltlich geordnete Listen zu verwenden, weil der Algorithmus rein auf geometrische Lückenfüllung optimiert ist und keine Rücksicht auf semantische Reihenfolge nimmt. Wer die visuelle Kompaktheit von dense will, aber die Reihenfolge nicht verlieren darf, muss die Daten schon vor dem Rendern so anordnen, dass die entstehende Umsortierung akzeptabel bleibt.

6. Accessibility-Implikation: warum Tab-Reihenfolge und visuelle Reihenfolge auseinanderlaufen

Tastatur-Navigation und Screenreader folgen grundsätzlich der DOM-Reihenfolge, nicht der visuellen Darstellung, die CSS erzeugt. grid-auto-flow: dense ändert ausschließlich, wo ein Item auf dem Bildschirm landet, nicht seine Position im Dokumentbaum. Das Ergebnis: Ein Tab-Druck springt weiterhin strikt in Markup-Reihenfolge durch die Kacheln, kann dabei aber optisch quer über das Grid hin und her springen, weil die visuelle Anordnung durch dense verändert wurde, die logische Reihenfolge aber unverändert blieb.

Genau dieses Auseinanderlaufen adressiert das WCAG-Erfolgskriterium 1.3.2 (Meaningful Sequence), das eine programmatisch bestimmbare Lesereihenfolge verlangt, wenn diese für das Verständnis der Inhalte relevant ist. Für sehende Tastaturnutzer entsteht durch den Sprung von Fokusrahmen ein verwirrendes visuelles Muster, für Screenreader-Nutzer bleibt die Vorlese-Reihenfolge zwar korrekt der DOM-Reihenfolge treu, kollidiert aber möglicherweise mit dem, was sehende Begleitpersonen gerade auf dem Bildschirm sehen. In beiden Fällen sollte dense nur für rein dekorative, bedeutungsfreie Anordnungen eingesetzt werden.


<style>
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-flow: row dense;
  gap: 1rem;
}
.grid a:focus-visible {
  outline: 3px solid #7c3aed;
  outline-offset: 2px;
}
</style>

<!-- Tab-Reihenfolge bleibt 1, 2, 3, der Fokusrahmen
     springt visuell trotzdem quer durchs Grid, weil
     Link 1 wegen span 2 optisch mehr Platz einnimmt. -->
<nav class="grid">
  <a href="/thema-1" style="grid-column: span 2;">Erstes Thema</a>
  <a href="/thema-2">Zweites Thema</a>
  <a href="/thema-3">Drittes Thema</a>
</nav>

7. Wann dense sinnvoll ist und wann man besser darauf verzichtet

dense eignet sich gut für Layouts, in denen die Reihenfolge der Elemente keine inhaltliche Aussage trägt, etwa ein Moodboard, eine reine Bildergalerie ohne Zeitachse oder ein Grid aus austauschbaren Werbekacheln. In solchen Fällen überwiegt der Vorteil eines lückenlosen, visuell dichten Layouts klar gegenüber dem Nachteil der veränderten Fokus-Reihenfolge, weil ohnehin niemand erwartet, dass Kachel drei inhaltlich auf Kachel zwei aufbaut.

Verzichten sollte man auf dense dagegen bei Preistabellen, Blog-Übersichten mit Veröffentlichungsdatum, Formularen mit logisch aufeinander aufbauenden Feldern oder jedem Kontext, in dem Nutzer eine erwartbare Lese- oder Tab-Reihenfolge brauchen. Als Kompromiss lässt sich dense oft gezielt nur auf einen Teilbereich des Grids anwenden, etwa eine reine Bildstrecke innerhalb eines ansonsten sequenziellen Artikels, statt die gesamte Seite auf dense umzustellen.

8. Kombination mit grid-auto-rows, grid-auto-columns und explizitem Grid

dense entfaltet seine volle Wirkung erst im Zusammenspiel mit grid-auto-rows und grid-auto-columns, welche die Größe der implizit erzeugten Spuren festlegen. Ohne eine explizite Höhe berechnet der Browser jede automatische Zeile aus dem höchsten Inhalt, der in dieser Zeile landet, was bei dense-Layouts zu inkonsistenten Zeilenhöhen führen kann, weil unterschiedliche Durchläufe unterschiedliche Items in dieselbe Zeile packen.

In gemischten Layouts mit einem expliziten Grid-Bereich für Kernelemente, etwa Headline und Lead-Text mit fester Position, und einem impliziten, dense gepackten Bereich für Zusatzinhalte empfiehlt es sich, beide Bereiche über benannte Grid-Linien klar zu trennen. So bleibt der explizite Teil unabhängig vom Packing-Algorithmus, während nur der dynamisch befüllte Bereich von dense profitiert und der Kernbereich vorhersehbar an seiner Position bleibt.


.layout {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: auto;
  grid-auto-rows: 100px;
  grid-auto-flow: row dense;
  gap: 0.75rem;
}

/* Fester Bereich, bleibt von dense unberuehrt */
.layout__hero {
  grid-column: 1 / -1;
  grid-row: 1;
}

/* Dynamischer Bereich, hier greift dense */
.layout__card--big {
  grid-column: span 2;
  grid-row: span 2;
}

9. dense debuggen: Werkzeuge und Vorgehen bei unerwarteten Sprüngen

Wenn ein Grid-Layout mit dense unerwartete Ergebnisse zeigt, hilft es, die Firefox-Grid-Inspector-Überlagerung zu aktivieren, die jede Grid-Zelle nummeriert und farblich hervorhebt. So lässt sich sofort erkennen, welches Item in welcher Zelle gelandet ist, unabhängig von seiner Position im Markup. Chrome und Edge bieten über die DevTools ein vergleichbares Overlay, das ebenfalls Zeilen- und Spaltennummern zusätzlich zur reinen Fläche anzeigt und Zellen beim Hover hervorhebt.

Als zweite Prüfung lohnt sich ein testweises Umschalten auf grid-auto-flow: row ohne dense, um zu vergleichen, wie stark sich die Anordnung ändert. Bleibt der Unterschied gering, ist dense meist unbedenklich. Springt ein bestimmtes Item dabei um mehrere Positionen, sollte man gezielt prüfen, ob dieses Item eine inhaltliche Reihenfolge trägt, die für Nutzer erkennbar sein muss, und im Zweifel eine explizite Position statt automatischer Platzierung setzen.

Aspekt row (sparse) row dense Empfehlung
Cursor-Verhalten Nur vorwärts Rückwärts-Suche pro Item dense nur bei bedeutungsloser Reihenfolge
Lücken im Grid Häufig bei gemischten Größen Werden aktiv aufgefüllt dense für dichte Galerien nutzen
DOM- vs. visuelle Reihenfolge Bleibt synchron Kann stark abweichen Tab-Reihenfolge vorher testen
Typischer Einsatz Formulare, Dashboards Bildergalerien, Moodboards Je nach Inhaltstyp wählen
WCAG-Relevanz Unkritisch 1.3.2 Meaningful Sequence prüfen Nur für dekorative Grids einsetzen

Mironsoft

Modernes CSS, Layout-Architektur und Rendering-Performance

CSS, das wartbar bleibt statt mit jeder Änderung zu brechen?

Wir prüfen bestehende Stylesheets auf Spezifitäts-Chaos und Layout-Thrashing und bauen daraus eine CSS-Architektur mit Cascade Layers, Custom Properties und modernen Layout-Primitiven, die auch nach dem zehnten Feature noch verständlich ist.

CSS-Audit

Spezifität, Cascade-Konflikte und ungenutzte Selektoren systematisch aufdecken.

Architektur-Refactoring

Cascade Layers, Custom Properties und Design Tokens sauber einführen.

Performance-Tuning

Layout-Thrashing, teure Selektoren und Rendering-Engpässe gezielt beheben.

10. Zusammenfassung

grid-auto-flow: dense: Das Wichtigste auf einen Blick

Sparse vs. dense

Sparse bewegt den Platzierungs-Cursor nur vorwärts, dense sucht bei jedem Item das gesamte Grid rückwärts nach der ersten passenden Lücke ab.

Visuelle Umsortierung

dense kann Items deutlich vor ihrer DOM-Position platzieren, während Tab-Reihenfolge und Screenreader weiterhin der Markup-Reihenfolge folgen.

WCAG 1.3.2

Meaningful Sequence verlangt eine erhaltene Lesereihenfolge, wenn diese für das Verständnis relevant ist, dense sollte nur für bedeutungsfreie Layouts genutzt werden.

Praxis-Rezept

grid-auto-rows explizit setzen, dense gezielt auf abgegrenzte Bildbereiche anwenden statt auf die gesamte Seite, Fokus-Reihenfolge vor dem Launch testen.

11. FAQ: grid-auto-flow: dense: Das Wichtigste auf einen Blick

1Was macht grid-auto-flow: dense konkret anders als row?
dense sucht bei jeder Platzierung das gesamte bisherige Grid von vorne nach der ersten passenden Lücke ab, row bewegt den Platzierungs-Cursor dagegen nur vorwärts und lässt einmal passierte Lücken leer.
2Ändert dense die Reihenfolge im DOM?
Nein. dense verändert ausschließlich die visuelle Position der Items im Grid, die Reihenfolge im Dokumentbaum und damit die Tab- und Screenreader-Reihenfolge bleibt unverändert.
3Warum sollte ich bei Blog-Listen kein dense verwenden?
Weil die zeitliche oder inhaltliche Reihenfolge der Artikel für Nutzer erkennbar bleiben sollte. dense kann einen später veröffentlichten Artikel optisch vor einen früheren setzen, wenn eine passende Lücke frei war.
4Wie hängt dense mit WCAG 1.3.2 zusammen?
Das Erfolgskriterium verlangt eine erhaltene, bedeutungsvolle Lesereihenfolge. Weicht die visuelle Reihenfolge stark von der DOM-Reihenfolge ab, kann das für Tastaturnutzer und Screenreader-Nutzer zu Verwirrung führen.
5Brauche ich grid-auto-rows, wenn ich dense nutze?
Nicht zwingend, aber ohne explizite Zeilenhöhe berechnet der Browser jede implizite Zeile aus dem höchsten Inhalt, was bei dense zu ungleichmäßig aussehenden Reihen führen kann.
6Kann ich dense nur für einen Teilbereich des Grids aktivieren?
Ja. dense ist eine Eigenschaft des Grid-Containers, daher kann man einen separaten, verschachtelten Grid-Container nur für den dense-tauglichen Bildbereich anlegen und den Rest der Seite sequenziell lassen.
7Unterstützen alle modernen Browser grid-auto-flow: dense?
Ja, dense ist Teil der CSS Grid Level 1 Spezifikation und wird von allen aktuellen Versionen von Chrome, Firefox, Safari und Edge unterstützt.
8Wie finde ich heraus, welches Item wo im Grid gelandet ist?
Der Firefox-Grid-Inspector und vergleichbare Overlays in Chrome und Edge nummerieren jede Grid-Zelle und zeigen, welches Element sie belegt, unabhängig von der Markup-Reihenfolge.
9Löst dense automatisch alle Lücken im Grid?
Nein. dense füllt nur Lücken, für die ein nachfolgendes Item in Größe und Form passt. Bleibt kein passendes Item übrig, bleibt die Zelle trotzdem leer.
10Gibt es eine Alternative zu dense für dichte Layouts ohne Reihenfolge-Risiko?
Ja, man kann Items vor dem Rendern serverseitig oder mit JavaScript in eine bereits lückenlose Reihenfolge bringen und dann row ohne dense verwenden, wodurch DOM- und visuelle Reihenfolge synchron bleiben.