CSS-Grundlagen: Webseiten gestalten mit Selektoren, Box-Modell, Flexbox und Grid

https://bildung.social/@oerinformatik/

https://oer-informatik.de/css-grundlagen

CSS, Selektor, Kaskade, Spezifität, Vererbung, Box-Modell, CSS-Variablen, Kontrast, WCAG, Flexbox, Grid, Media Queries

tl/dr; (ca. 31 min Lesezeit): CSS legt fest, wie eine Webseite aussieht: Schriften, Farben, Abstände und die Anordnung der Bereiche. Am Beispiel der Abfahrtsanzeige für einen Flurmonitor lernst Du, wie Du ein Stylesheet einbindest, Elemente mit Selektoren auswählst und vorhersagst, welche Regel sich durchsetzt. Du berechnest Maße mit dem Box-Modell, wählst passende Einheiten und Farben mit ausreichendem Kontrast und baust ein Layout mit Flexbox und Grid, das sich per Media Query an einen hochkant montierten Monitor anpasst [AI-Assisted]. (Zuletzt geändert am 11.11.2026)

Dieser Artikel ist Teil einer Artikelreihe über die Grundlagen der Webentwicklung: HTML / CSS / Bootstrap

Schon fit in diesem Thema? Nutze die Kann-Liste und die Leitfragen unter dem Artikel als Checkliste, ob Du die relevanten Kompetenzen bereits hast.

Dieser Artikel baut auf der Kann-Liste des Artikels HTML-Grundlagen auf.

Als CSS-Grundlagen kann ich nach Durchlesen des Artikels…

  • … CSS auf drei Arten einbinden (externes Stylesheet, style-Element, style-Attribut) und begründen, warum ein externes Stylesheet der Standard ist.

  • … an einer CSS-Regel Selektor, Deklarationsblock, Eigenschaft und Wert benennen.

  • … Element-, Klassen-, ID-, Attribut-, Nachfahren- und Kindselektoren sowie die Pseudoklassen :hover, :focus-visible und :nth-child() anwenden.

  • … Kaskade, Spezifität und Vererbung erklären und vorhersagen, welche Regel sich für ein Element durchsetzt.

  • … das Box-Modell mit Inhalt, Innenabstand, Rahmen und Außenabstand erklären und Maße mit und ohne box-sizing: border-box berechnen.

  • … die Einheiten px, rem, em, %, vw und vh unterscheiden und für eine Anzeige passende auswählen.

  • … Farben als Hexwert, mit rgb() und hsl() angeben und mit CSS-Variablen an einer Stelle festlegen.

Darüber hinaus kann ich als CSS-Expertenwissen…

  • … Farbkontraste nach WCAG 2.2 prüfen und Mängel beheben.

  • … Layouts mit Flexbox (eindimensional) und Grid (zweidimensional, mit grid-template-areas) bauen und mit Media Queries an Quer- und Hochformat anpassen.

  • … mit den Entwicklerwerkzeugen des Browsers herausfinden, welche Regel wirkt und woher ein Abstand kommt.

Im Artikel zu den HTML-Grundlagen ist für den Flurmonitor im DBH-Berufskolleg eine Abfahrtsanzeige mit den offenen Fahrtangeboten der Mitfahr-App „Mitfahrlabor“ entstanden. Die Struktur steht: Kopfbereich mit Navigation, zwei Tabellen mit Abfahrten, ein Hinweis der Schulverwaltung und ein Fußbereich. Im Browser sieht die Seite aber noch so aus:

Bildschirmfoto der Abfahrtsanzeige im Browser ohne CSS: Überschriften, Navigationsliste und zwei Tabellen in Standarddarstellung

Für einen Bildschirm im Eingangsbereich reicht das nicht. Wer im Vorbeigehen aus drei Metern Entfernung auf den Monitor schaut, braucht große Schrift, klare Kontraste und eine Anordnung, die auf einen Blick zeigt, wohin es geht. Genau das leisten die Cascading Style Sheets (CSS). HTML beschreibt, was ein Teil der Seite ist, CSS legt fest, wie er aussieht. Am Ende dieses Artikels sieht dieselbe HTML-Datei so aus:

Bildschirmfoto der gestalteten Abfahrtsanzeige im Querformat: blauer Kopfbereich mit Logo und Navigationsknöpfen, zwei Tabellen nebeneinander mit Zebrastreifen, rechts ein gelber Hinweiskasten

CSS wird vom W3C in einzelnen Modulen (etwa für Farben, Flexbox oder Grid) weiterentwickelt, eine Versionsnummer wie „CSS3“ gibt es nicht mehr.

CSS einbinden: drei Wege, ein Standard

Ein Browser kennt drei Orte, an denen CSS stehen kann:

<!-- 1. externes Stylesheet: eigene Datei, im head verlinkt -->
<link rel="stylesheet" href="css/flurmonitor.css">

