Responsive Images in TYPO3 v10

In diesem Artikel möchte ich auf die Integration von Responsive Images in TYPO3 v10 eingehen. Die hier genannten Beispiele können auch zum Großteil auf ältere TYPO3-Versionen angewendet werden (v9 und v8). Hierzu ist es empfehlenswert, sich die jeweilige Dokumentation zurate zu ziehen, wie z.B. die TYPO3 Core Dokumentation, die übersichtlich aufzeigt, in welcher TYPO3 Version welches Feature dazugekommen ist und wie es verwendet wird, zum Teil sogar mit Code-Beispielen.

#ninja { color: black; visibility: hidden; }

Martin Alker
kennt TYPO3 wie seine Westentasche und entwickelt sich stets im Webumfeld weiter
Lesedauer: ca. 6 Minuten

Standard-Image-Rendering

Bilder in TYPO3 v10 werden gewöhnlich über einen Fluid-ViewHelper gerendert. Hierbei kommt der media-ViewHelper zum Einsatz, der je nach Ressource (Bild oder Video) das entsprechende HTML-Markup rendert. Das für uns relevante Fluid-Markup befindet sich in der Datei typo3/sysext/fluid_styled_content/Resources/Private/Partials/Media/Rendering/Image.html:

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
    <f:media
        class="image-embed-item"
        file="{file}"
        width="{dimensions.width}"
        height="{dimensions.height}"
        alt="{file.alternative}"
        title="{file.title}"
        loading="{settings.media.lazyLoading}"
    />
</html>

Das ganze Markup kommt sehr leichtgewichtig daher - was mich positiv überrascht hat. Die Dokumentation zum media-ViewHelper https://docs.typo3.org/other/typo3/view-helper-reference/master/en-us/typo3/fluid/latest/Media.html zeigt neben den hier dargestellten Argumenten noch weitere, wie z.B. das neue Argument loading.
Das Argument loading ist ab v10.3 für den media-ViewHelper verfügbar und wird standardmäßig mit dem Wert lazyH verwendet. Über die Konstante, die hierfür von fluid_styled_content eingeführt wurde styles.content.image.lazyLoading kann das loading-Argument angepasst werden (lazy, eager oder auto). Zum loading-Attribut gibt es hier eine Erklärung: https://web.dev/native-lazy-loading/#the-loading-attribute

Und so sieht bei der Verwendung des oben gezeigten Codes die HTML-Ausgabe aus:

<div id="c1" class="frame frame-default frame-type-image frame-layout-0">
    <div class="ce-image ce-center ce-above">
        <div class="ce-gallery" data-ce-columns="1" data-ce-images="1">
            <div class="ce-outer">
                <div class="ce-inner">
                    <div class="ce-row">
                        <div class="ce-column">
                            <figure class="image">
                                <img class="image-embed-item" title="Demo Bild Title Text" alt="Demo Bild Alt Text" src="/fileadmin/_processed_/6/e/csm_image_01_a55d08e819.jpg" loading="lazy" width="600" height="337">
                            </figure>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

Wie zu sehen wurde auch das bereits erwähnte loading-Attribut mit dem Wert lazy gerendert. Das HTML-Konstrukt um den img Tag entstammt weiteren Fluid-Templates, die sich zum Großteil hier befinden: typo3/sysext/fluid_styled_content/Resources/Private/Partials/Media/. Schauen wir uns den media-ViewHelper mal genauer an.

media-ViewHelper anpassen

Dem media-ViewHelper fehlen Argumente wie srcset oder sizes. Das Attribut srcset ist aber notwendig damit wir das src-Attribut eines Bildes durch ein Set von Bildquellen überschreiben können.
Um nun beide Argumente integrieren zu können, benötigen wir ein eigenes Fluid-Partial, dass das Original überschreibt (...Partial/Media/Rendering/Image.html). Hierfür habe ich mir eine eigene kleine Extension (responsive_images_demo) über https://www.sitepackagebuilder.com/ erstellt - basierend auf TYPO3 v10 und Fluid Styled Content.

