Responsive Retrofitting am Beispiel einer Webanwendung

oder Warum Standard-Frameworks nicht für Web-Anwendungen geeignet sind

Responsive Design ist heute in aller Munde, wenn es um die Entwicklung von Webseiten geht. Mobile Geräte wie Smartphones und Tablets erfreuen sich immer größerer Beliebtheit, darum soll natürlich jede neue Webseite auf diesen Plattformen auch genauso gut benutzbar sein wie auf dem klassischen PC-Desktop. Entsprechend gibt es eine Vielzahl an Libraries, Tools und Frameworks, die responsive und fluide Techniken unterstützen und versprechen, dass sich damit responsive Webseiten geradezu kinderleicht entwickeln lassen. Doch was geschieht, wenn es sich einmal nicht um eine Webseite handelt, sondern um eine Webanwendung?

Hintergrund und Ziel

Die Situation ist eigentlich nichts Besonderes: Wir betreiben eine ältere Webanwendung, ein firmeninternes Fußball-Tippspiel für unsere Mitarbeiter und Kunden. In der Anwendung können sie die Ergebnisse von Fußballspielen tippen und je näher der Tipp am tatsächlichen Spielergebnis liegt, desto mehr Punkte gibt es dafür. Die Anwender können außerdem jederzeit ihren eigenen Punktestand mit dem der anderen Spieler vergleichen. Es gewinnt, wer am Ende einer Spielserie die meisten Punkte erzielt hat. Neben den beiden Hauptfunktionen Tippen und Auswertung gibt es noch die üblichen Formulare zur Registrierung, Anmeldung, und Profilverwaltung, sowie ein paar statische Seiten, die Spielregeln und Gewinne aufführen.

Technisch handelt es sich bei dem Tippspiel um eine Play2-Anwendung, mit JDO-Backend und MySQL-Datenbank, sowie einem Web-Frontend auf Basis von HTML, CSS, und ein wenig jQuery. Das Design ist dabei pixelbasiert mit fester Größe – die klassischen 960 Pixel der Desktop-Browser.

Ziel ist es nun, das Tippspiel für die Weltmeisterschaft 2014 neu zu gestalten – natürlich responsiv, damit die Anwendung neben Desktop auch auf Tablet und Smartphone gleichermaßen gut bedienbar ist. Aus Kostengründen soll das Ganze mit möglichst geringem Aufwand erfolgen, d.h. das Backend soll größtenteils so bleiben wie es ist, und das Frontend so umgesetzt werden, dass sich die Implementierung für alle drei Zielplattformen eignet und höchstens minimale Anpassungen erfordert. Immerhin: Es handelt sich um eine interne Webanwendung, bei der uns die Zielgruppe bekannt ist. Folglich können wir bei den Anwendern halbwegs moderne Browser voraussetzen, und damit ausnahmsweise auf die sonst üblichen Verrenkungen für IE7 und Konsorten verzichten.

Entsprechend dieser Vorgaben entwirft unser UX-Team nun eine moderne Oberfläche, welche die verfügbare Darstellungsfläche auf Desktop, Tablet und Smartphone möglichst optimal ausnutzt, und deren Interaktions-Design sowohl für Maus als auch Touch-Bedienung geeignet ist. In der Umsetzung wird weiterhin Play2 verwendet, was uns schon einmal den grundlegenden Page-Flow sowie eine recht nützliche Template-Engine bereitstellt. Anstelle des bisherigen monolithischen CSS wechseln wir aber zu modularem LESS, um das Styling übersichtlicher und wartbarer zu gestalten. Dabei kommt LESS Elements zum Einsatz, damit wir diverse nützliche CSS-Eigenschaften wie border-radius nicht in all ihren browserspezifischen Varianten angeben müssen. Die nötigen Anpassungen an unsere drei Zielplattformen werden einfach in entsprechenden @media-Blöcken gekapselt…

Moment – Vielleicht können wir stattdessen auch eines der diversen Web-Frameworks verwenden? Immerhin gibt es mehrere Grid-Lösungen, die schon von Hause aus ein fluides Layout bieten und dieses bei Bedarf responsiv umstellen können. Außerdem sind verschiedene Widget-Sets verfügbar, die üblicherweise auf Webseiten für Navigation und Steuerung eingesetzt werden, und durchaus auch Mobilgeräte mit Touch-Bedienung unterstützen. Mit solchen Werkzeugen sollte sich unser Tippspiel doch sicherlich schnell und browserneutral umsetzen lassen… Oder etwa nicht?

Anwendungs-Layout

