Zum Inhalt
wannacode

Responsive Design

Kurz gesagt: Responsive Design lässt eine Seite auf jedem Bildschirm funktionieren: das Viewport-Meta-Tag, Mobile First und Media Queries, die das Layout nach der Breite ändern.

Problem

Der Button liegt außerhalb des Bildschirms

Die Produktseite wurde an einem Monitor gebaut, und dort sieht sie gut aus. Ein Kunde am Handy schreibt: „Ich kann nicht bestellen, da ist kein Button.“ Hier ist sie in der Breite eines Handys, 360px:

HTML, CSS, JS

Der Button „Bestellen“ liegt rechts, außerhalb des Bildschirms. Die Seite lässt sich seitwärts schieben, und die meisten kommen nicht auf die Idee. Der Code hat keinen Fehler: Das Bild ist 400px breit, die Textspalte 360px, und sie stehen nebeneinander. Auf einem Monitor mit 1000px Breite passt das. Auf 360px nicht.

Die Idee

Bildschirme haben unterschiedliche Breiten

Dieselbe Seite öffnet sich auf einem Handy (360–430px), auf einem Tablet (etwa 800px) und auf einem Computer (1000px und mehr). Eine Seite, die sich jeder Breite anpasst, heißt responsiv.

Der erste Schritt steht in <head>:

<meta name="viewport" content="width=device-width, initial-scale=1">

Ohne diese Zeile gibt sich das Handy als Monitor aus: Es legt die Seite mit 980px an und verkleinert sie, bis sie passt. Der Text wird winzig, und dein CSS sieht die echten 360px nie. Mit ihr ist die Breite der Seite die Breite des Bildschirms. In der Sandbox fügen wir diese Zeile für dich hinzu, auf deinen eigenen Seiten schreibst du sie selbst.

Der zweite Schritt ist die Reihenfolge, in der du das CSS schreibst. Mobile First heißt: Die Basisregeln sind für den schmalsten Bildschirm. Dort steht alles untereinander, und das passt fast immer. Dann fügst du mit einer Media Query Änderungen für breitere Bildschirme hinzu:

.product {
display: grid;
gap: 16px;
}
@media (min-width: 700px) {
.product {
grid-template-columns: 1fr 1fr;
}
}

@media (min-width: 700px) ist eine Bedingung: „Die Breite ist mindestens 700px.“ Die Regeln darin gelten nur, wenn die Bedingung wahr ist. Sonst überspringt der Browser sie.

Der dritte Schritt sind Bilder. width="400" ist gut für das HTML: Der Browser hält Platz frei, während das Bild lädt. Auf einem schmalen Bildschirm braucht es aber eine Obergrenze:

img {
max-width: 100%;
height: auto;
}

max-width: 100% heißt „nicht breiter als seine Box“, und height: auto behält die Proportionen, damit es nicht gestaucht wird.

Sieh es dir an

So schalten sich die Queries ein

Das Fenster wächst von 360px auf 1000px. Achte auf das Lineal oben und die Zeilen im Code.