<!-- 2. style-Element im head: gilt nur für diese eine Seite -->
<style>
  h1 { color: #1d4f91; }
</style>

<!-- 3. style-Attribut: gilt nur für dieses eine Element -->
<p style="color: #1d4f91;">Flurmonitor im Eingang A</p>

Der Standard ist das externe Stylesheet. Lege im Ordner flurmonitor einen Unterordner css an und darin die Datei flurmonitor.css. In beiden Seiten, abfahrtsanzeige.html und hinweis-anlegen.html, kommt im head direkt unter den title die Zeile mit <link rel="stylesheet" href="css/flurmonitor.css">. Für diesen Weg spricht:

  • Trennung von Struktur und Gestaltung: Wer das Aussehen ändert, muss die Inhalte nicht anfassen.

  • Wiederverwendung: Eine Datei gestaltet beliebig viele Seiten. Soll das Blau des Mitfahrlabors dunkler werden, ändert sich das an einer Stelle für alle Seiten.

  • Zwischenspeicher: Der Browser lädt die Datei einmal und hält sie im Cache für alle weiteren Seiten.

  • Sicherheit: Viele Webanwendungen verbieten mit einer Content Security Policy eingebettetes CSS, weil darüber Angriffe möglich sind. Externe Dateien sind davon nicht betroffen.

Das style-Element ist praktisch für kleine Experimente oder eine einzelne Seite. Das style-Attribut solltest Du meiden: Es vermischt Inhalt und Gestaltung wieder, lässt sich nicht wiederverwenden und setzt sich, wie Du gleich sehen wirst, gegen fast jede andere Regel durch.

Aufbau einer CSS-Regel

Ein Stylesheet besteht aus Regeln. Jede Regel nennt zuerst, welche Elemente gemeint sind, und dann, wie sie aussehen sollen:

Aufbau einer CSS-Regel: Selektor nav a, Deklarationsblock in geschweiften Klammern mit zwei Deklarationen aus Eigenschaft, Doppelpunkt, Wert und Semikolon (mit KI erstellt)
  • Der Selektor (nav a) wählt Elemente aus dem DOM-Baum aus, hier alle Links innerhalb der Navigation.

  • Der Deklarationsblock steht in geschweiften Klammern und enthält eine oder mehrere Deklarationen.

  • Jede Deklaration besteht aus einer Eigenschaft (color), einem Doppelpunkt und einem Wert (#ffffff). Ein Semikolon trennt sie von der nächsten Deklaration. Hinter der letzten darf es fehlen, üblich ist es trotzdem.

  • Manche Eigenschaften nehmen mehrere Werte auf. padding: 0.4rem 0.8rem setzt den Innenabstand oben und unten auf 0.4rem, links und rechts auf 0.8rem.

  • Mehrere Selektoren mit Komma teilen sich einen Block: th, td { padding: 0.4rem 0.6rem; }.

  • Kommentare stehen zwischen /* und */. Den Kommentar <!-- --> aus HTML gibt es in CSS nicht.

Eine falsch geschriebene Eigenschaft oder einen unbekannten Wert ignoriert der Browser ohne Fehlermeldung, nur diese eine Deklaration fällt weg. Hier helfen die Entwicklerwerkzeuge des Browsers (Abschnitt „Fehler finden mit den Entwicklerwerkzeugen“) und der W3C-CSS-Validator.

Selektoren: Elemente gezielt auswählen

Mit Selektoren bestimmst Du, für welche Elemente des DOM-Baums eine Regel gilt. Die Abfahrtsanzeige braucht die folgenden:

Selektoren und ihre Verwendung in der Abfahrtsanzeige
Selektor Art wählt aus im Flurmonitor
h2 Elementselektor alle Elemente dieses Typs alle Überschriften der zweiten Stufe
.plaetze Klassenselektor alle Elemente mit class="plaetze" Zellen der Spalte „Freie Plätze“
#zur-schule ID-Selektor das eine Element mit id="zur-schule" Bereich „Zur Schule“
a[href^="https://"] Attributselektor Elemente mit einem Attribut bzw. Attributwert; ^= heißt „beginnt mit“ Links auf andere Websites
header nav Nachfahrenselektor (Leerzeichen) nav, das irgendwo innerhalb von header liegt die Navigation im Kopfbereich
nav > ul Kindselektor (>) ul, das direktes Kind von nav ist die Liste der Navigationslinks
td.plaetze verbunden (ohne Leerzeichen) Elemente, die beide Bedingungen erfüllen nur td mit der Klasse plaetze
* Universalselektor alle Elemente Grundeinstellung für das Box-Modell

Auf das Leerzeichen kommt es an: td.wenig meint eine Zelle mit der Klasse wenig, td .wenig dagegen ein Element mit der Klasse wenig, das innerhalb einer Zelle liegt.

Damit die Zellen mit den freien Plätzen ausgewählt werden können, bekommen sie im HTML eine Klasse. Ist nur noch ein Platz frei, kommt eine zweite Klasse dazu. Mehrere Klassen stehen durch Leerzeichen getrennt im selben Attribut:

<th scope="col" class="plaetze">Freie Plätze</th>
...
<td class="plaetze">2</td>
...
<td class="plaetze wenig">1</td>

Neben der link-Zeile sind das die einzigen Änderungen am HTML; alles andere erreichen die Selektoren über die Position im Baum.

Pseudoklassen wählen Elemente nach einem Zustand oder nach ihrer Position aus. Sie beginnen mit einem Doppelpunkt:

  • nav a:hover gilt, solange der Mauszeiger über dem Link steht.

  • a:focus-visible gilt, wenn der Link den Tastaturfokus hat und der Browser einen sichtbaren Fokus für sinnvoll hält, also bei Bedienung mit der Tabulatortaste, nicht beim Klick mit der Maus.

  • tbody tr:nth-child(even) wählt jede gerade Zeile im Tabellenrumpf. Statt even sind auch odd oder Formeln wie 3n+1 erlaubt.

  • :root ist das Wurzelelement des Dokuments, in HTML also html.

Daneben gibt es Pseudoelemente mit zwei Doppelpunkten. ::after erzeugt einen zusätzlichen Kasten hinter dem Inhalt eines Elements, dessen Text in der Eigenschaft content steht. Damit ergänzt der Flurmonitor bei nur einem freien Platz den Hinweis „(letzter)“:

tbody tr:nth-child(even) {
  background-color: var(--farbe-akzent-hell);
}

.wenig {
  color: var(--farbe-warnung-text);
  background-color: var(--farbe-warnung);
}

.wenig::after {
  content: " (letzter)";
  font-weight: normal;
  font-size: 0.8em;
}

Erzeugter Text lässt sich nicht markieren, und nicht jede assistive Technik liest ihn vor. Wichtige Inhalte gehören daher ins HTML; für einen Zusatz, der die Farbe nur ergänzt, passt ::after.

Kaskade, Spezifität und Vererbung

Oft treffen mehrere Regeln auf dasselbe Element zu. Die Zelle mit der 1 bei Herford ist eine td, hat die Klassen plaetze und wenig und liegt in einer geraden Tabellenzeile. Welche Regel gilt, entscheidet die Kaskade: Für jede Eigenschaft eines Elements sammelt der Browser alle Deklarationen, die zutreffen, und wählt eine davon nach festen Kriterien aus. Erst wenn ein Kriterium keinen Sieger ergibt, kommt das nächste:

Kaskade in vier Schritten: Herkunft und Wichtigkeit, style-Attribut, Spezifität, Reihenfolge; daneben eine Tabelle mit der Spezifität von fünf Selektoren (mit KI erstellt)
  1. Herkunft und Wichtigkeit: Jeder Browser bringt ein eigenes Stylesheet mit Vorgaben mit (daher sind Links blau und Überschriften fett). Deklarationen der Seite gehen diesen Vorgaben vor. Mit !important hinter einem Wert lässt sich eine Deklaration über alle normalen heben. Das ist ein Notbehelf, der spätere Änderungen erschwert. Im Flurmonitor kommt es nicht vor.

  2. style-Attribut: Deklarationen direkt am Element setzen sich gegen jeden Selektor im Stylesheet durch.

  3. Spezifität: Je genauer ein Selektor ist, desto mehr Gewicht hat er. Dazu zählt man drei Spalten: IDs, dann Klassen, Attribute und Pseudoklassen, dann Elemente und Pseudoelemente. Der Universalselektor * und Kombinatoren wie Leerzeichen oder > zählen nicht. Verglichen wird spaltenweise von links: (1,0,1) schlägt (0,2,1), weil schon die erste Spalte entscheidet, egal wie viele Klassen in der zweiten stehen.

  4. Reihenfolge: Bei gleicher Spezifität gewinnt die Deklaration, die zuletzt notiert ist, auch über Dateigrenzen hinweg in der Reihenfolge der link- und style-Elemente.

Ein Beispiel aus dem Flurmonitor: Die Regel th, td { text-align: left; } hat für eine Zelle die Spezifität (0,0,1), die Regel .plaetze { text-align: right; } die Spezifität (0,1,0). Die Zellen mit den freien Plätzen sind deshalb rechtsbündig, alle anderen linksbündig. Würde jemand später zusätzlich #zur-schule td { text-align: center; } notieren, wären in diesem Bereich alle Zellen zentriert, auch die mit der Klasse plaetze, denn (1,0,1) schlägt (0,1,0). Wegen dieses Gewichts verwendet man ID-Selektoren sparsam.

Beachte: Die Kaskade vergleicht nur Deklarationen für dieselbe Eigenschaft am selben Element. Die Regel für gerade Zeilen färbt den Hintergrund der Zeile tr, die Regel .wenig den Hintergrund der Zelle td. Beide gelten, und die Zelle liegt über der Zeile. Darum ist die Zelle bei Herford gelb, obwohl die Zeile hellblau ist.

Die Vererbung greift, wenn für eine Eigenschaft überhaupt keine Deklaration zutrifft. Dann übernimmt ein Element bei manchen Eigenschaften den Wert seines Elternelements. Vererbt werden vor allem Eigenschaften für Text: color, font-family, font-size, line-height und text-align. Nicht vererbt werden Eigenschaften für den Kasten selbst wie margin, padding, border, background-color, width oder display. Deshalb genügt es, Schriftart und Textfarbe einmal am body festzulegen.

Ein typischer Stolperstein: Der Kopfbereich bekommt color: #ffffff. Überschrift und Untertitel erben die weiße Schrift, die Links in der Navigation aber nicht, denn das Browser-Stylesheet setzt für Links eine eigene Farbe. Eine Deklaration, die direkt für ein Element gilt, schlägt immer den geerbten Wert, auch wenn sie aus dem Browser-Stylesheet stammt. Deshalb steht in der Regel nav a noch einmal color: #ffffff. Alternativ übernimmt der Schlüsselwert inherit ausdrücklich den Wert des Elternelements: color: inherit.

Das Box-Modell

Für CSS ist jedes Element ein rechteckiger Kasten. Er besteht aus vier Schichten von innen nach außen:

Box-Modell mit Inhalt, Innenabstand, Rahmen und Außenabstand von innen nach außen, daneben die Formeln für die Breite bei content-box und border-box (mit KI erstellt)
  • Inhalt (content): Text, Bilder, Kindelemente. Seine Größe legen width und height fest.

  • Innenabstand (padding): der Raum zwischen Inhalt und Rahmen. Die Hintergrundfarbe reicht bis hierhin.

  • Rahmen (border): eine Linie mit Breite, Stil und Farbe, z. B. border: 2px solid #ffffff.

  • Außenabstand (margin): Abstand zu den Nachbarn. Er ist immer durchsichtig.

Jede Schicht lässt sich für die vier Seiten einzeln setzen (padding-top, border-left, margin-bottom …) oder in Kurzschreibweise im Uhrzeigersinn, beginnend oben: margin: 0 0 0.5rem heißt oben 0, links und rechts 0, unten 0.5rem. Elemente im Textfluss (inline) wie a oder span nehmen width, height und senkrechte Außenabstände nicht an. Mit display: block werden sie zu Blockelementen; so bekommen die Navigationslinks eine bequem anklickbare Fläche.

Wie breit ein Element auf dem Bildschirm wird, hängt von box-sizing ab. Ein Beispiel mit width: 300px, padding: 20px, border: 4px solid und margin: 16px:

Breite eines Elements mit width 300px, padding 20px, border 4px und margin 16px
Größe box-sizing: content-box (Vorgabe) box-sizing: border-box
width gilt für nur den Inhalt Inhalt, Innenabstand und Rahmen
Breite des Inhalts 300 px 300 px − 2 · 20 px − 2 · 4 px = 252 px
sichtbare Breite (mit Rahmen) 300 px + 2 · 20 px + 2 · 4 px = 348 px 300 px
belegter Platz (mit Außenabstand) 348 px + 2 · 16 px = 380 px 300 px + 2 · 16 px = 332 px

Mit content-box wird ein Kasten mit width: 100% und Innenabstand breiter als sein Elternelement und ragt heraus. Fast alle Stylesheets beginnen deshalb mit derselben Regel, die border-box für alle Elemente festlegt:

*,
*::before,
*::after {
  box-sizing: border-box;
}

Einheiten: Wie groß ist groß?

Längen stehen in CSS immer mit Einheit (nur bei 0 darf sie fehlen). Die wichtigsten:

Häufig genutzte Einheiten in CSS
Einheit bezieht sich auf typischer Einsatz
px CSS-Pixel, festgelegt als 1/96 Zoll; auf hochauflösenden Bildschirmen umfasst ein CSS-Pixel mehrere Gerätepixel feine Linien, Rahmen
rem Schriftgröße des Wurzelelements html (root em) Schriftgrößen, Abstände
em Schriftgröße des Elements selbst (bei font-size: des Elternelements) Größen, die mit der Schrift des Elements mitwachsen sollen
% meist die entsprechende Größe des Elternelements Breiten
vw, vh 1 % der Breite bzw. Höhe des sichtbaren Fensters (viewport) Mindesthöhe einer Seite, sehr große Anzeigen

Die Grundschrift der meisten Browser ist 16 px. Für den Flurmonitor reicht das nicht. Deshalb setzt das Stylesheet mit html { font-size: 125%; } die Schriftgröße des Wurzelelements auf 20 px. Alle anderen Größen sind in rem angegeben und wachsen mit: Die Tabellen mit 1.2rem haben 24 px, die Hauptüberschrift mit 2.2rem 44 px. Soll die Schrift auf einem größeren Monitor noch größer werden, genügt eine einzige Änderung an html. Der Zusatz (letzter) ist mit 0.8em angegeben. Er bezieht sich damit auf die Schrift seiner Zelle und ergibt 0,8 · 24 px = 19,2 px.

Warum nicht einfach font-size: 20px? Prozent- und rem-Angaben berücksichtigen die Schriftgröße, die jemand im Browser eingestellt hat. Wer dort eine größere Grundschrift wählt, bekommt auch auf dieser Seite größere Schrift. Feste Pixelangaben für Schrift übergehen diese Einstellung. Am body sorgt min-height: 100vh dafür, dass er mindestens die ganze Fensterhöhe einnimmt.

Farben und CSS-Variablen

Farben lassen sich auf mehrere Arten angeben. Das Blau des Mitfahrlabors lautet in jeder Schreibweise gleich:

Schreibweisen für Farben in CSS
Schreibweise Beispiel Bedeutung
Name white, black, navy rund 150 fest definierte Farbnamen
Hexwert #1d4f91 je zwei Hexadezimalziffern für Rot, Grün, Blau (00 bis ff); Kurzform #fff = #ffffff
rgb() rgb(29 79 145) Rot, Grün, Blau als Zahlen von 0 bis 255
hsl() hsl(214 67% 34%) Farbton als Winkel im Farbkreis, Sättigung, Helligkeit
mit Deckkraft rgb(29 79 145 / 50%), #1d4f9180 vierter Wert für die Deckkraft (alpha)

Ältere Stylesheets trennen die Werte mit Kommas: rgb(29, 79, 145). hsl() ist praktisch, wenn Du eine hellere oder dunklere Variante derselben Farbe brauchst: Du änderst nur den letzten Wert.

Damit dasselbe Blau nicht an zehn Stellen im Stylesheet steht, legt man es einmal als CSS-Variable fest (offiziell custom property). Ihr Name beginnt mit zwei Bindestrichen, gelesen wird sie mit var():

:root {
  --farbe-text: #1b1b1b;
  --farbe-hintergrund: #ffffff;
  --farbe-akzent: #1d4f91;
  --farbe-akzent-hell: #e8eef7;
  --farbe-warnung: #fff1c2;
  --farbe-warnung-text: #5c3d00;
  --farbe-fokus: #b3261e;
  --abstand: 1rem;
}

thead th {
  color: #ffffff;
  background-color: var(--farbe-akzent);
}

Variablen, die an :root stehen, gelten im ganzen Dokument, denn sie werden vererbt. Man kann sie an einem Element oder in einer Media Query auch überschreiben, etwa für ein dunkles Farbschema. Mit calc() lässt sich mit ihnen rechnen: padding: var(--abstand) calc(2 * var(--abstand)).

Kontrast: Farbe muss vor allem lesbar sein. Die Web Content Accessibility Guidelines (WCAG) 2.2 legen dafür Mindestwerte fest. Das Kontrastverhältnis wird aus der relativen Helligkeit L der helleren (L_1) und der dunkleren Farbe (L_2) berechnet und reicht von 1:1 (gleiche Farben) bis 21:1 (Schwarz auf Weiß):

\text{Kontrastverhältnis} = \frac{L_1 + 0{,}05}{L_2 + 0{,}05}

Das Verhältnis zeigen der Farbwähler der Entwicklerwerkzeuge und Online-Rechner wie der WebAIM Contrast Checker an. Die Grenzwerte der Stufe AA:

  • Text: mindestens 4,5:1 (Erfolgskriterium 1.4.3). Für großen Text ab 24 px oder ab 18,66 px in Fettschrift genügen 3:1.

  • Bedienelemente und Grafiken: Rahmen von Eingabefeldern, Fokusrahmen und Symbole brauchen mindestens 3:1 zur Umgebung (Erfolgskriterium 1.4.11).

  • Nicht nur Farbe: Eine Information darf nicht allein über die Farbe vermittelt werden (Erfolgskriterium 1.4.1). Rund 8 % der Männer haben eine Rot-Grün-Sehschwäche. Darum ist der letzte freie Platz nicht nur gelb hinterlegt, sondern zusätzlich mit „(letzter)“ beschriftet.

Die Farben des Flurmonitors erfüllen alle Anforderungen deutlich:

Kontrastverhältnisse der Farben im Flurmonitor
Vordergrund Hintergrund Verhältnis genutzt für
#1b1b1b #ffffff 17,2:1 Fließtext
#ffffff #1d4f91 8,1:1 Kopfbereich, Tabellenkopf
#1b1b1b #e8eef7 14,8:1 Text in gestreiften Zeilen
#5c3d00 #fff1c2 8,8:1 Hinweis, letzter Platz
#b3261e #ffffff 6,5:1 Fokusrahmen

Zur Barrierefreiheit gehört auch ein gut sichtbarer Fokusrahmen. Viele Stylesheets entfernen ihn mit outline: none, weil er beim Klicken stört. Damit können Menschen, die nur mit der Tastatur arbeiten, nicht mehr erkennen, wo sie sich befinden (Erfolgskriterium 2.4.7). Die Pseudoklasse :focus-visible löst das Problem, denn sie greift nur bei Tastaturbedienung:

a:focus-visible {
  outline: 4px solid var(--farbe-fokus);
  outline-offset: 3px;
}

header a:focus-visible {
  outline-color: #ffffff;
}

Auf dem blauen Kopfbereich wäre der rote Rahmen zu schwach, dort wird er weiß. Die zweite Regel setzt sich durch, weil header a:focus-visible mit (0,1,2) spezifischer ist als a:focus-visible mit (0,1,1). Anders als border braucht outline keinen Platz im Box-Modell und verschiebt beim Erscheinen nichts.

Flexbox: Elemente in einer Reihe anordnen

Die Navigation besteht aus einer Liste. Die drei Links sollen nebeneinander stehen, ohne Aufzählungszeichen, mit gleichmäßigem Abstand. Das ist die Aufgabe des Flexible Box Layout, kurz Flexbox. Flexbox ordnet Elemente entlang einer Richtung an, in einer Zeile oder in einer Spalte.

nav > ul {
  display: flex;
  gap: var(--abstand);
  margin: 0;
  padding: 0;
  list-style: none;
}

Mit display: flex wird ul zum Flex-Container, seine direkten Kinder, die li-Elemente, werden zu Flex-Items. Sie stehen nun entlang der Hauptachse nebeneinander. Die wichtigsten Eigenschaften des Containers:

  • flex-direction: Hauptachse row (Vorgabe, waagerecht) oder column (senkrecht),

  • justify-content: Verteilung entlang der Hauptachse (flex-start, center, space-between …),

  • align-items: Ausrichtung quer dazu, z. B. center,

  • gap: Abstand zwischen den Items,

  • flex-wrap: wrap: Umbruch in die nächste Zeile, wenn der Platz nicht reicht.

Auch der Kopfbereich ist ein Flex-Container mit Logo, Überschrift, Untertitel und Navigation als Items. Der Trick margin-left: auto an der Navigation schiebt sie an den rechten Rand, denn ein automatischer Außenabstand nimmt den gesamten freien Platz ein:

header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--abstand) calc(2 * var(--abstand));
  padding: var(--abstand) calc(2 * var(--abstand));
  color: #ffffff;
  background-color: var(--farbe-akzent);
}

