CSS: Layout

CSS Grid: Layout in Zeilen und Spalten

CSS Grid ordnet Elemente in einem Raster aus Zeilen und Spalten an. Du definierst das Raster am Container mit display: grid und grid-template-columns. Die Kinder rutschen automatisch hinein oder werden über Linien und Namen gezielt platziert.

Grid ist das einzige CSS-Layoutsystem, das beide Achsen gleichzeitig steuert. Vor seiner Einführung entstanden zweidimensionale Layouts aus Floats, Tabellen oder ineinander geschachtelten Flexbox-Containern. Jede dieser Techniken kannte eine Achse und brauchte für die zweite eine Hilfskonstruktion.

Diese Seite zeigt die Eigenschaften in der Reihenfolge, in der du sie beim Bauen brauchst. Ein Raster, das Elemente visuell umsortiert, ändert die Reihenfolge im Quelltext an keiner Stelle. Der Abschnitt zur Barrierefreiheit erklärt die WCAG-Verstöße, die daraus entstehen. Stand September 2026 unterstützen alle aktuellen Browser Grid vollständig, einschließlich Subgrid.

Das Wichtigste in Kürze

  • display: grid macht ein Element zum Raster-Container. Alle direkten Kinder werden zu Rasterelementen und ordnen sich in die Spuren ein.
  • Das Raster entsteht mit grid-template-columns und grid-template-rows. Die Einheit fr verteilt den freien Platz, repeat() kürzt Wiederholungen ab und minmax() setzt eine Unter- und eine Obergrenze je Spur.
  • gap ersetzt die Abstandsrechnerei mit margin. Der Abstand steht zwischen den Spuren und bleibt am äußeren Rand weg.
  • Elemente werden über Linien-Nummern (grid-column: 1 / 3), über span oder über benannte Bereiche aus grid-template-areas platziert.
  • repeat(auto-fit, minmax(16rem, 1fr)) erzeugt ein umbrechendes Raster ohne eine einzige Media Query.
  • Grid steuert zwei Achsen, Flexbox eine. Die visuelle Platzierung ändert die DOM-Reihenfolge nie. Das kann 1.3.2 Bedeutungstragende Reihenfolge verletzen.

CSS Grid Layout: das Raster in zwei Achsen

Ein Grid entsteht am Container. display: grid allein legt eine einzige Spalte an und stapelt die Kinder untereinander. Interessant wird es mit dem Grid-Template, also der Beschreibung der Spuren.

.karten {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 1.5rem;
}
<ul class="karten">
  <li>Erste Karte</li>
  <li>Zweite Karte</li>
  <li>Dritte Karte</li>
  <li>Vierte Karte</li>
</ul>

Zwei Regeln gelten immer. Erstens werden ausschließlich die direkten Kinder zu Rasterelementen. Zweitens erzeugt der Browser für überzählige Elemente neue Zeilen von selbst. Im Beispiel oben landet die vierte Karte in einer zweiten Zeile.

grid-template-columns: Spaltenbreiten festlegen

grid-template-columns nimmt eine Liste von Spurgrößen entgegen, ein Wert je Spalte. Erlaubt sind feste Längen, Prozentwerte, auto, min-content, max-content und die Grid-eigene Einheit fr. grid-template-rows arbeitet genauso für Zeilen.

.beitrag {
  display: grid;
  grid-template-columns: 250px 1fr;
  grid-template-rows: auto 1fr auto;
}

Die Einheit fr verteilt den freien Platz

Ein fr ist ein Anteil am verbleibenden Platz. Der Browser zieht zuerst alle festen Breiten und alle gap-Abstände ab und verteilt den Rest im Verhältnis der fr-Werte. 2fr 1fr ergibt zwei Spalten im Verhältnis zwei zu eins. Prozentwerte leisten das nicht, weil sie den gap mitrechnen und dadurch Überlauf erzeugen.

repeat() und minmax(): Wiederholung und Grenzen

repeat() schreibt wiederkehrende Spuren kurz. minmax(min, max) gibt einer Spur eine Untergrenze und eine Obergrenze. Der Browser wählt innerhalb dieses Bereichs.

.raster {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}

.mit-seitenspalte {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(14rem, 22rem);
}

