Warum Design auch Kopfsache ist

Wenn man einen Auftrag erhält, schießen einem als Designer oft sofort tausende Ideen in den Kopf. Das fühlt sich gut an, und als blutiger Anfänger möchte man am liebsten gleich drauflos designen. Erfahrene Designer wissen jedoch, wenn man das macht, passiert in vielen Fällen genau das Gegenteil von dem gewünschten Effekt: Das Design sieht zwar meist ganz gut aus und im Kopf war alles noch so logisch, aber man vergisst dann auf Usability und die Wünsche des Kunden. Oder noch schlimmer, es trifft nicht einmal optisch den Geschmack – ganz zu schweigen von der technischen Umsetzbarkeit. Man hat also Zeit und Energie in eine Arbeit investiert, die völlig neu aufgerollt werden muss. Schlimmstenfalls hat man als Designer schon alle Energie verpulvert und gar keine Lust mehr auf das Projekt auf das man sich zuvor so gefreut hat. Aber so muss es nicht sein.

Design besteht zum einen Teil aus Kreativität und Gespür für das Schöne und Ansprechende. Aber der zweite große Teil ist Funktionalität und Logik. Eine noch so schöne Website verliert jeden Charme, wenn sie nicht logisch aufgebaut und gut bedienbar ist. Ein sehr schönes Zitat zu diesem Thema habe ich kürzlich in einem Artikel von Ryan J. Morrison gelesen (siehe medium.com): ! Every decision in your design needs to have purpose.

Das klingt nachvollziehbar. Aber woher weiß ich, welchen Zweck meine Designentscheidungen erfüllen müssen? Dazu brauche ich viele Informationen über die Bedürfnisse der User, des Kunden und den Details der technischen Umsetzung. Deshalb muss ich mich, bevor ich mit dem Design beginne und entsprechende Entscheidungen fälle, mit diesen Komponenten intensiv auseinandersetzen.

1 Referenzmaterial, Mood Boards & Skizzen

Die Ideen, die man ganz zu Beginn eines Projektes hat, sind sehr wertvoll und sollten auf keinen Fall verworfen werden. Sammlungen von Referenzmaterial, Mood Boards und Skizzen sind in dieser Konzeptionsphase die richtigen Methoden um Ideen festzuhalten. Das beste an diesen Methoden ist, dass man sie mit dem Kunden und den Developern teilen kann. Dafür eignen sich zum Beispiel www.pinterest.com oder gomoodboard.com sehr gut. Zuerst muss ein gemeinsamer Nenner mit dem Kunden gefunden werden: die Farbpalette, die Typografie, die Art der Bilder und Illustrationen, der Stil der Icons, die Art der Navigation, die Menge und der Einsatz von Whitespace und der generelle Kontrast sind hier abzuklären.

2 Hoch lebe das Mockup

Sobald bei dem optischen Konzept Einigkeit herrscht, folgt der Teil, der vor allem für die Benutzerfreundlichkeit einer Website unerlässlich ist: die Erstellung von Mockups. Tools wie Balsamiq machen es uns leicht in kürzester Zeit eine grobe Skizze einer Website zu erstellen durch die man sich sogar durchklicken kann. Je mehr Inhalte auf einer Website untergebracht werden müssen und je komplexer sie zusammenhängen, desto wichtiger wird dieser Schritt. Mit den Mockups können auch schon vorab Usability-Tests durchgeführt werden, die zeigen, ob die User sich auf der Website zurechtfinden und ob das Navigationsdesign sofort verstanden und richtig angewendet werden kann.

3 Get together

Erst wenn der Aufbau der Website mithilfe der Mockups geklärt ist, folgt ein erneuter Designschritt: die Elemente von Punkt 1 (Referenzmaterial, Mood Boards und Skizzen) in die Form von Punkt 2 (Mockups) bringen. Manche würden sagen, dass diese vielen Planungsschritte die Kreativität hemmen. Aber dem widerspreche ich. Es verlangt sogar ein hohes Maß an Kreativität, um neue gestalterische Elemente – ob sie nun wo abgeguckt oder völlig neu erfunden sind – nicht einfach nach Schema F zu designen, sondern sie in eine Form zu bringen, die dem Kunden gefällt, den Usern nützlich ist und die technisch einwandfrei funktionieren.