header nav {
  margin-left: auto;
}

Ist das Fenster schmal, bricht die Navigation dank flex-wrap: wrap in eine eigene Zeile um und bleibt rechtsbündig.

Grid: Bereiche in Zeilen und Spalten anordnen

Für die Bereiche im main reicht eine Richtung nicht. Die Bereichsskizze aus dem HTML-Artikel sieht drei Spalten nebeneinander vor, auf einem hochkant gedrehten Monitor sollen die Bereiche untereinander stehen. Das erledigt das Grid Layout: Es ordnet Elemente in zwei Richtungen an, in Zeilen und Spalten.

Grid-Bereiche der Abfahrtsanzeige: im Querformat drei Spalten zur-schule, nach-hause und hinweis, im Hochformat dieselben Bereiche untereinander mit dem Hinweis oben (mit KI erstellt)
main {
  display: grid;
  grid-template-columns: 1fr 1fr minmax(12rem, 0.7fr);
  grid-template-areas: "zur-schule nach-hause hinweis";
  gap: calc(1.5 * var(--abstand));
  padding: calc(1.5 * var(--abstand)) calc(2 * var(--abstand));
}

#zur-schule {
  grid-area: zur-schule;
}

#nach-hause {
  grid-area: nach-hause;
}

aside {
  grid-area: hinweis;
  align-self: start;
}
  • display: grid macht main zum Grid-Container, die beiden section-Elemente und das aside zu Grid-Items.

  • grid-template-columns legt die Spalten fest. Die Einheit fr (fraction) verteilt den freien Platz: Zwei Spalten mit 1fr sind gleich breit. minmax(12rem, 0.7fr) macht die dritte Spalte etwas schmaler, aber nie schmaler als 12rem, damit der Hinweis lesbar bleibt.

  • grid-template-areas gibt den Bereichen Namen und zeichnet das Layout fast wie eine Skizze: Jede Zeichenkette ist eine Zeile, jedes Wort eine Zelle. Mehrere Zeilen stehen als mehrere Zeichenketten untereinander.

  • grid-area am Item legt fest, in welchen Bereich es gehört. Die Reihenfolge im HTML spielt dafür keine Rolle.

  • align-self: start verhindert, dass der Hinweiskasten auf die Höhe der Tabellen gestreckt wird.