Die Kombination minmax(0, 1fr) ist der wichtigste Sonderfall. Sie erlaubt einer Spalte, schmaler zu werden als ihr Inhalt, und nimmt damit langen Wörtern und breiten Codeblöcken den Überlauf.

gap: Abstände zwischen Zeilen und Spalten

gap setzt den Abstand zwischen den Spuren. Ein Wert gilt für beide Achsen, zwei Werte stehen für Zeilenabstand und Spaltenabstand in dieser Reihenfolge. Am äußeren Rand des Containers entsteht kein Abstand. Randkorrekturen mit negativen Margins entfallen damit.

.karten {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2rem 1rem; /* row-gap column-gap */
}

Platzierung mit grid-column, grid-row und span

Ein Raster mit drei Spalten hat vier senkrechte Linien. Die Zählung beginnt links bei 1, negative Nummern zählen von rechts. grid-column: 1 / 3 belegt den Bereich von Linie 1 bis Linie 3, also zwei Spalten. Wer die Endlinie nicht ausrechnen möchte, nutzt span mit der Zahl der Spuren.

.hero {
  grid-column: 1 / -1; /* über die volle Breite */
}

.hervorgehoben {
  grid-column: span 2;
  grid-row: span 2;
}

.rechte-spalte {
  grid-column: 3;
  grid-row: 1 / 4;
}

Diese Platzierung wirkt ausschließlich visuell. Die Tastaturbedienung folgt weiterhin dem Quelltext, was der Abschnitt zur Fokus-Reihenfolge genauer beschreibt.

grid-template-areas: benannte Bereiche für ein ganzes Seitenlayout

Mit grid-template-areas bekommen Bereiche Namen und das Layout wird im CSS lesbar. Jede Zeichenkette steht für eine Rasterzeile, jeder Name für eine Zelle. Ein Punkt lässt eine Zelle leer, ein wiederholter Name erzeugt einen zusammenhängenden Bereich. Das folgende Beispiel ist vollständig und kopierbar.

<div class="seite">
  <header>Kopfbereich</header>
  <nav aria-label="Hauptnavigation">Navigation</nav>
  <main id="inhalt">Hauptinhalt</main>
  <aside>Zusatzinhalt</aside>
  <footer>Fußbereich</footer>
</div>
.seite {
  display: grid;
  min-height: 100vh;
  gap: 1rem;
  grid-template-columns: 14rem minmax(0, 1fr) 16rem;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "kopf   kopf    kopf"
    "nav    inhalt  neben"
    "fuss   fuss    fuss";
}

.seite > header { grid-area: kopf; }
.seite > nav    { grid-area: nav; }
.seite > main   { grid-area: inhalt; }
.seite > aside  { grid-area: neben; }
.seite > footer { grid-area: fuss; }

@media (max-width: 48rem) {
  .seite {
    grid-template-columns: 1fr;
    grid-template-areas:
      "kopf"
      "nav"
      "inhalt"
      "neben"
      "fuss";
  }
}

Die Elementwahl im HTML ist Absicht. header, nav, main, aside und footer erzeugen die Landmarks, über die Screenreader springen. Ein Raster aus div-Containern sieht identisch aus und liefert keine Sprungziele. Die Details stehen unter Landmarks und Dokument-Outline und Struktur-Elemente.

Das implizite Grid: grid-auto-rows und grid-auto-flow

Alles, was du in grid-template-* beschreibst, ist das explizite Grid. Sobald mehr Elemente vorhanden sind als Zellen, legt der Browser weitere Spuren an. Diese impliziten Spuren sind standardmäßig auto hoch. grid-auto-rows gibt ihnen eine Größe.

.galerie {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(8rem, auto);
  grid-auto-flow: row;
}

grid-auto-flow legt die Füllrichtung fest. Der Standard row füllt zeilenweise, column spaltenweise. Der Zusatz dense erlaubt dem Browser, spätere Elemente in frühere Lücken vorzuziehen. Diese Umsortierung wirkt rein visuell und ist deshalb unten ein eigenes Thema.

Ausrichtung: justify-items, align-items und place-items