Über folgende Konstanten könnt Ihr dann Angaben machen, wo sich z.B. Eure eigenen Partials oder Templates befinden:

styles {
    templates {
        layoutRootPath = EXT:responsive_images_demo/Resources/Private/Layouts/ContentElements/
        partialRootPath = EXT:responsive_images_demo/Resources/Private/Partials/ContentElements/
        templateRootPath = EXT:responsive_images_demo/Resources/Private/Templates/ContentElements/
    }
}

Mein eigenes Image-Partial befindet sich unter typo3conf/ext/responsive_images_demo/Resources/Private/Partials/ContentElements/Media/Rendering/Image.html und überschreibt automatisch das Original (typo3/.../Partial/Media/Rendering/Image.html).

srcset / sizes integrieren

Der media-ViewHelper wird mit Hilfe des ViewHelpers uri.image und desArguments additionalAttributes um die Attribute srcset und sizes ergänzt:

<f:media
    class="image-embed-item"
    file="{file}"
    width="{dimensions.width}"
    height="{dimensions.height}"
    alt="{file.alternative}"
    title="{file.title}"
    loading="{settings.media.lazyLoading}"
    additionalAttributes="{srcset: '{f:uri.image(image: file, maxWidth: 768)} 768w,
                                    {f:uri.image(image: file, maxWidth: 990)} 990w,
                                    {f:uri.image(image: file, maxWidth: 1200)} 1200w,
                                    {f:uri.image(image: file, maxWidth: 1440)} 1440w,
                                    {f:uri.image(image: file, maxWidth: 1900)} 1900w',
                           sizes: '(min-width: 1200px) 50vw, 100vw'}"
/>

Die gezeigten srcset- und sizes-Angaben sind hier beispielhaft. Für welche Gerätecharakteristiken Ihr die entsprechenden Auflösungen bereitstellen wollt, ist natürlich Euch überlassen.

Und so sieht nun die HTML-Ausgabe im Frontend aus:

<img
    srcset="/fileadmin/_processed_/6/e/csm_image_01_8159d1e3b0.jpg 768w,
            /fileadmin/_processed_/6/e/csm_image_01_19d1039365.jpg 990w,
            /fileadmin/_processed_/6/e/csm_image_01_9cee8957cf.jpg 1200w,
            /fileadmin/_processed_/6/e/csm_image_01_922756210c.jpg 1440w,
            /fileadmin/_processed_/6/e/csm_image_01_0e137c815f.jpg 1900w"
    sizes="(min-width: 1200px) 50vw, 100vw"
    class="image-embed-item"
    title="Demo Bild Title Text"
    alt="Demo Bild Alt Text"
    src="/fileadmin/_processed_/6/e/csm_image_01_a55d08e819.jpg"
    loading="lazy"
    width="600"
    height="337"
>

Die Attribute width und height können über folgende Konstanten angepasst werden:

  • styles.content.textmedia.maxW
  • styles.content.textmedia.maxWInText - Wie maxW aber hierbei handelt es sich um die maximale Breite, wenn Text um einen Block von Medien-Elementen umgebrochen wird. Standardwert ist 50 % der normalen maximalen Medien-Elementbreite → maxW)

Der Standardwert in TYPO3 v10.4 ist für maxW → 600 und für maxWInText → 300.

Alternative Fluid Variante

img Tag

Eine alternative Variante um Responsive Images anstelle unter zu Hilfenahme von f:media oder f:image zu rendern, kann die Verwendung eines ganz gewöhnlichen img Tags in Kombination mit dem f:uri.image ViewHelper sein:

<img
    class="image-embed-item lazy"
    data-src="{f:uri.image(image: file, treatIdAsReference: 1, maxWidth: 1168)}"
    data-srcset="{f:uri.image(image: file, treatIdAsReference: 1, maxWidth: 768)} 768w,
                 {f:uri.image(image: file, treatIdAsReference: 1, maxWidth: 990)} 990w,
                 {f:uri.image(image: file, treatIdAsReference: 1, maxWidth: 1200)} 1200w,
                 {f:uri.image(image: file, treatIdAsReference: 1, maxWidth: 1440)} 1440w,
                 {f:uri.image(image: file, treatIdAsReference: 1, maxWidth: 1900)} 1900w"
    data-sizes="(min-width: 1200px) 1168px, 100vw"
    alt="{file.alternative}"
    title="{file.title}"
    width="{dimensions.width}"
    height="{dimensions.height}"
/>

Im Beispiel erhalten die Attribute src, srcset und sizes das data-Präfix, das oft für lazy loading Skripte, wie https://github.com/verlok/lazyload, benötigt wird.

picture Tag

Das picture Tag kann als weitere Variante, wie auch das gezeigte img Tag Beispiel mit dem f:uri.image ViewHelper ausgestattet werden:

<picture>
    <source
        media="(min-width: 990px)"
        srcset="{f:uri.image(image: file, maxWidth: 1200)}"
    />
    <source srcset="{f:uri.image(image: file, maxWidth: 768)}" />
    <img src="{f:uri.image(image: file, maxWidth: 768)}" alt="{file.alternative}" />
</picture>

TypoScript-Variante

In der Vergangenheit wurden Responsive Images sehr häufig durch TypoScript gerendert, aber später durch die steigende Popularität von Fluid wurde dies mehr und mehr verdrängt. Nichtsdestotrotz hat das Rendern der Responsive Images durch TypoScript immer noch seine Daseinsberechtigung, vor allem wenn die Site zum Großteil (noch) durch TypoScript gerendert wird. TypoScript ist ein mächtiges Werkzeug und bietet große Flexibilität beim Rendern. Manchmal lassen sich mit wenigen Zeilen TypoScript Elemente einfacher umsetzen, als mit verschachtelten Fluid-Konstrukten. Allein der Blick in die Dokumentation zum TypoScript IMAGE Objekt zeigt das Potenzial: https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/ContentObjects/Image/Index.html
Die nächsten TypoScript-Beispiele zeigen nur die Bereiche, die für Responsive Images relevant sind. Die genannte IMAGE-Dokumentation beinhaltet vollständige Beispiele, sowie deren HTML-Ausgabe.

Das Beispiel zeigt ein typisches IMAGE-Objekt, das durch diverse Properties erweitert wurde, sodass es z.B. das srcset-Attribut (###SRCSETCANDIDATE###) erhält:

10 = IMAGE
10 {
  file = fileadmin/example.jpg
  file.width = 3141
 
  layoutKey = default
  layout {
    [...]
 
    srcset {
      element = <img src="###SRC###" srcset="###SOURCECOLLECTION###" ###PARAMS### ###ALTPARAMS### ###SELFCLOSINGTAGSLASH###>
      source = |*|###SRC### ###SRCSETCANDIDATE###,|*|###SRC### ###SRCSETCANDIDATE###
    }
 
    [...]
  }
 
   {
    small {
      width = 800
 
      srcsetCandidate = 800w
      mediaQuery = (min-device-width: 800px)
      dataKey = small
    }
 
    [...]
  }
}
 
20 < 10
20.layoutKey = srcset
 
[...]

Das IMAGE Objekt kann z.B. als ein wiederverwendbares TypoScript-Objekt lib.responsiveImage definiert werden:

lib.responsiveImage {
    default = IMAGE
    default {
        file {
            import.current = 1
            treatIdAsReference = 1
        }
 
        altText.field = alternative
        titleText.field = title
        params = class="image-embed-item"
 
        layoutKey = srcset
        layout {
            [...]
 
            srcset {
                element = <img src="###SRC###" srcset="###SOURCECOLLECTION###" sizes="100vw" ###PARAMS### ###ALTPARAMS### ###SELFCLOSINGTAGSLASH###>
                source = |*|###SRC### ###SRCSETCANDIDATE###,|*|###SRC### ###SRCSETCANDIDATE###
            }
        }
 
        sourceCollection {
            mobile {
                maxW = 314
                srcsetCandidate = 314w
                dataKey = mobile
            }
            [...]
        }
    }
}
 
[...]
 
renderObj < lib.responsiveImage.default
renderObj {
    [...]
  
    sourceCollection {
        tablet {
            width = 768
             
            srcsetCandidate = 768w
            mediaQuery = (max-device-width: 768px)
            dataKey = tablet
        }
 
        [...]
    }
}

Oder in einem Fluid Template:

<f:cObject
    typoscriptObjectPath="lib.responsiveImage.default"
    data="{file.uid}"
/>

Cropping

Seit einigen TYPO3-Versionen (v7.6) existiert das Crop-Feature, das Redakteuren ein Werkzeug an die Hand gibt, um einen Ausschnitt eines Bildes festzulegen.
Die beiden nächsten Code-Abschnitte sollen einen möglichen Weg aufzeigen, zum Einen die Crop-Varianten anzupassen, die im Backend für die Bild-Manipulation zur Verfügung stehen, und zum Anderen wie auf die Ausschnitte zugegriffen werden kann.

TCEFORM

Die crop Variante default, die TYPO3 bereits von Haus aus mitliefert, sowie zusätzliche Varianten können über TCEFORM angepasst werden:

TCEFORM {
    sys_file_reference.crop.config.cropVariants {
        default {
            title = Alle (Standard)
            selectedRatio = NaN
            allowedAspectRatios {
                NaN {
                    title = Frei
                    value = 0.0
                }
 
                [...]
 
                4:3 {
                    title = 4:3
                    value = 1.333333
                }
            }
        }
 
        card {
            title = Card
            selectedRatio = 16:9
            allowedAspectRatios {
                16:9 {
                    title = 16:9
                    value = 1.777777778
                }
            }
        }
    }
}

Über die Eigenschaft allowedAspectRatios können verschiedene Seitenverhältnisse angegeben werden. So erhält der Redakteur für ein Bild unterschiedliche vordefinierte Seitenverhältnisse, um z.B. schneller für gewisse Layoutvorgaben das passende Seitenverhältnis auszuwählen.
Im obigen Beispiel wurde neben der default Variante, die hier überschrieben wird, die neue Variante card hinzugefügt. Wird nur eine neue Variante, wie im Beispiel die card Variante definiert, dann wird dem Nutzer im Backend auch keine default Variante zur Verfügung gestellt.

Fluid

Im Fluid-Template wird über das Argument cropVariant des f:uri.image ViewHelpers auf die entsprechende Variante zugegriffen. In diesem Beispiel auf card:

<img
    class="lazy"
    data-src="{f:uri.image(src: file, treatIdAsReference: 1, maxWidth: 768, cropVariant: 'card')}"
    data-srcset="{f:uri.image(src: file, treatIdAsReference: 1, maxWidth: 768, cropVariant: 'card')} 768w,
                 {f:uri.image(src: file, treatIdAsReference: 1, maxWidth: 1900, cropVariant: 'card')} 1200"
    data-sizes="..."
    alt="..."
    [...]
/>

Als Ergänzung möchte ich noch den Weg über TCA nennen: https://docs.typo3.org/m/typo3/reference-tca/master/en-us/ColumnsConfig/Type/ImageManipulation.html
Ein Blick in die TCA-Dokumentation lohnt sich, weil u.a. auch die Verwendung der coverArea  gezeigt wird, um z.B. Bild-Bereiche für den Redakteur aufzuzeigen, die im Frontend einen Textbereich enthalten können, der das Bild später überdeckt oder die focusArea Eigenschaft, die Koordinaten speichert (focal point), die z.B. für JavaScript-Plugins wie https://github.com/jonom/jquery-focuspoint verwendet werden können.

Extensions

Wer sich Tipparbeit beim Integrieren der Responsive Images in Fluid oder TypoScript sparen möchte oder vielleicht mehr Funktionalität benötigt, als die Fluid-ViewHelper wie f:media oder f:image anbieten, kann sich ja immer nach TYPO3-Extensions umsehen.
In der TYPO3-Community werden für das Rendern von Responsive Images häufig die beiden nachfolgenden TYPO3-Extensions verwendet:

vhs media ViewHelper

Die vhs TYPO3-Extension bietet eine Vielzahl nützlicher ViewHelper an, darunter befindet sich der v:media.image ViewHelper, der Ähnlichkeiten mit den bekannten ViewHelpern f:media oder f:image aufweist. Ein erster Blick auf den vhs-ViewHelper zeigt schon die Einfachheit, mit der sich z. B. die unterschiedlichen srcset-Angaben definieren lassen:

<v:media.image
    src="path/to/media.jpg"
    width="123"
    height="123"
    [...]
    treatIdAsReference="1"
    srcset="314, 768, 990, 1200"
    srcsetDefault="768"
/>

Die v:media.image ViewHelper Referenz führt noch einige weitere interessante Argumente auf: https://fluidtypo3.org/viewhelpers/vhs/master/Media/ImageViewHelper.html

sms_responsive_images

Neben vhs ist die TYPO3-Extension sms_responsive_images eine weitere Empfehlung. Auch hier ist die Fluid-Konfiguration relativ simpel und hat so einige Features parat:

<sms:image image="{image}" srcset="400, 600, 800, 1000" />
<sms:image image="{image}" srcset="1x, 2x" />
 
<sms:image
    image="{image}"
    sizes="(min-width: 1200px) 600px, (min-width: 900px) 800px, 100vw"
/>
 
<sms:image
    image="{image}"
    breakpoints="{
        0: {'cropVariant': 'desktop', 'media': '(min-width: 1000px)', 'srcset': '1000, 1200, 1400, 1600'},
        1: {'cropVariant': 'mobile', 'srcset': '400, 600, 800, 1000, 1200, 1400, 1600'}
    }"