Flexbox oder Grid? Als Faustregel: Flexbox für Elemente in einer Reihe, deren Größe sich nach dem Inhalt richtet (Navigation, Knopfleisten). Grid für das Gerüst einer Seite, bei dem Zeilen und Spalten zusammenpassen müssen. Beides lässt sich verschachteln: Ein Grid-Item kann selbst wieder ein Flex-Container sein. Eines solltest Du dabei beachten: Bei Bereichen mit Links oder Formularfeldern sollte die sichtbare Reihenfolge zur Reihenfolge im HTML passen. Sonst springt der Tastaturfokus beim Weiterschalten kreuz und quer über die Seite. Der Hinweis der Schulverwaltung enthält keine Bedienelemente; dass er im Hochformat gleich nach oben rückt, ist deshalb unkritisch.

Media Queries: ein Layout für hochkant montierte Monitore

Nicht jeder Flurmonitor hängt quer. Im Gang vor dem Lehrerzimmer soll ein hochkant montierter Bildschirm dieselbe Seite zeigen. Mit einer Media Query gelten Regeln nur dann, wenn das Ausgabegerät eine Bedingung erfüllt:

@media (orientation: portrait) {
  main {
    grid-template-columns: 1fr;
    grid-template-areas:
      "hinweis"
      "zur-schule"
      "nach-hause";
  }
}