Grid trennt zwei Fragen. Wie liegt ein Element in seiner Zelle? Und wie liegt das gesamte Raster im Container? Die *-items-Eigenschaften beantworten die erste, die *-content-Eigenschaften die zweite. justify-* wirkt waagerecht, align-* senkrecht.

.raster {
  display: grid;
  grid-template-columns: repeat(3, 10rem);
  place-items: center;     /* align-items und justify-items zugleich */
  justify-content: center; /* das ganze Raster mittig im Container */
}

Ein einzelnes Element weicht mit justify-self und align-self ab. Die Zentrierungsmuster im Vergleich stehen unter Elemente zentrieren.

Responsiv ohne Media Query: auto-fit gegen auto-fill

Das meistgenutzte Grid-Muster braucht keinen Breakpoint. repeat() nimmt statt einer Zahl die Schlüsselwörter auto-fit oder auto-fill entgegen und der Browser rechnet aus, wie viele Spalten in die verfügbare Breite passen.

/* Do: das Raster passt sich jeder Breite an und legt selbst neue Zeilen an */
.karten {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: 1.5rem;
}
/* Don't: feste Spaltenbreiten erzwingen waagerechtes Scrollen */
.karten {
  display: grid;
  grid-template-columns: repeat(3, 320px);
}

Der Unterschied zwischen beiden Schlüsselwörtern zeigt sich bei wenigen Elementen. auto-fill lässt leere Spuren stehen. auto-fit klappt sie auf null zusammen, sodass die vorhandenen Karten über 1fr die Zeile füllen. Das min(16rem, 100%) verhindert Überlauf auf sehr schmalen Bildschirmen, weil die Untergrenze dort auf die Containerbreite sinkt.

Subgrid: verschachtelte Raster an denselben Linien

Ein Kind mit eigenem Grid hat normalerweise ein eigenes Raster ohne Bezug zum Elternteil. Bei Kartenlisten führt das zu unterschiedlich hohen Überschriften und ungleich stehenden Buttons. subgrid übernimmt die Linien des Elterngrids.

.karten { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }

.karte {
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid; /* Titel, Text und Fuß richten sich über alle Karten aus */
}

Stand September 2026 unterstützen alle aktuellen Browser subgrid. Firefox hat es seit Version 71, Safari seit 16 und die Chromium-Browser seit 117. Für ältere Installationen genügt eine @supports (grid-template-rows: subgrid)-Abfrage.

Die wichtigsten Grid-Eigenschaften im Überblick

Eigenschaft Wirkung Beispielwert
display Macht das Element zum Raster-Container grid
grid-template-columns Zahl und Breite der Spalten repeat(3, 1fr)
grid-template-rows Zahl und Höhe der Zeilen auto 1fr auto
grid-template-areas Benennt Bereiche im Raster "kopf kopf"
gap Abstand zwischen den Spuren 1.5rem
grid-column Spaltenbereich eines Elements 1 / -1
grid-row Zeilenbereich eines Elements span 2
grid-area Bereichsname oder Kurzform aller vier Linien inhalt
grid-auto-rows Höhe impliziter Zeilen minmax(8rem, auto)
grid-auto-flow Füllrichtung des impliziten Rasters row
justify-items Lage in der Zelle, waagerecht start
align-items Lage in der Zelle, senkrecht center
place-items Kurzform für beide Achsen center
justify-content Lage des ganzen Rasters, waagerecht space-between

Grid oder Flexbox: was wann?

Die Regel passt in einen Satz. Zwei Achsen bedeuten Grid, eine Achse bedeutet Flexbox. Sobald du Zeilen und Spalten gleichzeitig ausrichten musst, ist Grid das passende Werkzeug. Steht eine Reihe von Elementen nur nebeneinander oder nur untereinander, ist Flexbox einfacher.

Der zweite Unterschied liegt in der Richtung der Entscheidung. Grid arbeitet vom Layout her. Der Container legt das Raster fest und die Kinder ordnen sich ein. Flexbox arbeitet vom Inhalt her. Die Elemente melden ihre Wunschgröße und verteilen den Rest über flex-grow und flex-shrink. Deshalb eignet sich Grid für das Seitengerüst und Flexbox für Werkzeugleisten, Button-Gruppen und Navigationszeilen.

