Einstieg in Sass und Compass bei eigenen Projekten

Im vierten Quartal 2011 bin ich auf Sass bzw. Scss aufmerksam geworden, doch den ersten Einsatz dieser Technik habe ich dann bis Ende 2012 verschleppt.

Die Möglichkeiten die Sass bietet hatten mich damals schon direkt überzeugt, aber die Sorge, dass man in einem echten Kundenprojekt sich mit den neuen Möglichkeiten verzettelt waren auch berechtigt. Beim ersten Projekt hatten nicht alle Entwickler die gleiche Wissensbasis zu Sass und dadurch kam es häufig dazu dass die Kompilierung nicht überall gleich gut funktionierte, oder dass Änderungen direkt in die css-Datei geschrieben wurden und dann wieder verloren gingen, bzw. dank SVN mühsam in die Sass Datei übertragen werden konnten.

Beim zweiten Projekt fühlte man sich soweit, dass ein CSS Framework als Ruby Gem genutzt wurde und auch hier war die Gefahr des Vergaloppieren groß.

Mittlerweile schreibe ich nur noch Sass und habe Wege gefunden und beschreibe im folgenden Wege wie man die Technologie einem ganzen Team mit gemischten Hintergrund (Designer, Markup Artist, Entwickler) zugänglich machen kann.

 

Schritt 1: Einrichtung von Sass und Compass auf allen Arbeitsplätzen

Auf der Sass Installationsseite und der Compass Seite wird die Installation für alle gängigen Betriebssysteme (Win, Mac, Linux) erklärt. Vom Einsatz eines GUI-Tool rate ich jedem ab, mit einer geschickten Konfiguration kann man seinen gewohnten Arbeitsablauf (IDE/Editor usw.) beibehalten und ein kleines Terminal Fenster erledigt alle Sass-Aufgaben.

Am Ende von Schritt 1 muss auf jedem Arbeitsplatz der Befehl compass watch ausführbar sein, auch auf dem Entwicklungssystem und dem Produktivsystem.

 

Schritt 2: Konfiguration des Projektes

Das Code Repository muss so angelegt sein, dass jedes Teammitglied, nach einem Checkout im Wurzelverzeichnis des Projektes den Befehl compass watch aufrufen kann und ab diesem Zeitpunkt sich wieder der Sass Entwicklung widmen kann.

Dazu gesellt sich die config.rb an die Seite des Entwicklers, eine simple Textdatei im Wurzelverzeichnis in der ein paar Variablen festgelegt werden:

http_path = "/"
css_dir = "css" # Verzeichnis in dem nachher das CSS laden soll: zum Beispiel "webroot/css"
sass_dir = "sass" # Verzeichnis in dem die scss Dateien liegen
images_dir = "img"
javascripts_dir = "js"

output_style = (environment == :production) ? :compressed : :expanded # Steuerung über das Format des erzeugten CSS

Die letzte Zeile hat es in sich, mit folgenden compass watch Befehlen, kann hier direkt minifiziertes CSS für das Produktivsystem oder lesbares CSS für die Entwicklung erzeugt werden:

compass watch -e production --force // erzeugt miniziertes CSS
compass watch // bringt menschenlesbares und debugbares CSS zu Tage

Schritt 3: Arbeiten mit Sass

Nachdem diese Hürde genommen ist kann die Arbeit mit Sass durch starten. Die grundlegende Technik (Schachteln von CSS Regeln) muss jedem Teammitglied erklärt werden sowie die Funktion der &– und @extend Funktion. Eine Sass Schulung wie sie zum Beispiel bei Code School angeboten wird kann hier hilfreich sein. Wie bei jeder neuen Technik unterscheiden sich die Lernkurven innerhalb eines Teams und daher sollte man schauen dass die Technik langsam Einzug hält und nicht zu abgefahrene Nutzung von @mixins und @functions Mitglieder ausschließt.

Ein nettes Gimmick während der Entwicklung bietet auch die // und /* ... */ Kommentarfunktionen in Sass. Der // Kommentar, weist den Compiler an, diese Zeile zu ignorieren, während der /* ... */ Kommentar in der kompilierten Datei erscheint.

Tipp: Comeback der @import Regel

Jeder Web Performance Optimierer hasst die @import Regel die immer noch in CSS Dateien vorkommt. Doch bei Sass feiert diese Regel zurecht ein Comeback. Der Compiler von Sass erkennt die @import Aufrufe und kopiert daraufhin die genannte Datei in die zu kompilierende Scss Datei, bevor er sie dann komplett kompiliert.

Dadurch kann ein Projekt beliebig viele verschiedene Sass Dateien benutzen, die in der Ausspielung alle in einer CSS Datei zusammengefasst sind. Wenn das ganze noch minifiziert ist, ist ein großer Schritt in der WPO einfach so erledigt.

// Beispiel app.scss
@import "video";
@import "bootstrap";

// ab hier folgen eigene Style Definitionen
.. {
 &:...{

 }
 .. {

 }
}