orientation: portrait trifft zu, wenn das Fenster höher als breit ist. Dann gibt es nur noch eine Spalte, und der Hinweis rückt nach oben, wo er auch im Vorbeigehen auffällt:

Bildschirmfoto der Abfahrtsanzeige im Hochformat: blauer Kopfbereich, darunter der gelbe Hinweis über die ganze Breite und die beiden Tabellen untereinander

Weitere häufige Bedingungen sind Breiten wie (min-width: 60rem), (prefers-color-scheme: dark) für ein dunkles Farbschema und print für den Ausdruck. Media Queries stehen meist am Ende des Stylesheets. Ihre Regeln haben keine höhere Spezifität und setzen sich nur durch, weil sie später notiert sind.

Ein Layout, das sich auf diese Weise an Bildschirmgrößen anpasst, nennt man Responsive Design. Es funktioniert auf Smartphones nur zusammen mit der viewport-Angabe im head, die schon im Grundgerüst des HTML-Artikels steht. Bei Websites schreibt man die Regeln oft zuerst für schmale Bildschirme und ergänzt breitere Layouts mit min-width (mobile first).

Fehler finden mit den Entwicklerwerkzeugen

Sieht ein Element anders aus als erwartet, wähle es in den Entwicklerwerkzeugen (F12) im Reiter „Elemente“ bzw. „Inspektor“ aus:

  • Der Bereich Styles bzw. Regeln zeigt alle Regeln, die auf das Element zutreffen, geordnet nach der Kaskade. Durchgestrichene Deklarationen haben gegen eine andere verloren, ein Warnsymbol markiert unbekannte Eigenschaften oder ungültige Werte. Werte lassen sich dort direkt ausprobieren.

  • Der Bereich Berechnet zeigt den Wert, der am Ende gilt, und mit einem Klick, aus welcher Regel er stammt. Hier siehst Du auch, ob ein Wert geerbt wurde.

  • Die Box-Modell-Ansicht zeigt Inhalt, Innenabstand, Rahmen und Außenabstand mit Zahlen. Woher eine unerwartete Lücke kommt, erkennst Du hier am schnellsten.

  • Die Geräteansicht (in Chrome und Edge Strg+Umschalt+M) stellt beliebige Fenstergrößen ein, auch Hochformat, ohne den Monitor zu drehen.