Für die Neugestaltung des Tippspiel-Frontends zur WM 2014 hat unser UX-Team ein schlankes Flat Design gewählt. Auf der rechten Seite des Kopfbereichs hat man Zugriff auf Profil-Verwaltung und Logout, sowie auf die statische Seite mit Gewinnen und Spielregeln. Im Inhaltsbereich kann man über Tabs zwischen den beiden Hauptfunktionen Tippen und Auswertung wechseln. Der Slider darunter bietet eine Detail-Navigation zu den jeweiligen Spielrunden; bisher sind nur die Gruppenspiele zum Tippen verfügbar, da ja die Zusammenstellung der Finalrunden noch nicht feststeht. Die einzelnen Spiele einer Gruppe sind nach Datum und Uhrzeit geordnet. Sie zeigen die jeweiligen Kontrahenten mit Name und Landesflagge, sowie zwei Felder für den Tipp des Spielausgangs. Diese erscheinen entweder als umrahmte Eingabefelder, oder als grau hinterlegte Flächen, wenn das Spiel begonnen hat und kein Tipp mehr abgegeben werden darf. Der Button zur Abgabe der Tipps schließlich ist als Overlay realisiert, damit er jederzeit ohne Scrolling erreichbar ist. Ganz am Ende der Seite (nicht gezeigt) befinden sich noch Command-Links zu den seltener benötigten Impressums- und Kontakt-Seiten.

Ein Vergleich der Screenshots zeigt, wie sich das Design responsiv an die verfügbare Darstellungsfläche der jeweiligen Zielplattform anpasst.

Der Kopfbereich enthält Icons, um die der Command-Links besser hervorzuheben.
Tippspiel im Desktop-Browser

Desktop: Der gesamte Inhalt wird auf eine maximale Breite beschränkt und zentriert, damit er immer gut lesbar bleibt. Außerdem ist im Kopfbereich genug Platz, um die Command-Links mit zusätzlichen Icons besser hervorzuheben.

Der Kopfbereich ist kompakter, mit weniger Icons, sowie unterschiedlicher Anordnung der Command-Links.
Tippspiel im Tablet-Browser

Tablet: Im Hauptbereich füllt die Tabelle nun die gesamte Breite der Anzeige. Der Kopfbereich schrumpft proportional, um dem geringeren vertikal verfügbaren Platz Rechnung zu tragen. Damit die Command-Links nicht in den Schriftzug hineinragen, sind sie kompakter übereinander angeordnet, und das Profil-Icon entfällt, da es für die Bedienung weniger essentiell ist.

Der Kopfbereich enthält keinen Schriftzug mehr. In der Tabelle entfallen die Ländernamen.
Im Smartphone-Browser

Smartphone: Die Tabelle wird zu schmal, um noch alle vorherigen Informationen anzuzeigen. Daher verschwinden die Ländernamen, denn die Flaggen reichen zur Unterscheidung der Spiele aus. Dazu sind die Randabstände der Tabellenfelder geringer, eine subtile aber platzsparende Anpassung. Auch der Kopfbereich wird deutlich kompakter. So verschwindet der Tippspiel-Schriftzug und fast alle Icons, bis auf den essentiellen Logout, wo das Icon den längeren Text ersetzt. Bei dieser minimalen Darstellungsfläche wird übrigens auch der Sinn des Navigations-Sliders klar erkennbar, denn es passen längst nicht mehr alle Spielrunden-Links nebeneinander.

So weit so gut. Aber was bedeutet das für die Umsetzung mittels Web-Frameworks? Tatsächlich zeigt sich schnell, dass Grid-Lösungen für dieses Design ungeeignet sind.
Das beginnt im Kopfbereich, wo neben horizontaler Anordnung auch eine vertikale Anordnung der UI-Elemente erforderlich ist. Grids unterstützen aber in der Regel lediglich die Horizontale. Eine vertikale Anordnung ist höchstens auf dem Umweg verschachtelter Grids möglich, was aber beileibe nicht alle Grid-Lösungen erlauben. Die verschiedenen responsiven Änderungen der Anordnung und Sichtbarkeit sind ebenfalls problematisch; um diese wie gewünscht umzusetzen, müsste man die entsprechenden Spalten-Definitionen jeweils extensiv modifizieren und/oder im HTML-Quellcode redundant vorhalten. Bei manchen Grid-Lösungen wäre das nicht einmal möglich, ohne auf Javascript zurückgreifen zu müssen. Unschön.
Im eigentlichen Inhaltsbereich sind die Probleme etwas subtiler, denn Grids sind ja eigentlich für fluide tabellarische Darstellungen durchaus gut geeignet. Bei näherer Betrachtung zeigt sich aber, dass das Design nur für die Kontrahenten-Spalten eine flexible Breite vorsieht, während die anderen Spalten Datum/Uhrzeit und Ergebnis-Tipp eine feste Breite haben sollen. Fluide Grids unterstützen aber in der Regel keine festen Spalten, sondern ändern die Breite aller Spalten immer proportional. In diesem Zusammenhang ist auch die Behandlung der Randabstände der Spalten problematisch, denn diese sollen ja nicht fluide sondern responsiv angepasst werden. Zuletzt stellt sich auch hier wieder das Problem, einzelne Informationen bei Bedarf auszublenden (Ländernamen), was je nach Grid-Lösung und HTML-Modellierung aufwendige Änderungen von Spalten-Definitionen nach sich zöge.