Beide Systeme arbeiten in derselben Seite zusammen. Bei mehrspaltigen Navigationsflächen wie Mega-Menüs liefert Grid die Spalten und Flexbox die einzelnen Einträge.

Barrierefreiheit: worauf es ankommt

Grid ist das Layoutsystem mit dem größten Abstand zwischen dem, was Sehende sehen, und dem, was Software liest. Genau daraus entstehen die fünf häufigsten Fehler.

Visuelle Position und DOM-Reihenfolge fallen auseinander

Mit grid-column, grid-row und grid-template-areas platzierst du jedes Element an jede Stelle des Rasters. Der Quelltext bleibt dabei unverändert. Screenreader lesen ihn und die Tabulatortaste folgt ihm ebenfalls. Ein Element, das visuell oben rechts steht, im HTML aber ganz hinten, wird zuletzt vorgelesen und zuletzt fokussiert.

Betroffen sind zwei Erfolgskriterien. 1.3.2 Bedeutungstragende Reihenfolge (Stufe A) verlangt, dass die Lesereihenfolge programmatisch bestimmbar ist, wenn die Reihenfolge die Bedeutung beeinflusst. 2.4.3 Fokus-Reihenfolge (Stufe A) verlangt eine Fokusreihenfolge, die Bedeutung und Bedienbarkeit erhält. Ein Sprung des Fokus von oben links nach unten rechts und zurück verletzt das.

Die Lösung liegt im HTML. Sortiere den Quelltext in die inhaltlich richtige Reihenfolge und baue das Raster darum herum. Der Test dauert eine Minute. Du drückst wiederholt die Tabulatortaste und beobachtest, ob der Fokus einen nachvollziehbaren Weg nimmt. Das Vorgehen steht unter Tastaturbedienung und Fokus und unter Barrierefreiheit selbst testen.

grid-auto-flow: dense sortiert visuell um

dense zieht spätere Elemente in frühere Lücken vor. Das Ergebnis ist ein lückenloses Raster, dessen visuelle Reihenfolge von der Quelltextreihenfolge abweicht. Bei reinem Bildmaterial ohne inhaltliche Ordnung bleibt das folgenlos. Sobald die Reihenfolge Bedeutung hat, etwa bei nummerierten Schritten oder Suchtreffern, verletzt dense das Kriterium 1.3.2. Bei fokussierbaren Elementen wie klickbaren Cards kommt der Verstoß gegen 2.4.3 dazu.

Reflow: das Raster muss bei 320 Pixeln umbrechen

1.4.10 Reflow (Stufe AA) verlangt, dass Inhalt bei einer Breite von 320 CSS-Pixeln ohne waagerechtes Scrollen nutzbar bleibt. Das entspricht 400 Prozent Zoom auf einem Fenster von 1280 Pixeln. Feste Spaltenbreiten in Pixel verhindern das zuverlässig, weil repeat(3, 320px) unabhängig vom Fenster 960 Pixel plus Abstände belegt.

Verwende deshalb fr und minmax() statt fester Breiten. repeat(auto-fit, minmax(16rem, 1fr)) erledigt die Anpassung ohne Media Query. Bei Layouts mit fester Seitenspalte gehört ein Breakpoint dazu, der grid-template-areas auf eine Spalte umstellt. Weil rem mit der Browser-Schriftgröße mitwächst, arbeitet das Muster auch für 1.4.4 Textgröße ändern. Der Prüfweg mit Zoomstufen steht unter Reflow, Zoom und Textabstände, die Sicht der Nutzenden unter Bildschirmvergrößerung.

minmax(0, 1fr) gegen überlaufenden Inhalt

Eine 1fr-Spur hat als Untergrenze auto und damit die Mindestgröße ihres Inhalts. Ein langes Wort, eine URL oder ein breiter Codeblock schiebt die Spalte über die Containerbreite hinaus und erzeugt waagerechtes Scrollen auf der ganzen Seite. minmax(0, 1fr) setzt die Untergrenze auf null. Der Inhalt selbst bekommt dann overflow-x: auto oder overflow-wrap: anywhere, wie unter overflow und Scrollen beschrieben.

display: grid auf Tabellen zerstört die Semantik