Das vollständige Stylesheet kannst Du hier einblenden und als css/flurmonitor.css speichern. Den ganzen Stand mit beiden HTML-Seiten findest Du im Repository zu diesem Artikel im Ordner src/css-grundlagen/.

/* Flurmonitor des Mitfahrlabors – Stylesheet zum Artikel
   https://oer-informatik.de/css-grundlagen */

/* 1. Farben und Abstände als CSS-Variablen */
:root {
  --farbe-text: #1b1b1b;
  --farbe-hintergrund: #ffffff;
  --farbe-akzent: #1d4f91;
  --farbe-akzent-hell: #e8eef7;
  --farbe-warnung: #fff1c2;
  --farbe-warnung-text: #5c3d00;
  --farbe-fokus: #b3261e;
  --abstand: 1rem;
}

/* 2. Box-Modell: Breite und Höhe schließen Innenabstand und Rahmen ein */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* 3. Grundschrift: 125 % von 16 px = 20 px, alle rem-Angaben bauen darauf auf */
html {
  font-size: 125%;
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: "Segoe UI", Arial, sans-serif;
  line-height: 1.4;
  color: var(--farbe-text);
  background-color: var(--farbe-hintergrund);
}

/* 4. Kopfbereich mit Flexbox */
header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--abstand) calc(2 * var(--abstand));
  padding: var(--abstand) calc(2 * var(--abstand));
  color: #ffffff;
  background-color: var(--farbe-akzent);
}