Mit diesen Angaben sucht der Compiler nach folgenden Dateien: video.scss, _video.scss, bootstrap.scss, _bootstrap.scss und bindet diese im Erfolgsfall ein. Der Unterstrich im Dateinamen weist den Compiler außerdem noch dazu an, die Datei nur einzubetten und nicht selbst zu kompilieren. Ohne Unterstrich enstehen im /css/ Verzeichnis auch die Dateien video.css und bootstrap.css.

Tipp: Einbindung von Frameworks per Sass

Die Technik habe ich im Abschnitt hier drüber erklärt, aber in der Einleitung habe ich erwähnt dass man viele Dinge auch als Ruby Gem in ein Projekt einbinden kann.

Das haben wir bei einem Projekt getan und Foundation 4 eingebunden. Ungeschickt dabei stellte sich heraus, dass alle Teammitglieder auch das Foundation Gem installiert haben mussten und Gem Versionen die nicht der initial Version (mit der das Projekt gestartet wurde) entsprachen, erzeugten fehlerhaftes CSS.

Dank dieser Erfahrung nutze ich mittlerweile immer die Anpassungsfunktion auf der Website eines Framework Anbieters und binde diese Dateien dann fest in das Projekt ein. Ein Update oder eine Erweiterung des Frameworks kann dann immer über diesen Weg erfolgen, was auf den ersten Blick etwas umständlich erscheint, aber dadurch ist es allen Teammitglieder immer möglich auf ihren Geräten das Projekt selbst zu erzeugen und das ist eine Menge wert.

Meine mobile Webseite ist langsam…

Ich wollte eigentlich „Deine mobile Webseite ist langsam…“ schreiben, aber das wäre gemein und vielleicht sogar falsch – und so stimmt der Betreff immerhin.

Bisher habe ich mich immer auf die „Responsive Alles Könner Rahmenwerke“ verlassen wie zum Beispiel Foundation oder Bootstrap und während man damit eine Seite entwickelt sitzt man im Büro, zu Hause oder sonstwo und hat Kabel-LAN, Kabellos-LAN, LTE oder HSDPA am Smartphone und schaut sich immer über diese Breitbänder die eigene Arbeit an.

Es hallt noch durch die Räume dass man vor dem Start der Umsetzung gesagt hat „Man müsse auf niedrig sowie hoch aufgelöste Displays achten“ aber diese Aussage hat man falsch verstanden, oder falsch gemeint.

Weiterlesen

HTML5 Video als Flashfallback

Tja, das Web wäre soweit, aber die User noch nicht. Daher gibt es doch relativ oft die Anforderung einen bestehenden Flashplayer bei Geräten zu ersetzen die kein Flash installiert haben.

Dabei gibt es zwei Dinge zu beachten: Kann der Browser ohne Flash überhaupt HTML5 Video und liefert man die richtigen Codecs aus – sonst schaut der User ja auch wieder in die Röhre.

Wenn zum Beispiel ein IE8 kein Flash installiert hat und man einfach als Fallback den <video>-Tag eingebunden hat ist dem User nicht geholfen.

Weiterlesen

scrollto infinite (mit jquery)

Ob der Begriff „inifinte scroll“ wirklich Sinn ergibt, sei mal dahin gestellt, aber bei Bings oder Googles Bildersuche hat man schon das Gefühl das es unendlich weitergehen wird.

Es geht darum, dass, sobald der Besucher einen bestimmten Seitenbereich erreicht (erscrollt) hat, automatisch weitere Inhalte geladen und in die Seite eingefügt werden – eine Art „Paginierung 2.0“.

Weiterlesen

Find As You Type / Autocomplete im Eigenbau (jQuery & yii)

Wir sind uns wohl alle einig: Ein Textfeld auf der eigenen Homepage das nach jedem Buchstaben, der eingegeben worden ist, eine aktualisierte Ausgabe erzeugt ist schön!
Google nennt sowas „Google Instant“, wir nennen es jetzt mal „Finde beim tippen“-Funktion oder so.

Als Ausgangssituation nehmen wir an, wir haben eine Datenbank mit beliebigen Inhalt (zB Blog Beiträge) und nun soll mit einem Textfeld der Titel der Blogeinträge durchsucht werden. Weiterlesen

Keine IE CSS HACKs mehr

Irgendwann ist es soweit, man muss sich nur noch mit IE Optimierungen beschäftigen und fängt an die speziellen Browser Hacks anzuwenden. Im Endeffekt erhält man am Ende schwer durchschau- und pflegbares CSS und da manche Browserhacks über verschiedene Browser Versionen gültig sind oder der IE8 einem Nutzer vorschlägt in den Kompatibilitätsmodus zu wechseln, ist das alles unoptimal. Abgesehen von der fehlenden Möglichkeit valides CSS zu erstellen.

Die Alternative mittels Conditional Comments CSS Dateien einzubinden finde ich persönlich auch unoptimal, einerseits finde ich den zweiten GET Aufruf für die extra CSS Datei überflüssig und anderseits finde ich es besser innerhalb einer CSS alles zu stylen und wenn möglich auf die Ausnahmen einzugehen.

Weiterlesen

Adobe Flex: HowTo URLLoader [updated]