Das ist auch der Grund, warum einem Designer niemals langweilig wird. Jedes Projekt ist anders, weil verschiedene Nutzer unterschiedliche Bedürfnisse aufweisen und jeder Kunde einen anderen Geschmack hat. Man muss sich nur darauf einlassen – und es bleibt immer spannend!

Scalable Vector Graphics (SVG): Eine Einführung

SVGs (scalable vector graphics) eröffnen völlig neue Möglichkeiten im Web. Ihr großer Vorteil besteht unbestritten darin, dass sie, wie der Name bereits sagt, skalierbar sind und dank ihrer Vektoreigenschaften dabei nicht an Qualität verlieren.

Accessibility von SVGs

Aber das ist längst nicht alles. SVGs können direkt in den HTML Code eingebunden werden und auch Textelemente beinhalten. Diese Textelemente können auch von Screenreadern erfasst werden und erfüllen somit – ganz im Gegenteil zu Texten auf Pixelbildern – die Web Content Accessiblity Guidelines (WCAG 2.0) des World Wide Web Consortiums (W3C). SVGs sind auch den Icon-Fonts vorzuziehen, da diese unter Umständen eine Barriere darstellen können. Denn es gibt User, die über Userstyles im Browser die Fonts austauschen, damit sie besser zu lesen sind (z.B. Legastheniker mit einer für sie eigens gestalteten Schrift). Wenn da dann der Font ausgetauscht wird, dann sind die Iconfonts visuell nicht mehr aussagekräftig. Bei Usern des Opera Mini (vor allem in Schwellenländern stark verbreitet) wird die Iconfläche sogar komplett schwarz dargestellt. Das ist also ein weiterer Grund aus der Sicht der Accessibility, der für SVGs spricht.

Um aber gänzlich barrierefreie SVGs zu erstellen, müssen einige Richtlinien befolgt werden. In einem Artikel von Léonie Watson (digital accessibility consultant, Mitglied der W3C HTML Working Group und der HTML Accessibility Task Force) auf sitepoint.com findet ihr eine ausführliche Anleitung wie man die Screenreader-Accessibility erreichen kann. Ein Screenreader ermöglicht es einem Blinden, dass die Inhalte einer Website vorgelesen werden. Um dies zu ermöglichen, braucht der Content eine klare semantische Struktur, sonst weiß man nicht, wo zum Beispiel die Überschrift ist und damit auch nicht, was wichtig ist. Was für den Blinden gut ist, ist dann auch für die Google-Suchmaschine gut, denn diese ist der weltweit größte blinde User.

DIE WICHTIGSTEN PUNKTE AUS LÉONIE WATSONS ARTIKEL:

  • Verwende SVGs, wenn immer es möglich ist. Allein ihre Skalierbarkeit ohne Qualitätsverlust macht sie barrierefreier als herkömmliche Pixelbilder.
  • Für Screenreader ist es einfacher, wenn du Inline SVG verwendest. Das heißt, wenn du bereits eine SVG Datei hast, öffne sie in einem Text- oder Code-Editor, kopiere den Code mit den Vektorinformationen innerhalb des <svg> Tags und füge ihn direkt in die HTML Datei in das Element <svg></svg> ein.
  • Das erste Kindelement des <svg> muss das Element <title> sein, das einen Titel für das SVG-Bild beinhaltet.
  • Darauf muss das Element <desc> folgen, das eine kurze Beschreibung des Inhalts des SVG-Bildes beinhaltet, also die Rolle des alt-Attributs übernimmt.
  • Manche Browser bzw. die entsprechenden Screenreader erkennen das title / desc-Element nicht von allein. Für diesen Fall ist es sinnvoll, das ARIA Label einzubauen, das zumindest ab IE9 interpretierbar ist. Es gibt lediglich an, welche Elemente im SVG die Beschreibung beinhalten. Angegeben wir dafür die id der jeweiligen Elemente:
<svg version="1.1" ... aria-labelledby="title desc">
     <title id="title">Header Animation</title>
     <desc id="desc">Bild mit 2 Quicklinks</desc>
     ....
</svg>
  • Wie oben bereits erwähnt, sollte jeder Text, der in der SVG Grafik vorkommt, als <text> Element eingebaut werden, um ihn für Screenreader klar als solchen hervorzuheben.
  • Mithilfe des <a> Elements werden auch Inhalte eines SVGs fokussierbar. Damit Screenreader das Ankerelement als Link erkennen, muss noch die Rolle zugewiesen werden: <a role=”link”>
  • Wenn die verwendete scalable vector graphic etwas komplexeres oder interaktives beinhaltet, dann muss eine Möglichkeit überlegt werden, wie diese Inhalte noch transportiert werden können. Im Idealfall gibt es zwei verschiedene Views für diesen Inhalt und der User kann selbst entscheiden, welchen er wählt.

Natürlich darf nicht vergessen werden, dass es noch andere Accessibility Themen gibt, die auch bei SVGs nicht außer Acht gelassen werden dürfen, wie z.B. Farbkontraste. Diese werden wir allerdings in einem gesonderten Blogbeitrag thematisieren und an dieser Stelle nicht genauer behandeln.

Line Icons und Drawing Animations

SVGs kann man nicht nur als barrierefreie Grafiken einsetzen, sie eröffnen uns auch eine Vielfalt an Animationsmöglichkeiten, z.B. für SVG Drawing Animations.

Erstellung der SVGs in Illustrator

Das Grundprinzip ist einfach. Die Grafik, die gezeichnet werden soll, muss einen stroke beinhalten. Wird die SVG Grafik beispielsweise in Illustrator erstellt, bedeutet das, dass das Objekt entweder eine Kontur benötigt oder selbst nur aus einer Linie oder einem Pfad besteht. Ein Objekt mit Kontur ist dann sinnvoll, wenn wie bei dem Beispiel unter tympanus.net ein detailliertes Objekt gezeichnet werden soll. Möchte man allerdings eine Animation von Line Icons erstellen, die sich selbst zeichnen, wie z.B. unter blog.teamtreehouse.com zu sehen ist, dann muss hierbei jede Linie wirklich ein einzelner Pfad sein und darf keine Fläche beinhalten. Das kann bei vorgefertigten Icons, die man nicht selbst from scratch erstellt, zu einem Problem führen, da diese meistens – auch wenn es sich um transparente “Line” Icons handelt – aus einer gefüllten Fläche bestehen.

Dieses Problem beschreibt das folgende Bild sehr gut: es wird nicht die gewünschte Linie animiert (wie erwartet, Abbildung rechts), sondern die Kontur der Fläche (Abbildung links).

Deshalb muss man diese Icons in tatsächliche Line Icons umwandeln. Das geht glücklicherweise relativ einfach. Zur Veranschaulichung sieht man unten im Bild ganz links ein Line Icon. Mit dem Befehl shift + x wird die Kontur des Objekts angezeigt. Diese sieht dann so aus wie die zweite Abbildung von links. Anschließend muss der zusammengesetzte Pfad zurückgewandelt werden und alle Teile gelöscht werden, die nicht nötig sind für das finale Icon (mittlere Abbildung unten). Mit dem Shortcut c wird das Schneidewerkzeug aktiviert, mit dem man Pfade zerteilen und somit danach noch weitere überflüssige Linien entfernen kann (vorletzte Abbildung unten). Im Anschluss müssen die letzten Linien so verlängert werden, dass die Lücken gefüllt sind. Somit entsteht wieder dasselbe Symbol, nur dass es jetzt nur noch aus (Kontur-)linien besteht (Abbildung rechts). Bei einem Icon wie aus unserem Beispiel würde es sich auch anbieten, es einfach nachzuzeichnen. Aber für komplexere Elemente wollte ich auch diesen Weg einmal durchgehen.