header h1 {
  margin: 0;
  font-size: 2.2rem;
}

header p {
  margin: 0;
}

header nav {
  margin-left: auto;
}

nav > ul {
  display: flex;
  gap: var(--abstand);
  margin: 0;
  padding: 0;
  list-style: none;
}

nav a {
  display: block;
  padding: 0.4rem 0.8rem;
  border: 2px solid #ffffff;
  border-radius: 0.4rem;
  color: #ffffff;
  text-decoration: none;
}

nav a:hover {
  color: var(--farbe-akzent);
  background-color: #ffffff;
}

/* 5. Fokusrahmen für die Bedienung per Tastatur */
a:focus-visible {
  outline: 4px solid var(--farbe-fokus);
  outline-offset: 3px;
}

header a:focus-visible {
  outline-color: #ffffff;
}

/* 6. Hauptbereich mit Grid: Bereiche wie in der Skizze */
main {
  display: grid;
  grid-template-columns: 1fr 1fr minmax(12rem, 0.7fr);
  grid-template-areas: "zur-schule nach-hause hinweis";
  gap: calc(1.5 * var(--abstand));
  padding: calc(1.5 * var(--abstand)) calc(2 * var(--abstand));
}

#zur-schule {
  grid-area: zur-schule;
}

#nach-hause {
  grid-area: nach-hause;
}

aside {
  grid-area: hinweis;
  align-self: start;
  padding: var(--abstand);
  border-left: 0.5rem solid var(--farbe-warnung-text);
  background-color: var(--farbe-warnung);
}

h2 {
  margin: 0 0 0.5rem;
  font-size: 1.4rem;
  color: var(--farbe-akzent);
}