/>
 
<sms:image image="{image}" srcset="400, 600" lazyload="true" />

Die Extension rendert ein <img> oder <picture> Tag, je nachdem welche Argumente verwendet werden. So z.B. wird ein picture-Tag bei der Verwendung des breakpoints Arguments gerendert.
In der Extension-Dokumentation findet Ihr neben den hier gezeigten Beispielen noch weitere: https://extensions.typo3.org/extension/sms_responsive_images

Bonus

fileExtension / WebP

Ab TYPO3 v10.3 ist es nun auch möglich bei den ViewHelpern <f:image>, <f:media> und <f:uri.image> für das Argument fileExtension die Option webp anzugeben:

<picture>
    <source srcset="{f:uri.image(image: file, treatIdAsReference: true, fileExtension: 'webp')}" type="image/webp"/>
    <source srcset="{f:uri.image(image: file, treatIdAsReference: true, fileExtension: 'jpg')}" type="image/jpeg"/>
    <f:image alt="{file.alternative}" image="{file}" treatIdAsReference="true"/>
</picture>

Hierbei sollte vorher überprüft werden, ob die eingesetzte ImageMagick-Version die WebP-Konvertierung unterstützt.
Weiterführende Links zum Einsatz von WebP in Verbindung mit TYPO3:

Teilen:

Weitere Beiträge

while(problem){ this->makeSolution()}
Ursula Klinger, Entwicklung / Scrum Masterin bei punkt.de
Arbeiten bei punkt.de