Ausgangssituation

Villeroy & Boch ist ein Premium Hersteller und Händler hochwertiger Porzellanwaren. Verkauft wird in mehrsprachigem Multistore-Setup sowohl an Händler als auch an Endkunden. Das vorherige Template wurde vor ca. 2 Jahren erstellt. Zum damaligen Zeitpunkt wurden Responsiveness und die Darstellung auf mobilen Geräten noch nicht berücksichtigt.

Zielsetzung / Anforderung

Der Bedeutung des wachsenden Anteils mobiler Endgeräte soll im Zuge eines Design-Relaunchs Rechnung getragen werden. Neben der Implementierung neuer Shop-Features steht ein aufgefrischtes, auch für die speziellen Anforderungen mobiler Endgeräte optimiertes Frontend im Fokus.


Projekt Setup

Technologien

Automatisierter Workflow

Bei Projekten dieser Größenordnung setzen wir zunehmend auf ein so genanntes Boilerplate zur standardisierten Frontendentwicklung. Das Rückgrat stellt dabei in unserem Fall Grunt dar. Grunt ist ein JavaScript-basierter Task Runner auf Grundlage des populären Node.js Frameworks.

Eine große Community stellt zahlreiche Erweiterungsmodule bereit, sodass sich Grunt mit wenig Aufwand erweitern und der Workflow an die eigenen Bedürfnisse anpassen lässt.

Grunt sorgt dafür, dass mittels Bower verwaltete Abhängigkeiten berücksichtigt werden, kompiliert LESS zu (minimiertem) CSS, und fügt JavaScripts zusammen („merging“) und minimiert auch diese.

Weiterhin generieren wir z.B. ausgehend von einzelnen Scalable Vector Graphics (SVG) Dateien ein Sprite und führen bei Bedarf noch Bildoptimierungen durch.

Das alles geschieht entweder vom Entwickler gesteuert über die Kommandozeile oder bequem im Hintergrund mittels eines File Watchers, der automatisch vordefinierte Tasks auslöst, sobald entsprechende Dateien geändert werden.

Verzeichnisstruktur

Grundsätzlich legen wir die Theme-Verzeichnisse nach Magento Best Practices an. Hinzu kommt, dass wir in unserem Repository ausschließlich unseren Quelltext haben möchten. Abhängigkeiten wie Frameworks etc. (z.B. Bootstrap) werden nicht versioniert. Diese laufen über Bower in ein eigenes „Vendor“-Verzeichnis. Nach der Kompilierung sind diese Sourcen auch nicht mehr nötig; sie werden nicht auf den Live-Server geladen.

Innerhalb des skin/ Verzeichnisses gibt es eine zusätzliche Distributions-Ebene, in die alle kompilierten Stile und Scripte eingefügt. Diese werden versioniert und von Magento zur Darstellung eingebunden.


Modulare Stylesheets

Responsive Designs mit einer Vielzahl von Breakpoints bringen schnell eine beträchtliche Menge an CSS-Code mit sich. Durch die Verwendung eines Präprozessors können die Dateien im Repository beliebig organisiert werden. Die genaue Struktur sei jedem Entwickler(-team) überlassen; für uns hat es sich jedoch als sinnvoll und praktisch erwiesen, die Stile in zunächst in drei Kategorien einzuteilen:

  • Generische Stile
  • Magento Template
  • Stile mit Bezug auf Externe Module, z.B. von Drittanbietern

Unter den generischen Stilen wird zusammengefasst, was globale Verwendung findet. Neben dem zugrunde liegenden Bootstrap Framework (welches hier genau genommen lediglich aus dem „Vendor“-Verzeichnis referenziert wird) gehören dazu zum Beispiel Stile für Links, Icons, Typographie, usw..

Den Löwenanteil machen allerdings die Stile im Bereich des Magento Templates aus. Ich persönlich bin kein Freund von Spaghetti-Code und versuche nach Möglichkeit, die Dateien so schlank wie möglich zu halten. Es hat sich als sinnvoll erwiesen, auch im Bereich der Stile die Struktur der Magento eigenen Module und Blöcke aufzugreifen. So ergibt sich folgende, beispielhafte, Verzeichnisstruktur:

Im modules/ Verzeichnis werden die Stile von weiteren (externen) Abhängigkeiten abgelegt, z.B. Slideshow-Plugins etc.

Gesteuert wird die Reihenfolge der Elemente im kompilierten CSS über die zentrale styles.less.

Die Struktur ist recht selbsterklärend und beliebig skalierbar. Es erfordert allerdings anfangs ein wenig Überwindung und Vorausdenken, wenn man seit den Anfangstagen von Magento gewohnt ist, mit wenigen, großen Dateien zu arbeiten.


LESS ist nicht (automatisch) weniger

Yeah, wir haben eine modulare Entwicklungsumgebung, unsere Abhängigkeiten und Frameworks sind von den Arbeitsressourcen getrennt, wir nutzen einen tollen Präprozessor, was kann uns stoppen!? Richtig, nichts!