aside h2 {
  color: var(--farbe-warnung-text);
}

/* 7. Tabellen: groß, mit Zebrastreifen */
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 1.2rem;
}

caption {
  text-align: left;
  font-size: 0.8rem;
  padding-bottom: 0.3rem;
}

th,
td {
  padding: 0.4rem 0.6rem;
  text-align: left;
}

thead th {
  color: #ffffff;
  background-color: var(--farbe-akzent);
}

tbody tr:nth-child(even) {
  background-color: var(--farbe-akzent-hell);
}

.plaetze {
  text-align: right;
  font-weight: bold;
}

/* nur noch ein Platz frei: Farbe und Text, nicht Farbe allein */
.wenig {
  color: var(--farbe-warnung-text);
  background-color: var(--farbe-warnung);
}

.wenig::after {
  content: " (letzter)";
  font-weight: normal;
  font-size: 0.8em;
}

/* 8. Fußbereich, Link nach außen erkennbar machen */
footer {
  padding: var(--abstand) calc(2 * var(--abstand));
  font-size: 0.8rem;
  border-top: 2px solid var(--farbe-akzent);
}

a[href^="https://"] {
  color: var(--farbe-akzent);
}

/* 9. Hochkant montierter Monitor: Bereiche untereinander */
@media (orientation: portrait) {
  main {
    grid-template-columns: 1fr;
    grid-template-areas:
      "hinweis"
      "zur-schule"
      "nach-hause";
  }
}

Nächster Artikel

Ein eigenes Stylesheet wie dieses verstehst Du jetzt Zeile für Zeile. In größeren Projekten greift man oft zu einem fertigen CSS-Framework, das Raster, Tabellen, Knöpfe und Formulare schon mitbringt. Im Artikel Bootstrap-Grundlagen gestaltest Du die Abfahrtsanzeige mit Bootstrap und passt das Framework über CSS-Variablen an. Übungsaufgaben zu diesem Artikel findest Du unter CSS-Grundlagen: Übungsaufgaben.

Leitfragen

  1. Warum bindet man CSS in der Regel als externe Datei ein?

Eine externe Datei trennt Struktur und Gestaltung, gestaltet beliebig viele Seiten einheitlich, wird vom Browser zwischengespeichert und ist auch dann erlaubt, wenn eine Content Security Policy eingebettetes CSS verbietet. siehe Abschnitt „CSS einbinden: drei Wege, ein Standard“

  1. Was ist der Unterschied zwischen nav a, nav > a und nav.a?

nav a wählt jeden Link irgendwo innerhalb von nav (Nachfahre), nav > a nur Links, die direkte Kinder von nav sind, und nav.a ein nav-Element mit der Klasse a. siehe Abschnitt „Selektoren: Elemente gezielt auswählen“

  1. Nach welchen Kriterien entscheidet die Kaskade, welche Deklaration gilt?

Nacheinander nach Herkunft und Wichtigkeit (!important), dem style-Attribut, der Spezifität (IDs, dann Klassen/Attribute/Pseudoklassen, dann Elemente) und zuletzt der Reihenfolge. Greift keine Deklaration, wird bei vererbbaren Eigenschaften der Wert des Elternelements übernommen. siehe Abschnitt „Kaskade, Spezifität und Vererbung“

  1. Wie breit wird ein Element mit width: 200px, padding: 10px und border: 1px solid ohne und mit box-sizing: border-box?

Ohne border-box (also mit content-box) 200 + 2 · 10 + 2 · 1 = 222 px, mit border-box genau 200 px; der Inhalt ist dann 178 px breit. siehe Abschnitt „Das Box-Modell“

  1. Warum sind rem für Schriftgrößen besser geeignet als px?

rem bezieht sich auf die Schriftgröße des Wurzelelements. Alle Größen lassen sich so an einer Stelle gemeinsam ändern, und die im Browser eingestellte Grundschrift wird berücksichtigt. siehe Abschnitt „Einheiten: Wie groß ist groß?“

  1. Welche Kontrastverhältnisse verlangt WCAG 2.2 in Stufe AA?

Mindestens 4,5:1 für normalen Text, 3:1 für großen Text sowie für Bedienelemente, Fokusrahmen und Grafiken. Informationen dürfen außerdem nicht allein über die Farbe vermittelt werden. siehe Abschnitt „Farben und CSS-Variablen“

  1. Wann nutzt Du Flexbox, wann Grid?

Flexbox ordnet Elemente in einer Richtung an, etwa eine Navigation; Grid ordnet Bereiche in Zeilen und Spalten an, etwa das Gerüst einer Seite. siehe Abschnitt „Grid: Bereiche in Zeilen und Spalten anordnen“

  1. Wie passt Du ein Layout an einen hochkant montierten Monitor an?

Mit einer Media Query @media (orientation: portrait) { … }, in der z. B. grid-template-columns und grid-template-areas neu gesetzt werden. siehe Abschnitt „Media Queries: ein Layout für hochkant montierte Monitore“


Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “CSS-Grundlagen: Webseiten gestalten mit Selektoren, Box-Modell, Flexbox und Grid” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/css-grundlagen veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/programmierung/web-grundlagen. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 11.11.2026.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: