Responsive Images

Bilder und Grafiken, die sich an die zur Verfügung stehende Bildschirmbreite anpassen, nennt man im Webdesign “responsive Images”. Und dafür gibt es zwei Elemente, die man einsetzen kann, im HTML5-Standard, einmal <picture> und einmal das bekannte <img>-Tag mit dem “neuen” srcset Attribut. Beides werden wir uns jetzt einmal anschauen, besonders aber das <img>-Tag mit srcset.

Zuerst müssen wir uns jedoch ein bisschen mit der Theorie der responsive Images beschäftigen. Es gibt nämlich mehrere Möglichkeiten, wie wir das realisieren können. Wir werden uns am Ende für eine der Möglichkeiten entscheiden. 1In einem weiteren Artikel sehen wir wir uns an, wie wir das Ganze mit Hugo nutzen können. Wir werden uns nämlich einen Hugo Shortcode erstellen, so dass sich ganz unkompliziert responsive Grafiken im Text einsetzen lassen.

Die erste Möglichkeit, responsive Images umzusetzen, ist der ganz normale img Tag und zwar in Verbindung mit dem srcset Attribut. Man kann durch dieses srcset Attribut mehrere Bilder angeben, also Bildgrößen definieren und der Browser entscheidet dann selbst, welche Bildgröße er laden möchte. Abhängig von dem Gerät, auf dem man sich das gerade anschaut. Der Browser berücksichtigt dabei die Bildschirmgröße und die Auflösung des Displays (Pixeldichte).

“If you’re just changing resolutions, use srcset.”

Chris Coyier (Link)

Es gibt noch eine zweite Variante. Das ist die anfänglich eher propagierte Variante, nämlich das Picture-Element. Das picture-Element ermöglicht es, mehrere img-Tags zu definieren, die wiederum das srcset Attribut besitzen. Der Sinn und Zweck ist ursprünglich, dass man dem Browser ein Bildformat geben kann, das für ihn lesbar ist2Ganz ähnlich wie mit dem <video> Element. So könnte sich zum Beispiel der ältere Browser ein .jpg aussuchen. Ein moderner Browser aber könnte das neuere, von Google entwickelte, WebP-Bildformat verwenden.

“Don´t use <picture> (most of the time).”

Jason Grigsby (Link)

Ich kann Dir da einen etwas älteren Artikel vom September 2015 von Jake Archibald empfehlen. Da wird sehr schön auch visuell dargestellt, wie man responsive Images mit dem img-Tag und mit srcset umsetzen kann. In diesem Blogpost erwähnt er auch, das Picture-Element und er beschreibt hier die variierende Breite, Pixeldichte und das Thema “Art direction”.

Art direction mit <picture>

Am sinnvollsten ist die Verwendung des picture-Elementes aus meiner Sicht, wenn man definitiv verschiedene Bilder pro Bildschirmgröße anzeigen will. Das ist es auch, was man unter dem Begriff “Art direction” verstehen kann. Macht zum Beispiel die Darstellung einer Infografik im Breitformat auf kleinen Bildschirmen keinen Sinn, lädt man dank des picture-Elements auf kleineren Bildschirmen eine Infografik die für die Darstellung im Hochkant-Format optimiert wurde.

<img> mit srcset-Attribut

In der Realität soll meist das selbe Bild für verschiedene Bilschirmgrößen in unterschiedlichen Dateigrößen ausgeliefert werden.

Aus diesem Grund möchte ich mich jetzt auch auf genau diese Variante konzentrieren, nämlich den <img>-Tag zu verwenden und dem Tipp von Chris Coyier folgen: Wenn man nur die Auflösung wechseln will, dann einfach bei <img> + srcset bleiben, statt das <picture> Element zu verwenden. Das ist technisch einfacher umzusetzen und fordert dem Redakteur weniger Entscheidungen ab. Deswegen schauen wir uns jetzt an, wie wir das machen können.

Code im static site builder HUGO

Dann wollen wir mit einem kurzen Überblick über meinen Hugo-Projektverzeichnis starten. Ich habe nur einen Default Archetype. Ich habe einen Content erstellt, der heißt einfach nur “Bilder”. Dann habe ich einige Layouts angelegt, einfach damit wir irgendwas vernünftig ausgeben können. Es gibt eine CSS-Datei und es gibt im Ordner “static” einen Ordner “images” mit drei Bildern die wir verwenden wollen. Und ich habe dann im Unterverzeichnis “images/article” schon mal Bilder angelegt, jeweils in drei verschiedenen Größen die wir je nach Breakpoint verwenden wollen. Es ist immer exakt das gleiche Bild, aber in drei verschiedenen Varianten skaliert. Das  orientiert sich so ungefähr an der Größe, wie wir sie in unserem css-Layout brauchen. In “/layouts/_default/” gibt es ein Listen Template, das ich auf der Startseite verwende, um alle Inhalte aufzulisten. Und zwar mit einem Bild, das ich aus den Parametern des Contents nehme und zwar .Params.images.teaser.

Ich habe im ‘front matter’3Als ‘front matter’ wird der Kopf-Bereich einer Datei bezeichnen, der Meta-Informationen beinhaltet. meines Contents (/content/bilder.md) die Eigenschaft “images” und darunter den Begriff “teaser”. Dort habe ich einfach nur den Bildnamen ohne Pfad und ohne Dateierweiterung rein geschrieben. Das ist für den Redakteur vielleicht am einfachsten. Er kann hier einfach im Ordner “static/images/” schauen, Bilder dort rein laden – also nicht in den Artikel-Ordner, sondern einfach dort in den Ordner “/static/images/”.

Das Bild in dem Fall heißt “lissi”. Ohne die Extension .jpg. Die lassen wir weg. Das hat einen bestimmten Grund. Denn der Datei-Typ könnte sich ja ändern, wenn wir automatisiert umwandeln – vielleicht beim Deployment oder in der Vorbereitung zum Deployment. Also der Redakteur lädt zum Beispiel eine Ping-Datei hoch (also .png). Wir wollen aber unsere Bilder immer als jpg’s haben und deswegen würden wir vor dem Deployment die Bilder von png nach jpg umwandeln. Dann ist auch zu empfehlen, dass die Bilder optimiert/komprimiert werden und so weiter, um Speicherplatz zu sparen. Die Bilder würden dann automatisiert im Ordner “/static/images/article/” landen, umbenannt, alles als .jpg mit einem Dateinamen-Suffix für Large, Medium und Small. Aber unser Redakteur gibt quasi nur vorne den ersten Teil des Dateinamens an und das Template selbst würde im Prinzip dann wissen müssen, dass wir diese drei Bildgrößen haben. Das nur so zur Vorbereitung, das wir das schon mal im Hinterkopf haben. Wir schauen uns später an, wie wir das realisieren.

Template für die Artikel-Liste

Gehen wir noch mal in die Listenansicht ‘/layouts/_default/list.html’ zurück. Dort haben wir jetzt einfach unser <figure>, also ein HTML-Tag, das anzeigt: “Der Inhalt der jetzt kommt, ist eine bildhafte Darstellung.” In diesem Fall benutze ich den <img>-Tag. Ich gebe hier den Pfad zu dem ‘static/images/’ Ordner an und dann lese ich Params.image.teaser aus und hänge dann den Dateityp ‘.jpg’ explizit dran. Das kann ich machen, weil ich eben weiß, meine Bilder werden letztendlich immer .jpg sein und den Pfad weiß ich auch, weil das beim (noch nicht vorhandenen) Deployment entsprechend erledigt wird.

Eine erste Änderung ist also, dass ich jetzt für diese Artikel-Images weiß, ich will später immer ein ‘images/article’ Verzeichnis haben, in dem die verschiedenen Größen des Bildes liegen. Also füge ich hier ‘article’ ein, wo wir unser Bild finden, zum Beispiel ‘-small’. Das Ganze würde auf der Startseite, in der Teaser-Ansicht so aussehen:

FIXME: Screenshot einfügen

Das Bildes ist recht unscharf, weil ich die kleinste Variante gewählt habe. Wenn wir uns das einmal anschauen, dann sehen wir hier das Bild wird dargestellt in 315×210 Pixeln. Es ist aber tatsächlich nur 129×86 Pixel groß. Es wird also größer gezogen und ist deswegen so verschwommen. Warum wird es eigentlich größer gezogen? Das liegt am CSS. Grundprinzip bei responsive images ist eigentlich, dass man sagt: “Okay, mein Bild soll 100% der Breite des Eltern-Containers einnehmen.” Also innerhalb des figure Tags, soll das Bild 100% der Breite einnehmen, den <figure> zur Verfügung stellt. Die Höhe setzen wir auf ‘auto’. Damit ist gewährleistet, dass das Seitenverhältnis gewahrt bleibt.

CSS Regeln für responsive Images

Im CSS habe ich hier nach dem Prinzip des “Mobile first” gearbeitet. Das heißt, alles Markup, dass ich schreibe, ist erstmal für das Smartphone im Hochkant- oder Portrait-Format gedacht. Der Browser geht die CSS-Datei von oben nach unten durch. Über sogenannte Media-Queries überschreibt man dann für verschiedene Bildschirm- oder Displaybreiten einzelne CSS-Anweisungen. Beim “Mobile first” ist die Empfehlung, in den Media-Queries mit “min-width” zu arbeiten (also von klein beginnend, hin zu größeren Displays).

Der das allgemein gültige CSS für <figure> ist:

217072

Das wird dann ab einer Bildschirmbreite von 321px überschrieben und erhält dann eine Breite von 50%. Außerdem wird nach links gefloatet. Das heißt, unser Fließtext kann rechts am Bild vorbei fließen und ein ‘padding’ wird eingestellt.

FIXME: Code

Ab einer Größe von 1025px, soll dann die <figure> nur noch 33,333 % der Displaybreite einnehmen.

FIXME: Code

An dem 1270px Breakpoint ändert sich dann nichts mehr am <figure> Element.

FIXME: Video

Demonstriert sieht das wie folgt aus (siehe voriges Video): Gehen wir hier mal auf responsive und nehmen uns hier den Breakpoint von 320px Breite. Und du siehst, sobald ich hier bei 321px bin, nimmt mein Bild bzw. die <figure> die es umschließt, nur noch 50% der Bildschirmbreite ein.

Kleiner: 100%, ab 321px 50%, ab 1025px nur noch ein Drittel des zur Verfügung stehenden Platzes. Da wir das ‘Small’ Bild genommen haben, ist es halt zu klein, um hier noch bei 1025px korrekt angezeigt zu werden.

Wir kommen mit drei verschiedenen Bildschirmgrößen aus. Hier würde ich ‘Small’ benutzen. Da würde ich ‘Medium’ benutzen wollen und bei 1024px da wollte ich ‘Large’ benutzen, weil das die größte Ausführung dieses Bildes ist mit einer Größe von 50% bei 1024px. Sobald wir ein bisschen größer gehen, sind wir dann hier bei 33%, einem Drittel der Bildschirmgröße.

So, wie setzen wir das jetzt um? Das ist eigentlich recht einfach. Ich habe das schonmal rauskopiert: Das ist unser tatsächlich responsives Image.

Hier haben wir unseren Pfad des Bildes und das ergänze ich jetzt, sozusagen noch durch srcset.

FIXME: Code

Ich habe im src Attribut das Medium Bild als Fallback, falls ein Browser srcset nicht erkennt. Und ich habe dort Breiten gewählt, ab denen ich gerne die jeweilige Bildgröße laden möchte. Also bei einer 320er Displaybreite hätte ich gerne das Medium-Bild geladen, bei 321er hätte ich gern das Small-Bild. Bei 600er wieder das Medium. Bei 1024er das Large und bei 1025er wieder das Medium.

Also das ist jetzt nur ein Pixel Unterschied, genauso wie beim Medium und Small. Und es wechselt sich immer ab: Medium, Small, Medium, Large und Medium.

Um das zu erreichen, habe ich jetzt ein bisschen ausprobiert und geschaut wie bekomme ich das jetzt am optimalsten hin. Obwohl ich ja eigentlich nur zwei Breakpoints habe und den Fallback muss es geben, wo keine Media-Query drin steht. Und dann haben wir wieder unser Mobilgerät und dort haben wir die 100% breite Grafik.

FIXME: Code

Jetzt haben wir also 50%, 33% und 100% und dem Browser Tipps gegeben, wie unser Bild dargestellt werden soll. Ich hab hier mal bei ‘width’ und ‘height’ ein Mittelmaß ausgewählt. So hat der Browser schon mal eine ungefähre Größenvorstellung, bevor das Bild geladen wurde. So weiß er beim Aufbau des DOM – also beim Rendern des HTML – ungefähr, wie groß das Bild-Element später sein wird. Ich wähle hier einen Mittelwert. Man könnte auch die Medium Maße dafür verwenden. Das hat dadurch, dass wir ja mit CSS die Bildbreite auf 100% des Elternelements formatiert haben, keine Auswirkung auf die tatsächliche Bilddarstellung. Aber es gibt dem Browser wiederum einen Tipp, mit welcher Größe er starten soll und danach passt er das dann auf die tatsächliche Bildgröße an. Dadurch minimiert sich das “Springen”, also dass der Text zum Beispiel am Anfang beim Laden der Seite ganz links anschlägt und wenn das Bild dann geladen wurde nach rechts springt.

Das war schon ein Haufen Information. Ich hoffe noch nicht zu viel. Responsive Images an sich ist ein Thema das nicht viel mit Hugo an sich zu tun hat. Und es ist auch recht komplex. Mit Hugo hat es aber doch insofern so zu tun, dass der Redakteur ganz einfach im Content das Teaser-Image angeben soll (den Dateinamen ohne Datei-Erweiterung und ohne Pfad) und dann wird beim Rendern des Site der Code zur Darstellung des Bildes ausgegeben.

Jetzt schauen wir uns an, wie sich das verhält. Und zwar nehme ich dafür in den Browser das Networks-Tab und gehe auf Mobile, also 320 Pixel Breite.

Was wir wissen wollen ist, ob die Bildgröße geladen wird die wir uns wünschen. Und das kannst Du sehen, wenn Du unten das Network-Panel aktivierst, das habe ich mal nur auf Bilder gefiltert. So dass er uns hier einfach nur anzeigt, welches Bild denn jetzt grade geladen wurde.

Wir haben hier Medium. Wenn du dort drauf klickst siehst du, dass Images ist in echt 288×191 Pixel breit.

Und wenn ich den Image Container auswähle, hat der in dieser Breite 288×191 Pixel.

Das hab ich mir angeschaut, nachdem mein CSS geschrieben war und habe dann die Bilder auf die entsprechende optimal Größe oder besser gesagt ein Mittelwert, den ich für sinnvoll halte,   runtergerechnet. Und daraus resultieren diese drei Größen Small, Medium und Lagre. Für das Medium habe ich halt jetzt die 288×191 Pixel gewählt. Wenn wir jetzt auf den 321er Breakpoint wechseln, dann ist unser Bild natürlich nur noch halb so groß. Weil es nur noch 50% des Platzes oder sogar noch weniger, weil wir noch ein Padding haben, in Anspruch nimmt und es hat sich hier nichts weiter geändert.

Der Browser hat also kein anderes Bild geladen. Eigentlich wollen wir, dass er Small lädt aber da er ja schon ein grösseres Bild hat und das auch weiß, geht er jetzt nicht hin und lädt noch Small. Weil er hat ein größeres Bild. Die Qualität wird also durch das kleiner darstellen, nicht schlechter. Insofern bleibt der Browser dabei und lässt uns das Medium Bild. Wenn ich jetzt aber die Seite komplett neu lade, dann siehst du wird das Small Bild geladen, weil das optimal dort reinpassen würde.

An die tatsächliche Bilddarstellung heran reicht 158,5×85,66 Pixel. Mal schauen wie groß das Bild ist. Es ist 129×86 Pixel groß. Gucken wir noch einmal hier ins responsive Image, also in die sourceset rein. Da habe ich ein bisschen rum getrickst und habe hier Medium für 320 Pixel genommen und Small für 321 Pixel.

Und dadurch kommt es eben, dass genau dieses eine Pixel den Unterschied macht. Ansonsten würde der Browser wahrscheinlich dazu tendieren, auch hier das Medium Bild zu laden, weil er das sowieso bald wieder braucht.

Wie Du siehst, sobald ich den sichtbaren Bereich verbreitere, lädt er dann die Medium Größe. Er fällt auch nicht mehr zurück. Er lädt jetzt nicht noch mal ein kleineres Bild, sondern er lädt immer nur das nächst Größere.

Und gehe ich mal hier hin und lade komplett neu, dann lädt er das Bild Large.

Das macht er nur beim Neuladen seltsamerweise. Wenn ich bei dem 786er lade, dann nimmt er auch schon Large. Gehe ich mal auf 425er, dann nimmt er wieder Medium. Und irgendwann entscheidet er vielleicht doch dann noch mal das Large Bild zu laden. Tut er aber nicht, weil er immer noch der Meinung ist, dass das Medium Bild hier reicht.

Das 288 x 191 Pixel dargestellt wird aber mit 471px Breite. Also meiner Meinung nach sollte der Browser hier ruhig schon das Large Bild laden. Aber beim Image-Tag mit srcset hat er ziemlich freien Gestaltungspielraum und entscheidet sich halt beim vergrößern des Browserfenster, keine größere Variante zu laden.

Wie gesagt, beim Reload ist das anders. Da sagt er: “Okay, hier nehme ich Large”.

Im nächsten Artikel werden wir uns mal anschauen, wie wir diese responsive Images auch für Redakteure einfach in den Content einbaubar machen. So dass also innerhalb des Fließtextes Bilder einsetzbar sind die sich responsiv verhalten, ohne dass der Redakteur mit dieser ganzen Komplexität in Berührung kommt.