OXID Azure ins Responsive Zeitalter katapultieren: mit Bootstrap + SASS und Grunt

Viele OXID eShops laufen mit auf Azure basierenden Templates. Dieses ist nicht responsive und sieht auf Smartphones nicht gut aus. Dieser Artikel erklärt wie das CSS-Grid der Seite auf Bootstrap umgestellt werden kann, sowie gibt Tipps und Ausblicke für die Entwickler-TODOs. Der hier vorgestellte Grunt+Bower-Workflow wird durch Sass erweitert zu einem modernen CSS-Workflow und bildet die Grundlage für die genannten Anpassungen.

Vorgeschichte

Da ist er also, der tolle OXID Shop in der CE-Edition, der jahrelang schon treu seinen Dienst verrichtet. Das Theme basiert auf Azure und biegt dieses an verschiedenen Stellen etwas zurecht. Leider ist Azure nicht responsive, weder progressive enhanced, noch mobile first oder sonstwas. OXID eShop CE bietet bringt von Haus aus ein Mobile Theme an.

Ich bin ehrlich, mir gefällt das Mobile Theme nicht. Auch müsste ich ja auch in diesen Mobile Theme die paar Sonderlocken einbauen die es nunmal in einen leicht angepassten Shop so gibt. Doppelpflege, Device-Weiche, Nein danke!

In 2015 ist schon länger Responsive Webdesign Trend. Wobei man von Trend nicht mehr sprechen kann. Google hat unlängst Warnungen herumgeschickt an Betreiber von Nicht-Smartphone-kompatiblen Webseiten. Man müsste sich darauf einstellen, demnächst im Suchindex (auf Handys) kaum bis gar nicht mehr wiederzufinden. Nett formuliert heißt das “Beheben Sie Probleme der mobilen Nutzerfreundlichkeit”.

Google Mail Beispiel

Zeit aktiv zu werden!

Screenshots zum Warmwerden

Flechtie vorher: flechtie_alt

Flechtie nachher:

Desktop flechtie_neu_desktop

Smartphone flechtie_neu_smartphone

Build Workflow in a nutshell

Bower besorgt alle im Frontend benötigen Komponenten: Bootstrap, JS-Libs, OXID-Module
Grunt kopiert OXID Azure CSS als SCSS in unser eigenes Theme-Verzeichnis
Sass baut alle in SCSS programmierten Styles zu einer CSS zusammen, inkl. adaptierter Bootstrap-Komponenten und Azure-Style

Node.js und Bower Pakete

Die benötigen Node.JS Module werden in der ./package.json konfiguriert:

Die Bower-Komponenten in der ./bower.json:

Und Install ..

npm install -g bower
npm install -g grunt-cli
npm install

Grunt Module

Insofern noch nie mit Grunt gearbeitet sei die Getting Started empfohlen. Grundlegend wird das hier beschriebene Setup erweitert.

Initfile Gruntfile.js

Initial sucht sich Grunt die Gruntfile.js, wobei mittels des Moduls load-grunt-config bequem die Modulkonfigs in einzelne .js-Dateien im Unterverzeichnis ./grunt/.

./Gruntfile.js

Task-Liste aliases.yaml

Darin werden alle Grunt-Tasks definiert. Später genügt z.B. grunt css und es werden alle CSS gebaut. Sollen die Bower-Komponenten aktualisiert werden? grunt update_components und fertig.

./grunt/aliases.yaml

Bower: JS-Komponenten einbinden mit grunt-bower-concat

Kopiert die nötigen JS-Libs, welche als Bower-Komponenten vorliegen, zusammengefasst ins Theme-src-Verzeichnis. In layout/base.tpl wird diese dann eingebunden. Vorteil vom Zusammenfassen ist, dass nur noch ein Request statt zig nötig sind.

./grunt/bower_concat.js

Dateien kopieren mit grunt-contrib-copy

Kopiert die Azure CSS als SCSS ins eigene Theme-Verzeichnis. Kopiert die Fontawesome-Schriften ins Theme-src-Verzeichnis für einfachen Include in layout/base.tpl. Damit ist die Grundlage für die Sass-Integration gelegt.

Weiterhin werden die OXID-Module wie bereits hier beschrieben ins Module-Verzeichnis kopiert.