Wir sollten bei aller errungener Vereinfachung nicht vergessen, hin und wieder einen Blick auf das zu werfen, was die kleinen Gizmos im Hintergrund so treiben, während wir mit Mixins und Co. um uns werfen.

Die „dunkle Seite der Macht“ von LESS offenbart sich unter Umständen ziemlich schnell, sieht man sich nach getaner Arbeit die Größe der generierten Stylesheets an. Unachtsamer bzw. unbedachter Umgang kann hier schnell Dateien in der Größenordnung einiger hundert Kilobytes oder mehr (auch komprimiert) generieren, was zum Performance-Fresser wird.

Hierzu gibt es bereits zahlreiche gute Beiträge im Netz, deshalb beschränke ich mich auf ein paar wenige Grundsätze, die während der gesamten Entwicklung stets im Auge behalten werden sollten:

  • Frameworks nach Möglichkeit nie vorkompiliert bzw „komplett“ einbinden. Nur die Bestandteile in den Prozess aufnehmen, die auch tatsächlich verwendet werden.
  • Nach Möglichkeit (auch im Framework) Imports referenzieren.
  • Klassen erweitern (extend) anstatt Mixins zu verwenden
  • Sinnvolle Media Queries verwenden

Bootstrap in der Anwendung

Wir verwenden Bootstrap in der Version 3.x und binden lediglich die von uns benötigten Komponenten ein. In erster Linie nutzen wir das überaus flexible Grid-System, die Responsive-Bestandteile, sowie einige Basis-Stile und Mixins als Arbeitsgrundlage. An interaktiven Elementen kommen hier hauptsächlich die Modals zum Einsatz.

In der Art und Weise, wie wir Bootstrap verwenden, versuchen wir dabei eine Balance zwischen einem möglichst semantischen Markup einerseits und einer möglichst guten Performance andererseits zu finden.

Gerade bei komplexen Templates, wie sie Magento erfordert, sind semantisches HTML und für sich sprechende Selektoren ein wichtiges Kriterium für die Übersicht und Weiterverwendbarkeit des Projekts – gerade auch in der Arbeit im Team. Eine „stupide“ Anwendung z.B. der Gridklassen (wie .col-sm-6) direkt im Markup, als performanteste Lösung, stellt für uns daher keine Option dar.

Entscheidet man sich wie wir dazu, die Framework-Funktionalitäten per Präprozessor den entsprechenden Klassen zuzuordnen, sollte man sich allerdings über den durchaus beträchtlichen Overhead im resultierenden Stylesheet bewusst sein und die weiter oben angesprochenen Prinzipien wo immer möglich berücksichtigen.

Beispiel:

Wir können das Bootstrap Grid auf verschiedene Art anwenden. Die beiden DIV-Elemente #red und #green haben im Grid die gleichen Eigenschaften.

Das HTML-Markup:

Das LESS:

Um zu verstehen, was wir im Fall des grünen DIVs anstellen, schaue man sich das entsprechende Mixin aus Bootstrap an:

Analoger Code existiert für die restlichen Breakpoints. Es ist unschwer zu erkennen, welcher Overhead hier generiert wird.

Ich spreche mich hier dabei keineswegs gegen diesen Weg aus – das bleibt jedem Entwickler nach entsprechender Einzelfallbetrachtung überlassen. In der Praxis bewegen wir uns meist irgendwo in der Mitte zwischen den beiden Extremen.


Responsiveness

Bei Villeroy&Boch haben wir über weite Strecken die simple und robuste Methodik genutzt, die durch Bootstrap bereit gestellt wird. Es gibt hier ein paar nützliche Variablen und Mixins zur Definition der Breakpoints und deren Anwendung in Markup und CSS. Das Rad muss hier nicht neu erfunden werden.

Interessant wird es in diesem Projekt vor allem im Bereich des Headers. In den unterschiedlichen Ansichten sind die Änderungen zu grundlegend, als dass dies rein über Media Queries in CSS abbildbar wäre. Dadurch wurde es notwendig, DOM-Elemente zur Laufzeit zu verschieben oder auch zu erstellen/entfernen.

Dabei handelt es sich um die wichtigsten, nicht aber um alle Zustände.

JavaScript to the rescue!

So weit, so gewöhnlich. An dieser Stelle möchte ich eine kleine, aber feine Library erwähnen, die für derartige Szenarien ein großartiges Tool darstellt. Mittels enquire.js lassen sich analog zum CSS Breakpoints definieren, in deren Abhängigkeit sich beliebiger Code ausführen lässt. Die Library hat keine Abhängigkeiten, passt mit 0.8k in jede Hosentasche und bietet neben den obligatorischen „match“ bzw. „unmatch“ Events die Möglichkeit, ein Event nur einmal auszuführen, und dieses hinauszuzögern, bis die Bedingung auch tatsächlich zum ersten Mal eintritt.

Das ist sehr praktisch und lässt sich anschaulich am Beispiel der Navigation erläutern: Das Navigationskonzept für mobile Geräte weicht zu stark von dem für Desktops ab, als dass hier ein paar Zeilen CSS ausreichen. Wir benötigen letztlich ein eigenes Objekt im DOM und zahlreiche, zusätzliche Skripte etc.

