mitmario.dev

Flexible Bilder und Medien

CSS Im Browser 4 Min Lesezeit 3 BeispieleLektion 4 von 7

Ein einziges Bild in Originalgröße reicht, und auf einem schmalen Gerät ist das Layout kaputt. Fünf Angaben räumen das ab, und drei davon gehören in jedes Stylesheet.

Die eine Regel

Das Bild, das die Seite sprengt
*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 16px;
  font-family: system-ui, sans-serif;
  line-height: 1.5;
  background-color: #f4f4f5;
}

.titel {
  margin-bottom: 4px;
  font-size: 14px;
  color: #52525b;
}

.karte {
  margin-bottom: 24px;
  padding: 16px;
  border-radius: 12px;
  background-color: #ffffff;
}

.karte p {
  margin: 8px 0 0 0;
  color: #52525b;
}

/* Die zwei Zeilen, die in vielen Resets stehen.
   height: auto gehört zwingend dazu, sonst bleibt
   die Höhe aus dem height-Attribut stehen und das
   Bild wird gestaucht. */
.brav img {
  max-width: 100%;
  height: auto;
}

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

Ein Bild bringt seine natürliche Größe mit. Ist die größer als der Kasten, in dem es liegt, ragt es heraus, und zwar über die ganze Seite. Auf dem Handy erscheint dann ein waagerechter Scrollbalken, und der Rest der Seite ist plötzlich schmaler als das Fenster.

max-width: 100% deckelt es auf die Breite seines Kastens. height: auto gehört zwingend dazu. Sonst bleibt die Höhe aus dem height-Attribut stehen, die Breite schrumpft trotzdem, und das Bild wird gestaucht.

Im Beispiel steht beides nebeneinander, und im Inspector stehen die Zahlen dazu. Das obere Bild misst 600 × 338, seine Karte ist 348 breit, also ragt es um mehr als die halbe Kartenbreite hinaus. Das untere misst rund 316 × 178, und interessant ist die zweite Zahl. Im Markup steht height="338", im Streifen steht rund 178, denn height: auto rechnet die Höhe aus dem Seitenverhältnis neu. Ohne diese Zeile stünde dort weiter die 338, und das wäre das gestauchte Bild.

Ich schreibe diese zwei Zeilen in jedes Stylesheet als Erstes, viele Resets haben sie schon drin.

Das width- und height-Attribut im HTML lasse ich trotzdem stehen. Die beiden CSS-Zeilen überstimmen sie zwar. Vorher sagen die Attribute dem Browser aber schon das Seitenverhältnis, und damit reserviert er den Platz, bevor die Datei da ist.

Dass die Datei wirklich geholt wird, siehst du im Reiter „Netzwerk“. Dort stehen drei Zeilen. /index.html und /styles.css tragen den Vermerk „aus der Aufgabe“, weil die Seite hier aus deinen Dateien zusammengesetzt wird. Darunter steht /lernen/media/tipptrainer-600.jpg als img mit seiner Größe, ein paar Millisekunden Ladezeit und „geladen“. Bilder sind in diesem Kurs das Einzige, was wirklich über das Netz ankommt, und ab dieser Lektion lohnt sich der Blick dorthin. Du siehst Größe und Ladedauer, und ein Tippfehler im Pfad meldet sich als rote Zeile.

Der Spalt unter dem Bild

Der Spalt unter dem Bild
body {
  margin: 0;
  padding: 16px;
  font-family: system-ui, sans-serif;
  line-height: 1.5;
  background-color: #f4f4f5;
}

.titel {
  margin-bottom: 4px;
  font-size: 14px;
  color: #52525b;
}

/* Der rote Grund macht den Spalt sichtbar. Er kommt
   daher, dass ein img von Haus aus inline ist und
   deshalb auf der Textgrundlinie sitzt: darunter
   bleibt der Platz für Unterlängen frei. */
.rahmen {
  margin-bottom: 24px;
  background-color: #b91c1c;
}

.block img {
  display: block;
}

Unter einem Bild klebt ein paar Pixel Leerraum, den man nicht wegbekommt. Kein margin, kein padding, und trotzdem ist er da.

Ein img ist von Haus aus inline und sitzt deshalb auf der Textgrundlinie. Darunter bleibt Platz für Unterlängen frei, also für das Bein vom kleinen g. Das Bild hat keine, reserviert wird der Platz trotzdem.

display: block löst es in einer Zeile. Deshalb steht in vielen Resets img { display: block; max-width: 100%; height: auto; } als Dreierpack.