./grunt/copy.js

CSS zusammendampfen mit grunt-csswring

CSSwring entfernt Kommentare und Zeilenumbrüche, sodass am Ende ein einzeiliges CSS übrig bleibt. Wenn das der Webserver noch gzipped ausliefert ist die Optimierung perfekt. Zumindest technisch. Fachlich geht sicher noch so einiges ;)

./grunt/csswring.js

Bower-Komponenten aktualisieren mit grunt-exec

Um die Bower-Aktualisierung anzustarten wird einfach bower update ausgeführt.

./grunt/exec.js

Sass starten und CSS generieren mit grunt-contrib-sass

Die Generierung unseres Theme-CSS wird mittels Sass durchgeführt. Da Sass selbst in Ruby geschrieben ist, also kein natives Node.JS-Modul verfügbar ist, ruft das Modul sass mit entsprechenden Parametern auf.

./grunt/sass.js

Kontinuierlicher Sass-Build mit grunt-contrib-watch

Sobald mal in der IDE / im Editor speichert, möchte man natürlich dass das CSS neu gebaut wird, sodass man anschließend die Änderung sofort im Browser sieht. Durch die Trennung in aliases.yaml nach css für alle CSS und css_sass für das Theme-SCSS ist es möglich, gezielt nur das eigene Theme-Style neuzubauen.

./grunt/watch.js

Generierte Komponenten in layout/base.tpl einbinden

In das im eigenen Theme-Verzeichnis liegende base.tpl werden die kombinierten CSS und JS nun eingebunden.

Vorher: (bei mir, kein Azure-Standard!)

[{block name="base_style"}]
    [{oxstyle include="css/reset.css"}]
    [{oxstyle include="css/oxid.css"}]
    [{oxstyle include="css/ie7.css" if="IE 7"}]
    [{oxstyle include="css/ie8.css" if="IE 8"}]
    [{oxstyle include="css/libs/jscrollpane.css"}]
[{/block}]
...
[{oxstyle include="css/flechtie.css"}]
...
[{oxscript include="js/libs/jquery.min.js" priority=1*}]
[{oxscript include="js/libs/jquery-ui.min.js" priority=1*}]
[{oxscript include="js/libs/cookie/jquery.cookie.js" priority=2}]
...
[{block name="base_js"}]
    [{oxscript include="js/libs/es5-shim.min.js" priority=1}]
    [{oxscript include="js/libs/featuredetection.js"}]
    [{oxscript include='js/libs/superfish/hoverIntent.js'}]
    [{oxscript include='js/libs/superfish/supersubs.js'}]
    [{oxscript include='js/libs/superfish/superfish.js'}]
[{/block}]

Nachher:

[{oxstyle include="css/vendor.css"}]
[{oxstyle include="css/meinshop.css"}]
...    
[{oxscript include="js/vendor-libs.min.js" priority=1}]

Gut, die IE7+8 Hacks sind auch gleich über Bord geflogen, aber prinzipiell sollte die Entschlackung erkennbar sein. Das wichtigste ist mir aber, dass ich nicht mehr Azure reset.css und oxid.css vor den eigenen Theme-Styles einbinden muss, sondern dass es nur noch genau ein Customize-CSS gibt und ein gebündeltes Drittanbieter-CSS. Bei voller Update-Fähigkeit zum Azure-CSS.

Bootstrap Grid einbinden

Spätestens jetzt sollte man sich erstmal mit den Komponenten des Bootstrap-Frameworks vertraut machen. Dazu sollte man am besten Getting Started, CSS (vor allem Grid, Forms, Buttons, Responsive Utilities) und Components (vor allem Navs, Badges, Jumbotrons, Wells) mindestens querlesen um die Begrifflichkeiten und Konzepte zu kennen.

Prinzipiell sind die Bootstrap-CSS-Klassen und JS nun vorhanden. Um das Grundlayout auf das Bootstrap-Grid umzustellen, muss das Azure-Grundlayout auf die CSS-Klassen container, row und col-*-* umgestellt werden Dazu sind die Templates in layout/ komplett anzupassen. Hier die Gists am Beispiel von Flechtie:

  • layout/header.tpl: Kopfbereich, Login, Kategorieliste / Menü (Gist)
  • layout/footer.tpl: Service-Links, Impressum, Seitenabschluss (Gist)
  • layout/page.tpl: Seiten-Grundlayout mit Sidebar-Ausrichtung (Gist)
  • widget/sidebar/categorytree.tpl: Kategorieliste in Artikelliste (u.a.) (Gist)