Also verzichten wir zunächst mal auf Grids, zumindest für Kopfbereich und Tippen-Funktionalität. Können uns wenigstens Web-Widgets irgendwie die Arbeit erleichtern? Leider nicht wirklich. Die Navigationselemente im Kopf- und Fußbereich sind einfache Command-Links. Für das Umschalten der Hauptfunktionen und den Tippen-Button können wir leider auch keine leichtgewichtige browserseitige Lösung wählen, da wir hierzu auf den Page-Flow von Play2 angewiesen sind. Bleibt der Navigations-Slider, der aber von seinem Design her ausgesprochen speziell ist. Wir haben dazu kein vordefiniertes Standard-Widget gefunden, was allen Anforderungen gerecht geworden wäre.

Auswertungs-Tabellen

Werfen wir als Nächstes einen Blick auf die zweite Hauptfunktion des Tippspiels, die Auswertung der Punktestände.

Der Fortschrittsbalken im Hintergrund jeder Tabellenzeile erlaubt einen schnellen visuellen Vergleich.
Auswertungs-Ansicht des Tippspiels

Das grundsätzliche Layout bleibt gleich, inklusive der Navigations-Tabs und dem Slider für die Spielrunden. Die Tabelle darunter zeigt die Liste aller Nutzer, geordnet nach der höchsten erzielten Punktzahl. Letztere ist jeweils rechts aufgeführt, der Name und Rang des Nutzers stehen links. Die ersten drei Plätze (und damit potentiellen Gewinner) sind mit einem goldenen, silbernen bzw. bronzenen Stern-Icon markiert. Es ist übrigens durchaus möglich, dass sich mehrere Nutzer denselben Rang bzw. Stern teilen. Als Anreiz für alle anderen Nutzer wird der Punktestand zusätzlich in der Art eines Fortschrittsbalkens angezeigt, was außerdem einen schnellen visuellen Vergleich ermöglicht. Sofern die Zeile des angemeldeten Nutzers außerhalb des sichtbaren Bereiches liegt, wird sie am unteren Rand als Overlay eingeblendet (nicht gezeigt).

Die Tabelle in der Detail-Ansicht hat unterschiedlich hohe Zeilen, je nachdem ob bereits ein Spielergebnis vorliegt oder nicht.
Modale Detail-Ansicht der Auswertung

Von der Übersichtstabelle aus kann man durch Auswahl einer Zeile zu einer Detailansicht gelangen. Diese verfügt über einen vereinfachten Kopfbereich, wo die normalen Command-Links zugunsten eines einzigen Links entfallen, mit dem man zur Übersichtstabelle zurückkehren kann. Die Tabelle ähnelt jener der Tippen-Funktion, nur dass hier keine Eingabe möglich ist. Sofern ein Spiel bereits vorüber ist, zeigt die betreffende Zeile zum Vergleich unter dem Tipp das Ergebnis nach 90 Minuten (als Bewertungsgrundlage), und gegebenenfalls den Endstand nach Verlängerung oder Elfmeter. Am linken Rand steht zu jedem Spiel die Punktzahl, die der Nutzer aufgrund seines Tipps erzielt hat, sowie in der ersten Tabellenzeile seine Gesamtpunktzahl für die gewählte Spielrunde.