Auf der Suche wie man Dateien in Flex einlädt, findet man bei Adobe und anderen Seiten häufig folgendes Beispiel:

package {
import flash.display.Sprite;
import flash.events.*;
import flash.net.*;

public class URLLoaderExample extends Sprite {
public function URLLoaderExample() {
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, completeHandler);
var request:URLRequest = new URLRequest(“urlLoaderExample.txt”);
try {
loader.load(request);
} catch (error:Error) {
trace(“Unable to load requested document.”);
}
}

private function completeHandler(event:Event):void {
var loader:URLLoader = URLLoader(event.target);
trace(“completeHandler: ” + loader.data);
}
// alle weiteren Handler gelöscht
}
}// Quelle www.adobe.com

Dies lädt schnell dazu ein, den Code-Schnipsel in den <mx:Script> Tag seiner MXML Datei zu kopieren, per initialize-Event oder creationComplete-Event diese Funktion aufzurufen und man hat sein Datenmodell in der ViewComponente drin. Funktioniert, aber schön ist das nicht!

Ich hätte das beinah auch so gemacht, wenn ich mein Modell nicht in zwei Views brauchen würde, und wenn ich nicht 10 XML Dateien als Modell hätte. Nach langem Suchen wurde ich in meinem Kopf fündig: in der C & OOP Klausur habe ich einem Objekt per Parameter Funktionen übergeben. Also habe ich folgende Storage Klasse geschrieben:
Und da meine erste Version einer kleinen DOS-Attacke glich, habe ich jetzt eine Warteschlange eingebaut, sodass die XML Dateien nacheinander geladen werden und nicht alle gleichzeitig…

package
{
import flash.events.*;
import flash.net.*;

import mx.collections.XMLListCollection;

public class Storage {
[Bindable] public var homeData:XMLListCollection = null;
[Bindable] public var newsData:XMLListCollection = null;

// URLLoader für alle
private var loader:URLLoader = null;

// Lade-Warteschlange
private var queue:Object = new Object();

// singleton
private static var storage:Storage = null;
public static function getInstance():Storage {
if ( storage == null ) storage = new Storage();
return storage;
}

public function loadHomeData(url:String):void {
if ( homeData == null )	load(url, this.setHomeData);
}

public function setHomeData(event:Event):void {
homeData = new XMLListCollection(new XMLList(event.target.data));
// loader Variable null setzen
loader = null;
// naechste Datei laden
loadNext();
}

public function loadNewsData(url:String):void {
if ( newsData == null ) load(url, this.setNewsData);
}
public function setNewsData(event:Event):void {
newsData = new XMLListCollection(new XML(event.target.data).children());
loader = null;
loadNext();
}

private function load(url:String, handlerObject:Function):void
{
// ueberpruefen ob die URL schon in der Warteschlange ist
if ( queue[url] == undefined )  {
queue[url] = {url: url, handlerObject : handlerObject};
}
// Ueberpruefen ob grade ein Ladevorgang aktiv ist
if ( loader == null )  {
loader = new URLLoader();
//configure Listeners
loader.addEventListener(Event.COMPLETE, handlerObject);
// load File
var request:URLRequest = new URLRequest(url);
try {
loader.load(request);
} catch (error:Error) {

trace(“Unable to load ‘” + url + “‘”);
}
}
}

private function loadNext():void {
// ersten Index aus der Warteschlange holen
for ( var i:String in queue ) {
break;
}
// Ueberpruefen ob ein gueltiges Element vorleigt
if ( queue[i] != undefined ) {
// Ladevorgang anstossen
load(queue[i]['url'], queue[i]['handlerObject']);
}
}
}

Nun rufe ich in den Views im initialize-Event

initialize="{Storage.getInstance().loadNewsData('news.xml')}"

auf und nutze das public Object newsData als dataProvider. Damit das ganze übersichtlicher bleibt habe ich mich hier nur auf das Complete Event beschränkt.

Ich habe die load Funktion extra gewrappt, damit ich bei Änderungen in der Storage Klasse nicht alle Views durchwuseln muss.

Playground Zero goes Flex

Im Moment arbeite ich (neben meiner Diplomarbeit, als Ausgleich sozusagen) an einem Relaunch der Playground Zero Webseite. Das hatte ich schon länger vor und ein Webspace Umzug gibt mir nun die Gelegenheit endlich eine Webseite mit dem Flex Framework umzusetzten. Nun man mag darüber streiten wie sinnvoll es ist, eine ganze Seite in Flex umzusetzten, aber da Flex im MVC Flow so wunderschön den Controller übernimmt muss man sich “nur” mit der GUI Programmierung und der Datenmodellprogrammierung rumschlagen und da ist es schön das die Vernetzung von beiden Flex übernimmt.
Das KeyFeature habe ich nun schonmal umgesetzt bekommen, eine Galerie die Bilder/Audio/Video einfach so anzeigt und abspielt, nun fehlt noch der Rest und dann muss das noch unter einen Hut gebracht werden. Entwicklerschätzung: 2 Tage, also wird in zwei Wochen vielleicht wirklich was draus!
Ich werde hier immer Statusberichte geben!