Damit ist der Grundstein für weitere Anpassungen gelegt.

Sass meinshop.scss

Die zentrale Theme-SASS-Datei out/meinshop/src/css/meinshop.scss wird in einzelne Sass-Include-Dateien aufgegliedert, die günstigerweise mit der Tpl-Struktur korrelieren. Ein mittelschwer angepasster Shop schaut also z.B. so aus:

Am Anfang werden die Azure-CSS eingebunden (die ja mittels Grunt als SCSS vorliegen). Dann werden alle in den SCSS-Modulen angesprochenen Bootstrap-Module inkludiert. Kommt was neues hinzu muss man hier ggf. noch nachsteuern.

Halt moment, Boostrap wird doch schon in der css/vendor.css eingebunden, die durch Grund csswring:vendor erstellt wird? Ja, sicher, hier wird es ein wenig kritisch. Prinzipiell müsste man alle CSS aus der vendor.css gleich mit in die meinshop.scss aufnehmen und nur noch eine einzige meinshop.css haben. Aber dann dauert der Sass-Build ewig. Für das entgültige Deployment-CSS kann man das machen, soll aber hier keine Berücksichtigung finden.

Warum muss Bootstrap überhaupt in meinshop.scss eingebunden werden? Ha! Das ist der Trick!

DER TRICK! OXID-spezifische CSS-Klassen auf Bootstrap-Klassen umbiegen

Folgendes Beispiel:

.submitButton {
  @extend .btn;
  @extend .btn-primary;
  text-transform: none;
  text-shadow: none;
  height: auto;
}

Mittels Sass @extend wird der OXID-übliche Submit-Button auf Bootstrap umgebogen. Sass rechnet die Bootstrap-Klasse .btn mit der Klasse .submitButton zusammen. Da das ganze nach dem Azure-SCSS ausgeführt wird, überschreibt dieses die CSS-Eigenschaften vom Original-submitButton. Dadurch muss man nicht jedes Template ableiten und submitButton in btn btn-primary umbauen.

Ein weiteres Beispiel: Formular-Eingabefelder um form-control erweitern und so das Bootstrap-Layout für alle .form Felder ausrollen:

.form {
  input[type=password], input[type=text], select, textarea {
    @extend .form-control;
  }
  ..
}

Und der oft verwendete .largeButton:

.largeButton {
  @extend .btn-lg;
  text-transform: none;
  text-shadow: none;
  height: auto;
}

Ein @extend .btn ist hier nicht nötig, da diese Klasse nur zusammen mit .submitButton verwendet.

Fleißarbeit: Anpassung des restlichen Layouts

Nachdem die Basics nun klar sind - a) Templates ableiten und anpassen und b) Bootstrap-Klassen per SCSS anflanschen - geht die Fleißarbeit los. Und die hängt vom konkreten Layout ab, was der Shop fährt. Hier noch ein paar Auszüge aus dem Flechtie-Layout:

Fazit

Einen responsiven OXID eShop bekommt man derzeit nicht geschenkt. Schon gar nicht wenn man es ein wenig individueller haben möchte oder über die Jahre entwickelt hat. Aber es gibt Mittel und Wege um bestehende Responsive CSS-Frameworks, wie Bootstrap eins ist, transparent und gezielt einzubauen, ohne alle auf einmal über den Haufen zu werfen. Prinzipiell kann man das Ganze auch auf Basis von Semantic UI oder Pure CSS bauen.

Belohnt wird man mit einer auf jahre hin tauglichen CSS-Basis, die noch dazu automatisch mit dem aktuellen Stand des Azure-Templates verschmolzen wird.

Mich würde brennend interessieren wie das OXID-Agenturen machen. Vielleicht plaudert mal jemand aus dem Nähkästchen. Kommentare und Anregungen gerne hier oder via Twitter. @rhflow_de.