Da das Design für die Auswertung stark dem für das Tippen ähnelt, ist es nicht weiter verwunderlich, dass es von denselben Problemen betroffen ist.
Ganz besonders deutlich wird das in der Detailansicht, die im Vergleich sogar noch komplizierter ist. Auch hier gibt es sowohl feste als auch variable Spaltenbreiten, und in der Smartphone-Variante entfallen die Ländernamen wegen Platzmangel. Erschwerend kommt hinzu, dass die einzelnen Tabellen-Zeilen unterschiedliche Höhe haben können. Wir können also wiederum kein Grid zur Umsetzung verwenden, aus den bekannten Gründen. Auch die gängigen Tabellen-Widgets helfen uns nicht weiter, denn sie unterstützen in der Regel ebenfalls keine Zeilen mit variabler Höhe und verschiedenen Inhalten.
Die Übersichtstabelle sieht zwar zunächst einfacher aus, doch ihr Design bringt ein eigenes Problem mit sich. Der Fortschrittsbalken im Hintergrund ist zwar schick und nützlich, bereitet aber nicht nur Grids und Tabellen-Widgets Schwierigkeiten, sondern sogar dem altehrwürdigen TABLE-Element selbst. Prinzipiell kann man zwar DIV, TR und TD mit Hintergrundbildern versehen, aber ein einzelnes Hintergrundbild über mehrere Elemente hinweg wird nur von sehr wenigen Browsern zuverlässig unterstützt.

Administrative Formulare

Neben den beiden Hauptfunktionen benötigt das Tippspiel auch diverse administrative Formulare. Schauen wir uns ein paar davon mal näher an.

Die Login-Seite des Tippspiels besteht aus einem einfachen Web-Formular.
Login-Seite des Tippspiels

Die Startseite der Anwendung setzt sich im Wesentlichen aus zwei solchen Formularen zusammen, eines für die Anmeldung und eines für die Registrierung neuer Nutzer (nicht gezeigt). Das Anmeldeformular selbst besteht aus den allgemein bekannten Kombinationen von Labels, Eingabefeldern und Aktions-Button. Zusätzlich gibt es diverse Command-Links für vergessenes Passwort und Registrierung im Formularbereich, sowie Impressum und Kontakt im Fußbereich. Zum Wechsel zwischen Anmelde- und Registrierungsformular ist wieder eine Tab-Navigation vorgesehen. Darüber zeigt sich eine dritte Variante des Kopfbereichs, der diesmal komplett ohne Command-Links auskommt und daher zentriert dargestellt wird.

Die Profil-Seite ist ein komplexeres Web-Formular mit mehreren Abschnitten. Eine Besonderheit sind die Info-Boxen mit Hilfestellungen.
Profilverwaltung mit komplexerem Formular
In der Smartphone-Variante werden die Formular-Elemente übereinander statt nebeneinander dargestellt.
Smartphone-Variante der Profilverwaltung.

Die übrigen Formulare der Anwendung unterscheiden sich davon nicht wesentlich, sie enthalten lediglich mehr Felder, und verwenden den schon von den Auswertungsdetails bekannten „modalen“ Kopfbereich mit Zurück-Link. Auch die responsive Anpassung folgt allseits bekannten Mustern, in der Smartphone-Variante erscheinen die Labels und Eingabefelder untereinander statt nebeneinander.
Einzig erwähnenswert sind höchstens noch die Info-Boxen an den Formular-Feldern: Bei Betätigung erscheint unter dem zugehörigen Feld ein Hilfetext-Bereich, der durch erneute Betätigung der Info-Box wieder verschwindet. Dieses etwas ungewöhnliche Verhalten ist dem Umstand geschuldet, dass das Design sowohl Maus- als auch Touch-Bedienung unterstützen muss, und daher die sonst üblichen Hilfetexte per Maus-Tooltip nicht verwendet werden dürfen.

Gerade weil die administrativen Formulare recht unspektakulär sind, könnte man für ihre Umsetzung ausnahmsweise einmal Grids und/oder Formular-Widgets einsetzen. Allerdings sind sie im Vergleich zum Rest der Anwendung wiederum so trivial, dass die Einbindung eines komplexen Frameworks nur zu diesem Zweck kaum eine entsprechend erhöhte Seiten-Ladezeit rechtfertigen würde. Das einzig halbwegs interessante Element, die Info-Boxen, muss man aufgrund ihres speziellen Verhaltens ohnehin selbst implementieren, was mit ein paar Zeilen jQuery schnell getan ist.

Herausforderungen beim Eigenbau

Da wir mit den gängigen Grid- und Widget-Frameworks nicht weit kommen, müssen wir bei der Umsetzung der Tippspiel-Anwendung wohl oder übel fast alles selber machen. Das bedeutet insbesondere eine ganze Menge Styling, damit die HTML-Inhalte unserem vorgegebenen Design entsprechen. Glücklicherweise gibt es ein paar Werkzeuge, die das Ganze einfacher machen. Wie eingangs schon erwähnt, setzen wir für das Tippspiel auf LESS mit LESS Elements, um das Styling modular und browserneutral umzusetzen:

Modulare Struktur der LESS-Dateien. Ein zentrales Media-Include wird in alle View-Module eingebunden. Das Main-Modul enthält Untermodule für allgemeines Layout, Kopfbereich, Navigation.
Modulares Styling mit LESS

media: Diese zentrale Include-Datei definiert verschiedene allgemeine Konstanten für das Tippspiel-Design, insbesondere die Einstellungen für min-width und max-width in den @media queries. Sie enthält auch zwei Präferenz-Listen für font-family in Normal- und Fettschrift, die unseren bevorzugten Zeichensatz „Clear Sans“ beinhalten. Ferner wird hier auch LESS Elements eingebunden.

elements: LESS Elements definiert diverse Mixins, die eine einheitliche Schnittstelle für neuere CSS-Eigenschaften bieten, z.B. border-radius. Damit braucht man nicht mehr auf browserspezifische Unterschiede und Präfixe achten.

main: Styling für das Main-Template von Play2. Das Modul enthält die font-face-Definitionen für den Zeichensatz „Clear Sans“, einen CSS Reset, sowie diverse Grundregeln für HTML und BODY. Ferner werden hier die Untermodule für allgemeines Layout, Kopfbereich-Elemente und Navigation eingebunden.

layout: Regeln für die grundlegende Aufteilung in Kopfbereich und Hauptbereich, sowie deren responsives Verhalten. Unter anderem werden die verschiedenen Höhen des Kopfbereichs für die drei Zielplattformen eingestellt, sowie die maximale Breite des Seiteninhalts für die Desktop-Variante.

navigation: Styling der Navigationselemente. Dies umfasst die Tabs, sowie den Slider und seine Bestandteile.

header: Styling und Verhalten der Elemente im Kopfbereich. Dies umfasst deren Positionierung und Sichtbarkeit in den Varianten Normal, Modal und Login, sowie die jeweilige responsive Anpassung für die drei Zielplattformen Desktop, Tablet und Smartphone. Damit ist header das bei weitem komplexeste LESS-Modul des Tippspiels.

tipp,hiscore,hidetail: Diese Module enthalten das Styling der logischen Haupt-Views der Anwendung: Tippen, Auswertung, sowie Detailansicht der Auswertung. Insbesondere werden hier die Breiten aller Tabellenspalten festgelegt, sowie die responsiven Anpassungen für Ländernamen und Randabstände in der Smartphone-Variante. Als Besonderheit findet sich hier auch die etwas trickreiche Definition, die in der Auswertungstabelle die Fortschrittsbalken im Hintergrund realisiert.

forms: Styling von Formularelementen, inklusive der responsiven Änderung der Anordnung nebeneinander bzw. übereinander. Da sich die Formulare stilistisch kaum voneinander unterscheiden, gibt es nur dieses eine gemeinsame Modul für alle formularbasierten Views.

Die Modularisierung des Stylings mit LESS hat uns während der Umsetzung gute Dienste geleistet. Bei Änderungswünschen des UX-Teams oder des Grafikers war es kein Problem die Übersicht zu behalten und schnell die betroffenen Regeln ausfindig zu machen. Lediglich bei den responsiven Anpassungen gab es ein kleineres Ärgernis. Wir hatten die Header-Höhe zuerst folgendermaßen definiert:

@header-height-var: 12em;

#header {
    width: 100%;
    height: @header-height-var;
}

@media screen and (max-width: @medium-width} {
    @header-height-var: 6em;
}

Intuitiv hätten wir erwartet, dass die geänderte Variable im @media-Block automatisch eine entsprechende Kaskaden-Regel erstellen würde. Leider tut uns LESS diesen Gefallen aber nicht. Folglich war es notwendig, die verschiedenen Verwendungen der Variable händisch herauszusuchen und die entsprechenden Regeln im @media-Block zu „wiederholen“:

@media screen and (max-width: @medium-width} {
    @header-height-var: 6em;

    #header {
        height: @header-height-var;
    }
}

Wir haben uns solche Arbeiten etwas erleichtert, indem wir als Namenskonvention die Endung -var für alle responsiv angepassten Variablen verwendet haben. Dies gilt auch rekursiv für alle daraus abgeleiteten Variablen und deren zugehörige Regeln. Z.B. haben wir dann @logo-height-var und @title-height-var, weil sie auf @header-height-var basieren.

Spezielle Inhalte