Je nachdem mit welchem Werkzeug die Objekte in Illustrator erstellt wurden, heißen sie im SVG unterschiedlich, z.B. rectpolygonpolylineline oder ähnliches. Sie alle enthalten ein stroke Element und sind somit geeignet für die Line Animation. Möchte man allerdings mit einheitlichen Formen arbeiten, dann empfiehlt es sich, die verschiedenen Formen in zusammengesetzte Pfade umzuwandeln. Somit heißen sie im SVG alle <path> und ihre Koordinaten werden gesammelt in der description d beschrieben. Eine Anmerkung am Rande: Die Umwandlung in Pfade bleibt eine Ausnahme, die nur für die Drawing Animation für entsprechend kleine Line Icons, die wirklich nur aus einer Linie bestehen, benötigt wird. Im Normalfall wandelt man Icons in eine Fläche mit Kontur um. Diese enthalten dann standardmäßig im Code nur die description d und keinerlei sonstige Informationen (außer es gibt eine Kontur stroke="1px" und explizit keine Füllung fill="none")

Animation der SVGs

Grundsätzlich gibt es zwei Möglichkeiten, wie SVGs animiert werden können: mit CSS Animationen und mit SVG Animationen. Da uns CSS Animationen schon geläufig sind (siehe Blogartikel Workshop: CSS Animationen) und sie für Drawing Animations sehr gut geeignet sind, werde ich im folgenden Abschnitt näher darauf eingehen. Die SVG Animationen sind noch einmal ein ganz eigenes Thema, das ich bei Gelegenheit in einem zukünftigen Blogbeitrag behandeln werde.

Für die Drawing Animations fand ich ebenfalls eine sehr gute Quelle, die alles einfachst und präzise erklärt, was man wissen muss: css-tricks.com/svg-line-animation-works/. Das Wichtigste aus diesem Artikel möchte ich hier kurz anführen. Das zu animierende Objekt muss, wie oben bereits erwähnt, einen stroke aufweisen. Das haben wir beim Erstellen der Objekte sichergestellt. Diesen stroke kann man strichliert darstelllen – was ja bedeutet, ein Teil der Linie wird gezeigt und ein gleich langer Teil wird nicht angezeigt. Dieser Fakt kann genützt werden, um den einen Teil so lang zu machen, dass er die gesamte Kontur ausfüllt.

Dafür gibt man dem Objekt im CSS einen stroke-dasharray in der Länge der Linie. Die Länge kann mittels Javascript herausgefunden oder mit ein wenig Spielerei in etwa erraten werden. Dann gibt man diesem Objekt noch einen stroke-dashoffset in derselben Länge. Das bewirkt, dass die Kontur vorerst noch nicht angezeigt wird, weil jetzt der nicht sichtbare Teil des dashes dargestellt wird. Dann muss man dem Element noch eine Animation zuweisen, wie z.B. animation: dash 5s linear forwards. Bei den @keyframes dash muss jetzt der offset verändert werden, damit die sichtbare Linie langsam die unsichtbare verdrängt. Das geschieht indem man den stroke-dashoffset auf 0 setzt. Das Attribut forwards bei der animation stellt zudem sicher, dass die Animation nur einmal in eine Richtung stattfindet.


.path {
  stroke-dasharray: 1000;
  stroke-dashoffset: 1000;
  animation: dash 5s linear forwards;
}

@keyframes dash {
  to {
    stroke-dashoffset: 0;
  }
}

Wenn der Fall eintritt, dass sich die Kontur in die falsche Richtung aufbaut, kann man einfach dem offset zuerst einen negativen Wert geben. Dadurch wird gewährleistet, dass sich die Kontur aus der anderen Richtung aufbaut.

Das ist nur einer von vielen Tricks zur Animation von SVGs. Hoffentlich habt ihr genauso viel Freude daran wie ich, denn weitere Beiträge zu diesem Thema werden noch folgen!

Drupal 8 Basics: eigene Service Klasse definieren

Heute werden wir uns in der Drupal 8 Modulentwicklung vertiefen und zeigen, wie man einen eigenen Service in Drupal 8 definieren kann. Das Ziel wird sein, eine (neuen) Controller zu definieren, um eine Route zu registrieren, auf welcher dynamisch die letzten 10 Tweets eines Twitter-Benutzers dargestellt werden. Wir haben bereits in einem unserer vorherigen Beiträge gezeigt, wie man in Drupal 8 eine Controller-Klasse erstelltt, die beim Aufruf einer URL einen einfachen Text ausgibt. Das Beispiel war immerhin nicht ganz trivial, denn der Text war von weiteren Parametern in der URL abhängig. Dieses Beispiel gilt es nun um das Konzepte von Services, ServiceContainer und Dependency Injection zu erweitern, um die Logik für den Abruf der Twitter-Daten in einer Service-Klasse getrennt zu halten und diese gezielt unserem Controller per Dependency Injection zur Verfügung zu stellen.

Voraussetzungen

Um Daten von Twitter holen zu können, muss man eine App bei Twitter registrieren. Das ist notwendig, damit man von der API von Twitter User-spezifische Daten abfragen kann. Wichtig ist es, nach der Erstellung der App einfach sich die Tokens für Consumer Key (API Key) und Consumer Secret (API Secret) zu merken, denn diese werden wir später in dem Code unserer Service-Klasse einsetzen.

Eine neue Route definieren

Dieser Teil dürfte uns schon aus unserem ersten Beitrag bekannt sein (Datei /mymodule.routing.yml):


mymodule.twitter:
  path: '/twitter/{username}'
  defaults:
    _controller: '\Drupal\mymodule\Controller\TwitterController::feed'
    _title_callback: '\Drupal\mymodule\Controller\TwitterController::setTitle'
    username: 'drupal'
  requirements:
    _access: 'TRUE'

Wir definieren einen neuen Pfad („/twitter“), der einen Variablen Teil ({username}) erhalten kann. Das neue hier ist, dass wir anstatt von _title den key _title_callback benützen, damit wir den Titel der Seite, entsprechend dem dynamischen Wert in der URL, durch eine Funktion ändern können.

Registrierung unserer Service-Klasse und deren Implementierung

Analog zur Vorgehensweise für die Registrierung der Controller, legen wir im root-Verzeichnis unseres Moduls eine YAML-Datei an, wo wir unsere Service-Klasse bekannt geben. Die Datei muss MODULENAME.services.yml heissen (in unserem Fall /mymodule.services.yml):


services:
  mymodule.twitter_service:
    class: 'Drupal\mymodule\Twitter\TwitterService'

Der Code oben ist ziemlich klar. Der „machine_name“ unserer Klasse ist mit dem Modulnamen prefixed, damit keine Namenskonflikte mit anderen Klassen entstehen. Wir können nun auch die Datei anlegen, die unsere Klasse beinhalten wird. Der Code für diese Datei (/src/Twitter/TwitterService.php) lautet wie folgt:

/** * @file * Contains Drupal\mymodule\TwitterService. */

namespace Drupal\mymodule\Twitter;