Aufpassen musst du nur, wenn ein Bild wirklich im Textfluss stehen soll, etwa ein Symbol mitten im Satz. Dann willst du inline behalten und setzt stattdessen vertical-align: middle.

object-fit

cover, contain und der Sprung beim Laden
*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 16px;
  font-family: system-ui, sans-serif;
  line-height: 1.5;
  background-color: #f4f4f5;
}

.titel {
  margin-bottom: 4px;
  font-size: 14px;
  color: #52525b;
}

.kasten {
  width: 260px;
  margin-bottom: 24px;
  background-color: #e4e4e7;
}

/* aspect-ratio reserviert den Platz, bevor das Bild
   da ist. Alle drei Kästen sind damit gleich hoch,
   und beim Nachladen springt nichts. */
.kasten img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
}

.cover {
  object-fit: cover;
}

.contain {
  object-fit: contain;
}

.fill {
  object-fit: fill;
}

Sobald ein Bild Breite und Höhe bekommt, stimmt das Seitenverhältnis meistens nicht mehr. Was dann passiert, bestimmt object-fit.

  • fill ist der Standard und verzerrt. Im Beispiel wird die Tastatur schmal und hoch, aus einem Kreis würde ein Ei.
  • cover füllt den Kasten vollständig und schneidet ab, was übersteht. Das ist fast immer die richtige Wahl für Vorschaubilder.
  • contain passt das ganze Bild hinein und lässt dafür Ränder frei. Richtig für Logos und Produktfotos, bei denen nichts fehlen darf.
  • scale-down nimmt contain, vergrößert aber nie über die Originalgröße hinaus.

Dazu gehört object-position. Es bestimmt, welcher Teil bei cover stehen bleibt. Der Standard ist die Mitte, und bei Porträts ist object-position: top oft besser, weil sonst Köpfe abgeschnitten werden.

aspect-ratio beendet den Sprung

Die fünfte Angabe ist die jüngste, etwa aspect-ratio: 16 / 9.

Sie legt das Seitenverhältnis fest, und der Browser reserviert damit die Höhe, bevor das Bild geladen ist. Fehlen beide, das Verhältnis im CSS und die Attribute im Markup, ist der Kasten anfangs null Pixel hoch. Dann kommt das Bild an, und alles darunter rutscht nach unten. Wer gerade liest, verliert dabei die Zeile.

Bei einem img mit width und height im Markup nimmt der Browser dir das schon ab. In seinem Standard-Stylesheet steht dafür eine eigene Regel, die aus den beiden Attributen ein aspect-ratio macht. Im CSS brauchst du die Angabe deshalb nur in drei Fällen, nämlich wenn die Attribute fehlen, wenn ein anderes Verhältnis gelten soll als das des Originals und wenn es gar kein Bild ist.

Vor aspect-ratio brauchte man dafür einen Trick. Einen leeren Kasten mit padding-top: 56.25% und darin ein absolut positioniertes Bild. Das ging, weil sich senkrechter Innenabstand auf die Breite bezieht (Lektion 5.1). Drei Regeln, ein Rechenweg im Kommentar und ein Element, das nur für das Layout existiert. Heute ersetzt das alles eine Zeile.

Im Beispiel siehst du das an drei gleich aussehenden Kästen. Alle drei Bilder messen 260 × 260, obwohl die Datei dahinter 600 mal 338 Pixel groß ist und keiner der drei object-fit-Werte an dieser Zahl etwas ändert. Das Quadrat kommt allein aus aspect-ratio: 1 / 1, und es steht schon da, bevor die Datei ankommt. Was object-fit daraus macht, siehst du im Bild, aber nicht im Streifen, denn dort stehen weder object-fit noch aspect-ratio. Im Streifen steht der Kasten, nicht der Inhalt, und hier geht es um den Kasten.

aspect-ratio funktioniert übrigens nicht nur für Bilder. Jeder Kasten, der ein festes Verhältnis halten soll, bekommt es genauso, und für eingebettete Videos ist es die Standardlösung.

In der Aufgabe machen zwei Bilder aus zwei gleichen Projektkarten zwei sehr verschiedene.

Zum Mitnehmen

Mit max-width: 100% und height: auto bleibt ein Bild in seinem Kasten, display: block nimmt den Spalt darunter weg. Passt das Verhältnis nicht, entscheidet object-fit, was vom Bild zu sehen ist, und aspect-ratio hält den Platz frei, bevor die Datei da ist.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

Was in dieser Lektion steckt

  • Artikel mit 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe im Editor, direkt im Browser geprüft

    Öffnet sich mit dem Basis Konto.