Ein häufiger Weg zu responsiven Tabellen setzt display: grid oder display: block auf table, tr oder td. Damit verlieren die Elemente ihre Tabellenrolle im Accessibility-Tree. Screenreader melden danach weder Zeilen- und Spaltenzahl noch die Kopfzelle zu einer Datenzelle. Das verstößt gegen 1.3.1 Info und Beziehungen (Stufe A).

/* Don't: die Tabellensemantik geht verloren */
@media (max-width: 40rem) {
  table, tr, td { display: grid; }
}
/* Do: die Tabelle bleibt eine Tabelle und bekommt einen Scrollbereich */
.tabellen-wrapper {
  overflow-x: auto;
}

Der Scrollbereich braucht tabindex="0" und einen zugänglichen Namen, damit er per Tastatur erreichbar bleibt. Die Muster dafür stehen unter Tabellen semantisch aufbauen und komplexe Datentabellen. Dieselbe Vorsicht gilt für Listen. display: grid auf einer ul entfernt in Safari mit VoiceOver die Listensemantik, solange role="list" fehlt.

Häufige Fragen

Wann nehme ich CSS Grid und wann Flexbox?

Zähle die Achsen. Musst du Zeilen und Spalten zugleich ausrichten, nimm Grid. Steht eine Reihe von Elementen nur nebeneinander oder nur untereinander, nimm Flexbox. Grid geht vom Layout aus, der Container legt das Raster fest. Flexbox geht vom Inhalt aus, die Elemente melden ihre Wunschgröße. In der Praxis kombinierst du beides: Grid für das Seitengerüst, Flexbox für Button-Gruppen und Navigationszeilen.

Was ist der Unterschied zwischen auto-fit und auto-fill?

Beide rechnen aus, wie viele Spalten in die Breite passen. auto-fill behält leere Spuren bei, sodass drei Karten in einem Raster für sechs Spalten ihre Breite behalten und links stehen bleiben. auto-fit klappt leere Spuren auf null zusammen. Die vorhandenen Karten wachsen dann über 1fr und füllen die Zeile aus. Für Kartenlisten willst du meist auto-fit.

Wie lasse ich ein Element über mehrere Spalten laufen?

Über grid-column. Mit grid-column: span 2 belegt das Element zwei Spalten ab seiner aktuellen Position. Mit grid-column: 1 / 3 legst du Start- und Endlinie fest, das sind ebenfalls zwei Spalten. grid-column: 1 / -1 reicht immer über die volle Breite, weil -1 die letzte Linie bezeichnet. Für Zeilen gilt grid-row mit derselben Syntax.

Warum gibt es bei Grid kein flex-wrap?

Grid braucht keine eigene Umbruch-Eigenschaft, weil das Raster den Umbruch selbst bestimmt. Bei grid-template-columns: repeat(3, 1fr) beginnt nach drei Elementen eine neue Zeile. Soll die Spaltenzahl von der Breite abhängen, übernimmt repeat(auto-fit, minmax(16rem, 1fr)) diese Aufgabe. Feste Pixelbreiten verhindern den Umbruch und erzeugen waagerechtes Scrollen.

Ändert Grid die Reihenfolge für Screenreader?

Nein. Screenreader und die Tabulatortaste folgen dem Quelltext. Verschiebst du ein Element per grid-column oder grid-area visuell nach vorn, bleibt es in der Vorlesereihenfolge an seiner alten Stelle. Bei bedeutungstragender Reihenfolge sind das Verstöße gegen die Kriterien 1.3.2 und 2.4.3. Sortiere deshalb das HTML richtig und baue das Raster darum herum.

Verwandte Themen

Quellen

Gratis E-Book PDF, 37 Seiten HTML & Barrierefreiheit, WCAG & BFSG, GEO & SEO

Kostenloses E-Book

Das Praxishandbuch für sauberes, zugängliches Web

Alles rund um semantisches HTML, Barrierefreiheit, WCAG & BFSG, GEO und SEO — praxisnah und am echten Code. In mehreren Feedbackschleifen von Leserinnen und Lesern verbessert.

  • 3.000+ Downloads
  • 7. Auflage
  • 37 Seiten
  • PDF

Kein Spam. Abmeldung jederzeit mit einem Klick möglich.