class TwitterService {

public function getData($username) { $api_key = urlencode(‚YOUR_TWITTER_API_KEY‘); $api_secret = urlencode(‚YOUR_TWITTER_API_SECRET‘); $auth_url = ‚https://api.twitter.com/oauth2/token‘;

$data_username = $username;
$data_count = 10;
$data_url = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
$api_credentials = base64_encode($api_key.':'.$api_secret);

$auth_headers = 'Authorization: Basic '.$api_credentials."\r\n".
  'Content-Type: application/x-www-form-urlencoded;charset=UTF-8'."\r\n";

$auth_context = stream_context_create(
  array(
    'http' => array(
      'header' => $auth_headers,
      'method' => 'POST',
      'content'=> http_build_query(array('grant_type' => 'client_credentials', )),
    )
  )
);

$auth_response = json_decode(file_get_contents($auth_url, 0, $auth_context), true);
$auth_token = $auth_response['access_token'];

$data_context = stream_context_create( array( 'http' => array( 'header' => 'Authorization: Bearer '.$auth_token."\r\n", ) ) );

$data = json_decode(file_get_contents($data_url.'?count='.$data_count.'&screen_name='.urlencode($data_username), 0, $data_context), true);

return $data;

}

public function renderData($username) { $data = $this->getData($username);

$tweets = [];
foreach ($data as $value) {
  $tweets[] = $value['text'];
}

return '<div>' . implode('</div><div>', $tweets) . '</div>';

} }

Hier passiert eigentlich nicht viel. In der Klasse gibt es 2 Methoden – getData() und renderData(). getData() beinhaltet einfach den Code, den man braucht, um die Twitter-API abzufragen. Hier müsste man auch die entsprechenden Consumer Key (API Key) und Consumer Secret (API Secret) einsetzen. Die Methode bekommt auch $username als Parameter. Die Zuweisung wird in unserem Controller passieren, den wir noch nicht geschrieben haben. Die $data-Variable, die zurückgegeben wird ist einfach ein Array mit etlichen Infos über die letzten 10 Tweets von einem Benutzer. Um das relativ gut anzeigen zu können existiert die 2. Methode in der Klasse- renderData(). Ja, diese Funktion ist zielmlich einfach, aber für unser Beispiel ausreichend.

Durch die Erstellung der Twitter-Service-Klasse haben wir den Code sauber separiert und anderen Entwicklern die Möglichkeit gegeben, diesen bei Bedarf per Dependency Injection an anderen Stellen einzusetzen. Das ist von Vorteil, denn unsere Twitter-Funktionalität ist nun abgekapselt und könnte sogar in andere PHP-Projekte Verwendung finden. Es ist auch gut, solchen Code, der ganz spezifische Anforderungen erfüllt, getrennt zu haben und nicht in unseren Route-Controller zu finden, welcher eigentlich nur Aufgaben zu Delegierung haben sollte.

Service per Dependency Injection aufrufen

Was uns noch fehlt ist die Implementierung unseres Controllers (siehe Schritt „Eine neue Route definieren“). Schauen wir uns einfach zuerst den Code an (Datei /src/Controller/TwitterController.php):

namespace Drupal\mymodule\Controller;

use Drupal\Core\Controller\ControllerBase; use Symfony\Component\DependencyInjection\ContainerInterface;

class TwitterController extends ControllerBase {

protected $twitterService;

public function __construct($twitterService) { $this->twitterService = $twitterService; }

public static function create(ContainerInterface $container) { return new static( $container->get(‚mymodule.twitter_service‘) ); }

public function feed($username) { $data = $this->twitterService->renderData($username);

$content = array(
  '#markup' => $data,
);

return $content;

}

public function setTitle($username) { return ‚Latest 10 tweets of ‚ . $username; } }

Die wesentliche Änderung zu unserer einfachen HelloWorldController-Klasse aus dem vorherigen Beitrag ist die Benützung von Symfony’s Komponente ContainerInterface. Das ist notwendig, damit wir in der create()-Methode in der Lage sind, Objekte aus anderen Klassen zu instanzieren. So können wir diese Objekte dann in der __construct()-Methode zuweisen und vollständig benützen (siehe die erste Zeile in der feed()-Methode). Ziemlich klar und sauber, oder?

Fazit

Schritt nach Schritt werden die neuen Konzepte in Drupal 8 klarer. Services machen es einem relativ leicht ganze Objekte getrennt zu warten und diese somit für andere Projekte (nicht unbedingt Drupal-Projekte) wieder zu verwenden. Durch deren Registrierung zum Symfony’s ServiceContainer ist es zudem sehr leicht die Eigenschaften und Methoden des geladenen Objekts an beliebigen Stellen einzusetzen.