body { margin: 0; padding: 16px; }
.cards {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
@media (min-width: 600px) {
.cards { grid-template-columns: 1fr 1fr; }
}
@media (min-width: 900px) {
.cards { grid-template-columns: repeat(4, 1fr); }
}
Schritt für Schritt, in Worten
  1. Ein Handy, 360px. Die Basisregel gilt für den schmalsten Bildschirm: eine Spalte. Eine Karte ist 360 − 2 × 16 = 328px breit.
  2. Das Fenster wird breiter und die Spalte wächst mit. Bei 599px ist die Bedingung min-width: 600px noch falsch, und der Browser überspringt die Regeln darin.
  3. Bei 600px ist die Bedingung wahr. Ihre Regel steht nach der Basisregel und gewinnt: zwei Spalten zu je 276px. Die Karten ordnen sich neu.
  4. Noch breiter: Die zwei Spalten wachsen auf je 425,5px. min-width: 900px ist noch falsch.
  5. Bei 900px sind beide Queries wahr. Sie setzen dieselbe Eigenschaft, also gewinnt die spätere: vier Spalten zu je 205px.
  6. Bei 1000px schaltet sich nichts Neues ein. Die vier Spalten wachsen auf 230px.
  7. Der Browser prüft die Queries bei jeder Änderung der Breite neu: ein gedrehtes Handy, ein schmaleres Fenster. Machst du es schmaler, gehen die Spalten denselben Weg zurück.

Eine Media Query wird nicht einmal berechnet. Der Browser prüft sie bei jeder Änderung der Breite und legt die Seite sofort neu an. Die zwei Queries setzen dieselbe Eigenschaft mit derselben Spezifität, also gewinnt bei 900px die spätere. Das ist die Kaskade, die du schon kennst.

Probier es aus

Für ein Handy reparieren

Die Seite ist 360px breit. Mach sie Mobile First: Bild und Text untereinander, und nichts ragt seitwärts heraus. Das Bild ist nicht breiter als seine Box. Ab 700px stehen Bild und Text in zwei Spalten. Die Prüfung sucht nach seitlichem Scrollen und danach, in wie vielen Zeilen Bild und Textspalte stehen.

HTML, CSS, JS

Probier es aus

Jetzt für einen Computer

Diese Seite ist schon für ein Handy gemacht: alles in einer Spalte. Hier ist die Breite 1000px. Ergänze eine Media Query, damit ab 700px Bild und Text in zwei gleich breiten Spalten stehen.

HTML, CSS, JS

Probier es aus

Finde den Fehler

Auf einem Computer sollten es zwei Spalten sein, aber die Seite bleibt bei einer. Warum?

Wähle die Zeile mit dem Fehler.

Probier es aus

Lücke füllen

Welches Wort lässt die zwei Spalten ab 700px gelten?

@media (: 700px) {
  .product {
    grid-template-columns: 1fr 1fr;
  }
}

Die Idee

Die Farbe an einer Stelle

Das Grün des Shops steht im CSS an vier Stellen: im Preis, in den Links, im Button und im Rahmen. Der Shop ändert seine Farbe, und jemand muss alle finden. Übersieht er eine, hat die Seite zwei Farben.

Eine CSS-Variable (Custom Property) ist ein Wert mit einem Namen. Der Name beginnt mit zwei Strichen, und var() liest ihn:

:root {
--accent: #1a7f37;
}
button {
background: var(--accent);
}

:root ist das oberste Element, <html>. Variablen werden wie color vererbt, also sieht man alles aus :root überall auf der Seite. Die Farbe ändert sich in einer Zeile.

Dasselbe ergibt ein dunkles Theme. @media (prefers-color-scheme: dark) ist wahr, wenn jemand auf Handy oder Computer ein dunkles Theme gewählt hat. Darin änderst du nur die Werte der Variablen. Die Regeln, die sie nutzen, bleiben gleich.

Sieh es dir an

Hell und dunkel

HTML, CSS, JS

Die Sandbox folgt dem Theme deines Browsers, nicht dem Theme-Button dieser Seite. Stell dein System auf dunkel und klick auf „Ausführen“: Hintergrund, Text und Grün ändern sich, während die CSS-Regeln für .card, .price und button gleich bleiben. Das Grün auf dunklem Grund ist heller, damit man es auf dem dunklen Hintergrund lesen kann.

Probier es aus

Eine neue Farbe für den Shop

Der Shop ändert das Grün #1a7f37 in Orange #c2410c. Lege in :root eine Variable --accent mit dem Wert #c2410c an und nutze sie an den vier Stellen, an denen jetzt das Grün steht.

HTML, CSS, JS

Die Idee

Zustände: berührt, fokussiert, deaktiviert

Ein Button sieht nicht nur auf eine Art aus. Er kann unter der Maus liegen, per Tastatur fokussiert sein oder deaktiviert. CSS fängt das mit Pseudoklassen ab: einem Wort mit Doppelpunkt hinter dem Selektor.

button:hover {
background: #116329;
}
button:focus-visible {
outline: 3px solid var(--accent);
outline-offset: 2px;
}
button:disabled {
background: #9ca3af;
cursor: not-allowed;
}
  • :hover: Die Maus ist über dem Element. Ein Handy hat keine Maus, deshalb versteckt man wichtige Informationen nicht hinter :hover.
  • :focus-visible: Das Element hat den Fokus, und der Browser findet, dass der Fokus sichtbar sein soll. Bei der Tastatur ist er immer sichtbar. Bei einem Mausklick auf einen Button meistens nicht. Deshalb ist outline: none an :focus ein Fehler: Wer mit der Tastatur arbeitet, verliert den Überblick, wo er ist.
  • :disabled: Das Element hat das Attribut disabled. Der Button „Ausverkauft“ soll deaktiviert aussehen, nicht nur deaktiviert sein.

Und die Größe. Auf einem Handy drückt man mit dem Finger, und der ist ungenauer als eine Maus. Ein Button von 24px Höhe wird verfehlt. Gib Buttons mindestens 44px Höhe: min-height: 44px.

Probier es aus

Die Buttons auf einem Handy

Ein Kollege hat den Fokusrahmen entfernt, und die Buttons sind niedrig. Sorg dafür, dass die Buttons mindestens 44px hoch sind (min-height), dass beim Tastaturfokus ein 3px breiter durchgehender Rahmen in der Farbe --accent erscheint und dass der deaktivierte Button „Ausverkauft“ grau (#9ca3af) ist, mit dem Cursor not-allowed. Die Prüfung drückt Tab und schaut sich den Button „Bestellen“ an.

HTML, CSS, JS
Genauer, für Neugierige

Viewport, genauer. Ohne das Meta-Tag legen mobile Browser die Seite mit 980px an und verkleinern sie. In Chromium mit einem emulierten Handy von 360px ist innerWidth 980, und @media (min-width: 700px) ist wahr: Das Handy bekommt das Layout des Computers. initial-scale=1 heißt „am Anfang nicht verkleinern“. Schalte den Zoom nicht ab (user-scalable=no, maximum-scale=1): Menschen mit schwachen Augen vergrößern die Seite mit zwei Fingern.

Breakpoints. 600, 700, 900 sind keine heiligen Zahlen. Setz einen Breakpoint dorthin, wo dein Layout schlecht auszusehen beginnt, nicht ans Modell irgendeines Handys. Manche schreiben Queries in em (min-width: 40em): Dann folgen sie auch der Schriftgröße, die jemand gewählt hat.

Queries nicht nur für die Breite. (hover: hover) ist wahr, wenn das Hauptzeigegerät schweben kann (eine Maus), und (pointer: coarse) ist wahr für einen Finger. (prefers-reduced-motion: reduce) sagt, dass jemand keine Bewegung möchte: Nimm die Animationen weg. (orientation: landscape) ist für einen Bildschirm im Querformat.

Variablen, genauer. var(--accent, #1a7f37) hat einen Ersatzwert für den Fall, dass die Variable fehlt. Eine Variable kann sich für einen Teil der Seite ändern: .sale { --accent: #c2410c; }, und alles in .sale ist orange. Der Wert wird erst geprüft, wenn er benutzt wird: --accent: 12px ist erlaubt, aber dann ist color: var(--accent) ungültig, und die Farbe wird die geerbte. Die Eigenschaft color-scheme: light dark sagt dem Browser, seine eigenen Teile (Felder, Scrollbars) auch im dunklen Theme zu färben.

Größe für den Finger. WCAG 2.2 (Kriterium 2.5.8) verlangt ein Ziel von mindestens 24 × 24px oder genug Platz drumherum. Apple empfiehlt 44 × 44pt und Material Design 48 × 48dp.

Übung

Der Katalog auf einem Handy

Der Katalog wurde für einen Computer gemacht: vier Karten zu 220px in einer Reihe. Hier ist die Breite 360px. Mach ihn für ein Handy: die Karten in zwei Spalten, kein seitliches Scrollen, Bilder nicht breiter als ihre Karte, die Buttons „Kaufen“ mindestens 44px hoch (min-height). Lege das Grün #1a7f37 in eine Variable --accent in :root und lass die Buttons sie nutzen. Wenn du magst, ergänze ab 700px auch vier Spalten.

HTML, CSS, JS

Test

Teste dich

1. Du schreibst Mobile First. Welche Regel hat keine Media Query?
2. @media (min-width: 600px) bei einem Fenster von 599px und von 600px?
3. :root hat --accent: #c2410c, und der Button hat background: var(accent). Welche Farbe hat der Button?
4. Warum :focus-visible und nicht :hover für den Rahmen des Buttons?
5. Wie groß sollte ein Button auf einem Handy sein?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·