Wo wir schon dabei sind: Da sich die Höhe des Kopfbereiches abhängig von der Zielplattform ändert, müssen natürlich auch das Tippspiel-Logo und der Titel-Schriftzug jeweils angepasst werden. Im Prinzip wären wir damit in der Welt der Responsive Images, einem heißdiskutierten Thema für das es bisher noch keine einfache oder allgemeingültige Lösung gibt. Glücklicherweise haben wir einen Ausweg: Wir verwenden für die Elemente im Kopfbereich einfach Scalable Vector Graphics (SVG). Diese passen sich automatisch an die verfügbare Fläche an, sofern man sie geeignet einbindet (siehe Blog-Artikel), was von halbwegs modernen Browsern auch sehr gut unterstützt wird. Die anderen Bilder in der Anwendung, insbesondere die Länder-Flaggen, haben immer eine feste Größe, so dass wir dafür bei den einfachen Bitmap-Formaten (PNG) bleiben können. Damit haben wir das Problemfeld der Responsive Images erfolgreich umgangen.

Was sich allerdings nicht so einfach vermeiden lässt, ist die Notwendigkeit, einige spezielle Widgets für die Anwendung selbst zu erstellen, damit sie dem vorliegenden Design entsprechen. Eines davon ist die bereits angesprochene Auswertungstabelle mit hinterlegten Fortschrittsbalken, ein Feature welches zwar prinzipiell mit TABLE-Elementen umsetzbar ist, aber nicht zuverlässig von allen Browsern unterstützt wird. Wir lösen dieses Problem daher selbst, indem wir den Inhalt jeder Tabellenzeile („highscore“) quasi doppelt definieren, einmal als Fortschrittsbalken („progress“) und einmal für die eigentlichen Daten im Vordergrund („data“):

@for(player <- sortedPlayers) {
<div class="highscore">
    <div class="progress" style="width:@(player.getProgress)%;"></div>
    <a class="data" href="...">
        <span class="star@{player.getPlace}"></span>
        <span class="rank">@player.getDisplayPlace</span>
        <span class="player">@player.getUsername</span>
        <span class="score">@player.getScore</span>
    </a>
</div>
}

Beim Styling sorgen wir dann dafür, dass die beiden Inhalte übereinander platziert werden, indem wir sie absolut positionieren und mit einem z-index versehen:

.highscore {
    position: relative;
    z-index: 0;
    height: @row-height; width: 100%;

    .progress {
        position: absolute;
        z-index: 1;
        top: 0; left: 0;
        height: 100%; width: 0; /* override in HTML */
    }

    .data {
        position: absolute;
        z-index: 3;
        top: 0; left: 0;
        height: 100%; width: 100%;
    }
}

Für die Fortschrittsbalken wird dabei nur eine Art Prototyp definiert. Die tatsächliche Breite für jede konkrete Zeile lassen wir vom HTML-Template erstellen, indem wir dessen width-Eigenschaft durch ein style-Attribut am DIV individuell überschreiben.

Das andere spezielle Eigenbau-Widget der Anwendung ist der Navigations-Slider. Inhaltlich ist dieser nichts Besonderes, wir definieren ihn im HTML-Template ganz einfach als Kombination von UL, LI und A-Elementen, wie allgemein für Navigationsmenüs üblich, und fügen noch die beiden Scroll-Buttons am Rand hinzu:

<div id="slider">
    <ul id="navitems">
        @for(round <- rounds) {
        <li class="selected"><a href="@(linkBase+round.getId)">@round.getShortDescr</a></li>
        }
    </ul>
    <div id="navleft">& #x25C0 ;</div>
    <div id="navright">& #x25B6 ;</div>
</div>

Wir sorgen außerdem dafür, dass die Liste nicht umgebrochen wird, wenn sie zu lang wird:

#navitems {
    white-space: nowrap;
    overflow: hidden;
}

Damit man den Inhalt per Maus oder Touch hin und her ziehen kann, verwenden wir das jQuery Plugin Overscroll. Dieses aktiviert für ein Zielelement das hardwarebeschleunigte Overflow-Scrolling auf Mobilgeräten, bzw. emuliert dies in Desktop-Browsern sofern erforderlich. Wir konfigurieren dann die Navigationsliste entsprechend, und implementieren bei der Gelegenheit gleich noch die beiden Scroll-Buttons. Ihr click-Handler schiebt einfach die jeweils nächsten nicht angezeigten Listenelemente in den sichtbaren Bereich:

$(document).ready(function() {
    var nav = $("#navitems");

    jQuery.fn.overscroll.settings.thumbThickness = 0;
    nav.overscroll({
        direction: "horizontal",
        cancelOn: "a"
    });

    var scrollTo = function(child) {
        if (child.size()) {
            nav.animate({
                scrollLeft: nav.scrollLeft() + child.position().left
            }, 500);
        }
    };

    $("#navleft").click(function() {
        scrollTo(nav.children("li").filter(function() {
            var me = $(this);
            return me.position().left - me.outerWidth() > -nav.outerWidth();
        }).eq(0));
    });
    $("#navright").click(function() {
        scrollTo(nav.children("li").filter(function() {
            var me = $(this);
            return me.position().left + me.outerWidth() > nav.outerWidth();
        }).eq(0));
    });
});

Der Code für den Slider ist übrigens bei weitem das komplexeste Stück Javascript der Anwendung. Damit sind Slider, Fortschrittsbalken und variabler Kopfbereich die einzigen drei Bestandteile des Tippspiels, die etwas aufwendiger umzusetzen waren. Alles Übrige war normale HTML-Modellierung und LESS-Styling, wie es in jedem anderen Entwicklungsprojekt auch erforderlich gewesen wäre. Der Verzicht auf Grids und Web-Widgets hat also gar nicht einmal so viel zusätzliche Arbeit verursacht, wie wir ursprünglich befürchtet hatten.

Mobil-Anpassungen

Natürlich stoßen wir beim Eigenbau gelegentlich auch auf solche Probleme, die uns gängige Web-Frameworks vielleicht abgenommen hätten. Glücklicherweise können wir moderne Browser bei unseren Nutzern voraussetzen, so dass uns die sonst notwendige Legacy-Kompatibilität erspart bleibt. Allerdings bekommen wir es dafür mit den Mobil-Browsern zu tun, die bisweilen ebenfalls einige erstaunliche Marotten aufweisen können.

Ein Beispiel dafür ist das Overlay mit dem Tippen-Button in der ersten Hauptansicht. Eigentlich sollte die fixed-Positionierung heutzutage kein Problem mehr darstellen, und die Mobil-Browser auf unseren Smartphone-Testgeräten spielen auch brav mit. Bis man eines der Tippen-Felder anwählt und die Bildschimtastatur erscheint. Dann ist der Button in der winzigen verbleibenden Rest-Ansicht ziemlich im Wege. Viel schlimmer, bei manchen Browsern funktioniert auf einmal die fixed-Positionierung nicht mehr, das Overlay bleibt an der letzten vorherigen Stelle kleben und scrollt munter mit dem Seiteninhalt mit! Es bleibt uns also nicht weiter übrig, als das Overlay („#footer“) auf Touch-Geräten abzuschalten und am Seitenende zu platzieren, sobald eines der Tipp-Felder („goal“) aktiviert wird. Wir haben das Styling dazu so eingerichtet, dass es genügt die Positionierung von fixed auf absolute umzuschalten:

$(document).ready(function() {
    var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0));
    if (isTouch) {
        $("input.goal").focus(function() {
            $("#footer").css("position", "absolute");
        });
        $("input.goal").blur(function() {
            $("#footer").css("position", "fixed");
        });
    }
});

Ein anderes Beispiel ist die Art und Weise, wie Mobil-Browser eine Webseite für ihr kleineres Display aufzubereiten versuchen. Das ist prinzipiell ja eine gute Idee, da das Design der meisten Webseiten immer noch für Desktop-Browser ausgelegt ist. Wenn man sich aber extra ein schönes Design für Smartphone-Displays überlegt hat, funkt diese standardmäßige Mobil-Aufbereitung massiv dazwischen, mit teilweise ziemlich grotesken Ergebnissen.

Zum einen tarnen sich die Mobil-Browser gerne als Desktop-Variante, indem sie ein typisches 960 Pixel breites Canvas simulieren. Entsprechend greifen die ganzen schönen @media-Blöcke nicht, die man für kleinere Displays vorgesehen hat. Als Gegenmaßnahme fügen wir im HEAD unseres Haupt-Templates den Meta-Header viewport hinzu. Damit sorgen wir dafür, dass der Mobil-Browser die echten Gerätedimensionen verwendet, und sein Canvas immer brav im Maßstab 1:1 bleibt:

<meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1">

Zu anderen versuchen die Mobil-Browser, gewisse Elemente einer Webseite besser lesbar zu machen, indem sie die CSS-Eigenschaft font-size knallhart ignorieren und durch ihren eigenen Standard ersetzen. Das passiert insbesondere dann, wenn ein Element die gesamte Breite der Seite einnimmt – oder auch nicht, wenn das Element wiederum verschachtelte Elemente enthält. Die Regeln dafür sind nicht immer ganz klar nachvollziehbar. Wir gewöhnen dem Browser diese Marotte ab, indem wir in main.less die CSS-Eigenschaft text-size-adjust angeben. Leider ist die Eigenschaft nicht standardkonform, so dass wir alle browserspezifischen Prefixe angeben müssen:

html {
    -webkit-text-size-adjust: 100%;
       -moz-text-size-adjust: 100%;
        -ms-text-size-adjust: 100%;
}

Fazit

Auch wenn Webseiten und Webanwendungen auf demselben Technologie-Stack beruhen, kann ihre unterschiedliche Natur teilweise gravierende Folgen für die Implementierung nach sich ziehen. Insbesondere greifen oftmals Standard-Frameworks nicht, die für die Entwicklung von Webseiten konzipiert wurden. Die Ursachen dafür sind vielfältig:

Beispielsweise sind Grid-Frameworks sehr beliebt, um eine Webseite mit einem fluiden Layout zu strukturieren und bei Bedarf geeignete responsive Umstellungen vorzunehmen. Allerdings arbeiten solche Grids dabei mit recht grobgranularen Blöcken von Text und Bildern. In einer Webanwendung dagegen gibt es meist deutlich feingranularere Elemente, die nach sehr speziellen Anforderungen positioniert werden müssen. Dabei ist neben der üblichen textartigen horizontalen Anordnung oftmals auch eine vertikale Anordnung vonnöten. Ein Grid ist damit schnell überfordert, selbst wenn es durch Verschachtelung prinzipiell auch komplexere Layout-Varianten unterstützen könnte.

Ferner sind die meisten Webseiten auf eine mehr oder weniger lineare Konsumierung ausgelegt; News-Meldungen, Suchergebnisse, Listen, und längere Texte fallen in diese Kategorie. Dabei ist vertikales Scrolling die primäre Interaktionsart, entweder explizit oder durch den gelegentlichen Klick auf einen lokalen Hyperlink. Für die Präsentation der Inhalte steht quasi „unendlich“ viel vertikaler Raum zur Verfügung; entsprechend ist es unproblematisch, die Inhalte zur responsiven Anpassung einfach untereinander statt nebeneinander anzuordnen. Webanwendungen sind dagegen zumeist deutlich interaktiver, so dass es wichtig ist, ihre Informations- und Steuerelemente kompakt zu präsentieren und leicht zugänglich zu machen. Auch nehmen andere Interaktionsarten wie Klick und Ziehen einen höheren Stellenwert ein. Webanwendungen tendieren daher zu Single-Page-Designs, bei denen störendes Scrolling nach Möglichkeit vermieden werden sollte. Entsprechend sind einfache Automatismen zur responsiven Umstellung unzureichend.

Natürlich ist es auch für Webseiten attraktiv, interaktive Web-Widgets für typische Aufgaben wie Navigation und Präsentation zu nutzen. Gerade weil die meisten Webseiten in dieser Hinsicht sehr ähnliche Anforderungen haben, gibt es so viele Frameworks mit Standard-Widgets, die die Entwicklung entsprechender Seiten vereinfachen und beschleunigen. Das ändert aber nichts an ihrer grundlegenden Textlastigkeit. Im Gegensatz zeichnen sich Webanwendungen dadurch aus, dass interaktive Elemente nicht nur schmückendes Beiwerk sind, sondern eben der zentrale Bestandteil. Darüber hinaus gibt es oft sehr spezielle Anforderungen an die funktionale und visuelle Gestaltung, um eine innovative Benutzerschnittstelle zu realisieren. Ein Standard-Framework kann das nicht leisten; eine Boostrap-Seite bleibt immer klar erkennbar eine Boostrap-Seite, egal wie sehr man an den Farbeinstellungen dreht.

Folglich sollte man zu Beginn eines Projekts immer klar festlegen, wo bei der responsiven Webanwendung die Prioritäten liegen. Wenn es schnell gehen muss und es keine besonderen Anforderungen an das Design der Benutzerschnittstelle gibt, kann der Einsatz von Web-Frameworks die Entwicklung deutlich vereinfachen. Speziellere Anforderungen rechtfertigen eventuell den Aufwand, ein bestehendes Framework punktuell anzupassen oder zu erweitern. Wenn der Schwerpunkt aber auf einem außergewöhnlichen und innovativen Design liegt, kommt man um einen großen Anteil an Eigenentwicklung nicht herum und muss auch einen entsprechend höheren Aufwand dafür in Kauf nehmen.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s