Da das alles aber nur auf mobilen Geräten passiert, möchten wir das entsprechende JavaScript auf Desktops gar nicht erst ausführen. Umgekehrt gilt es ebenso.

Weiter können z.B. bequem global verfügbare, vom Device abhängige, Variablen zur Nutzung in anderen Skripten gesetzt werden.

Retina & Co

Überaus wichtig war dem Kunden die Beachtung von Nutzern mit Geräten, die über besonders hochauflösende Displays verfügen. Wer über eine mindestens zweifache Pixeldichte verfügt (z.B. Retina-Displays), der bekommt eine entsprechende Bilddatei ausgeliefert.

In Magento haben wir dafür neben sämtlichen Produkt- und Listendarstellungen auch entsprechende Teaser-Widgets für diese Anforderung angepasst.

Im Frontend setzen wir auf die Verwendung von HTML5-Elementen; die Entscheidung obliegt dem Client (Browser).

Ein Beispiel:

Der Browser entscheidet auf Grundlage des srcset-Attributs, welche der angegebenen Quellen er darstellt. Nicht mit source/srcset kompatible Browser fallen auf das img-tag zurück.

Device abhängige Bildformate

Obiges Konzept lässt sich um verschiedene Kriterien erweitern. Im Falle des Teasers auf der Homepage können wir zusätzlich je nach Device unterschiedliche Formate anzeigen lassen und so eine Art Layout-Variante über die Bild-Ressourcen anbieten.

Hier wird für Geräte mit einer Mindestbreite des Viewports von 767px ein Portrait- und für kleinere ein Landscape-Format ausgegeben. Das erlaubt Editoren und Grafikern einen größeren Spielraum beim Erstellen passender und spannender Bildzuschnitte.

So sieht das ganze dann im Browser aus:


SVG Sprites

Sprites zur Organisation häufig verwendeter Grafikelemente (meist Icons, Logos, etc.) sind in der modernen Webentwicklung mittlerweile ein Standard. Die am meisten verwendeten Bildformate sind dabei wohl nach wie vor Gif, Jpeg und PNG.

Generell noch nicht ganz so häufig anzutreffen sind SVGs und insbesondere deren Organisation in Sprites.

Welche Vorteile bietet SVG? Wie der Name bereits vermuten lässt, handelt es sich nicht um ein pixel- sondern ein vektorbasiertes Format. Es ist daher prädestiniert für die Darstellung von Icons, Logos und ähnlichen Grafikelementen. Dabei kann es beliebig und verlustfrei im Browser skaliert werden, während die Dateigröße minimal bleibt.

Darüber hinaus ist eine SVG-Datei letztlich nichts weiter als Text in Form einer XML ähnlichen Syntax. Egal, auf welchem Wege (Z.B. Export aus Adobe Illustrator o.ä.) ich mein SVG erhalte, ich kann es mit einem Editor anpassen und in meinen Entwicklungsprozess integrieren, versionieren, usw. Das macht es gerade aus Entwicklersicht zu einem wunderbaren Format.

Innerhalb von SVG ist es möglich, Pfaden bzw. Ebenen Klassen oder IDs zu vergeben, die dann wiederum per CSS gestyled werden können. Ich glaube, ich brauche nicht mehr zu sagen, um die Möglichkeiten erkennen zu lassen, die sich hier verbergen.

Die genaue Umsetzung in unserem Workflow würde hier den Rahmen sprengen. Wir verwenden grunt-svg-sprite und binden das Sprite sowie die Icons inline ein. Bestandteile des Sprites können mit einer entsprechenden Syntax über IDs referenziert werden:

Ein paar Dinge gilt es allerdings zu beachten. Was die plattformübergreifende Implementierung anbelangt, so gibt es noch deutliche Unterschiede, wenn es um die Unterstützung einiger Features geht. Auch hat es sich als problematisch erwiesen, wenn der SVG-Code über Inline-Styles verfügt. Diese haben sich i.d.R. kaum überschreiben lassen. Sie sind meiner Meinung nach ohnehin nach Möglichkeit zu vermeiden und können entsprechend entfernt werden.

Fazit

Verglichen mit nicht-responsiven Designs verlangen Responsive Templates eine umfangreiche Planung, um für alle Geräte und Displayarten eine exzellente Nutzererfahrung zu erstellen. Im Projekt von Villeroy & Boch haben uns die ausgewählten Technologien und Vorgehensweise den Weg geebnet, um ein Template zu erstellen, das erfolgreich Wartungsfreundlichkeit mit guter Performance verbindet. Jedoch sollte man für jedes Projekt einzeln entscheiden, welche Technologien am besten für die Umsetzung der individuellen Anforderungen geeignet sind.

Sandro Wagner

Autor: Sandro Wagner

Sandro Wagner ist Mediengestalter für digitale und Druckmedien. Während seiner dreijährigen Selbstständigkeit hat er für verschiedene Agenturen und nationale sowie internationale Kunden gearbeitet, wobei er sich auf Frontend-Entwicklung und CMS-Integrationen fokussiert hat.
Bei integer_net ist er als Magento Certified Frontend Developer tätig.