CSS-Entwicklung: SASS im Einsatz
Welche Türen stehen mit vorkompilierten Stylesheets offen? Wie lassen sich Variablen und Vererbung nützlich in eigene Projekte integrieren? Und womit reizt man SASS aus? Nach der Einführung [1] in die Grundfunktionen von SASS [2] kommen nun also Erweiterungen, Mixins und Compass zum Einsatz.
Erweiterungen
Wie oft kommt es vor, dass verschiedene Elemente auf einer Seite sich zwar ähneln, nicht aber gleichen? Buttons zum Beispiel, oder Hinweisboxen. Letztere sind auch Ausgangspunkt des folgenden Beispiels: Neben allgemeinen Hinweisboxen soll es auch eine Variante für Fehlermeldungen geben.
Separate Fehler- und Erfolgsmeldungen
.message { background-color: lighten($main-color, 30%); border: 0.1em solid $main-color; padding: 1em; margin: 1em 0; p:last-child { margin-bottom: 0; } } .error { @extend .message; background-color: lighten($error-color, 30%); border-color: $error-color; }
Der erzeugte CSS-Code erhält nun keine Mehrfach-Deklarationen, sondern erweitert dank dem „@extend“-Element die entsprechenden Selektoren und lagert nur die Ergänzungen aus.
Erzeugter CSS-Code
.message, .error { background-color: #42ff62; border: 0.1em solid #00a81c; padding: 1em; margin: 1em 0; } .message p:last-child, .error p:last-child { margin-bottom: 0; } .error { background-color: #ff9999; border-color: red; }
Generell lässt sich dies auch im HTML-Code erreichen, indem die Fehlerboxen einfach mit den Klassen „message“ und „error“ ausgezeichnet sind. Das funktioniert in einem so einfachen Beispiel ziemlich ähnlich, in größeren Projekten kann es aber von Vorteil sein, den HTML-Code unberührt zu lassen. Zudem erleichtert die zentralisierte Formatierung im CSS-Code die Wartung.
Erweiterungen mit Mixins erweitern
Mixins sind im Grunde Code-Blöcke, die man in der SASS-Datei an beliebiger Stelle aufruft. Ihr Inhalt wird dann an dieser Stelle in die CSS-Datei geschrieben. Der Clou ist aber die Tatsache, dass ein Mixin, ähnlich einer Funktion in echten Programmiersprachen, mit übergebenen Variablen umgehen kann, die dann in diesem Mixin zur Verfügung stehen.
Mixins definieren
@mixin box-shadow($value) { -moz-box-shadow: $value; -webkit-box-shadow: $value; box-shadow: $value; }
Dass dieses Mixin Tipparbeit erspart, ist offensichtlich: Die Vendor-Prefixe für eine CSS-Eigenschaft, in diesem Fall den Schatten, sind einmal definert und müssen nicht bei jedem Aufkommen erneut getippt werden. Entsprechend kommt das Mixin fortan in Stylesheets zum Einsatz. Das funktioniert über „@include“ und den Namen des Mixins.
Mixins einbinden
.box { @include box-shadow(0.1em 0.1em 0 rgba(0, 0, 0, 0.4)); } .another-box { @include box-shadow(inset 0 0.2em 0.2em -0.15em rgba(0, 0, 0, 0.3)); }
Das Mixin generiert nun automatisch die entsprechenden Vendor-Prefixe. Soll später ein neuer Browser hinzukommen oder ändern sich die Prefixe, reicht ein Ändern des Mixins und das Generieren einer neuen CSS-Datei. Alle Vorkomnisse sind dann angepasst.
Zugriff auf Eltern-Elemente
Manchmal reicht die einfache Verschachtelung in die Tiefe nicht aus. Denn auch der umgekehrte Weg, also der Zugriff auf das übergeordnete Element, ist gelegentlich notwendig.
Herkömmliche Verschachtelung ohne Eltern-Zugriff
a { :link, :visited { ... } :hover, :focus { ... } }
Das Ergebnis dieses Beispiels enthält ein unschönes Leerzeichen zwischen den Elementen im Selektor, was für die Definition von Links falsch ist. Mit dem Et-Zeichen (auch: kaufmännisches Und) fällt dieses Leerzeichen weg und der direkte Zugriff auf das Eltern-Element ist möglich.
Zugriff auf das Eltern-Element
a { &:link, &:visited { ... } &:hover, &:focus { ... } }
In dieser Anwendung ist das nur wenig sinnvoll, da die ausgeschriebene CSS-Syntax kürzer ist als der SASS-Code. An anderer Stelle kann dieser Selektor aber durchaus hilfreich und sein Einsatz entsprechend nützlich sein.
Modernizr
Etwa beim Einsatz von Modernizr. Dieser erweitert das HTML-Element der Webseite um eine Vielzahl an Klassen, die anzeigen, welche modernen Funktionen der Browser mitbringt. Zum Beispiel weisen „.cssgradients“ oder eben „.no-cssgradients“ darauf hin, ob ein Browser Farbverläufe mit CSS unterstützt.
Hebt sich eine Box etwa nur durch einen Schatten vom Hintergrund ab, kann ein Fallback für ältere Browser notwendig sein. Hier kommt der Parent-Selektor mit dem „&“ sehr gelegen.
Da der Eltern-Selektor an beliebiger Stelle einsetzbar ist, bleiben die Definition
und das Fallback beisammen. Das hilft gerade in größeren Projekten mit
vielen Frontend-Entwicklern oder bei solchen, die regelmäßige Wartung
erfordern. In einer fernen Zukunft, in der Fallbacks wegen durchgängiger
Browserunterstützung nicht mehr notwendig sind, entfernt man sie
einfach wieder. Sie sind ja leicht zu finden.
Fallback dank Modernizr
.message { boxshadow: 1rem 1rem 1rem rgba(0, 0, 0, 0.5); .no-boxshadow & { border: 5px solid#CCC; } }
Semantische CSS-Frameworks
Das größte Problem an CSS-Frameworks ist das Markup. Ob 960gs [3], YAML [4] oder eines der unzähligen anderen Frameworks – die nötigen CSS-Klassen im HTML-Code sind semantisch nicht sinnvoll und dienen ausschließlich der Präsentation. Denn die Klasse „.grid_2“ im 960gs sagt genausowenig über den Inhalt aus wie „.ym-col3“ in YAML.
Durch das Nutzen von SASS lässt sich das Frontend-Pferd andersherum aufzäumen. So lassen sich Klassen und IDs ganz nach eigenem Geschmack und gegebenem Inhalt vergeben und mit den (Grid-) Frameworks verknüpfen. Soll der Inhaltsbereich beispielsweise in einem Grid zehn Spalten einnehmen, so verzichtet man im Markup auf „<div class=„content grid_10“>“. Stattdessen bleibt es bei „<div class=„content“>“ und die entsprechenden Auszeichnungen werden über das Stylesheet zugewiesen.
CSS-Klassen einheitliche Namen zuweisen
.content { @include grid_10; }
So bleibt das Markup sauber. Doch für jede Grid-Klasse eine SASS-Zuweisung zu schreiben, widerspricht der Logik geringeren Aufwands. Dank der Rechenfunktionen in SASS lassen sich diese aber auch in einem Mixin vereinen, das die Positionsberechnung durchführt.
Das folgende Beispiel ist natürlich sehr vereinfacht, es fehlen die Abstände
zwischen den Spalten in der Berechnung und alles was sonst noch daran
hängt. Doch dank derart dynamischer Berechnungen lässt sich etwa das Framework 960gs
auf 100 Zeilen SASS eindampfen [5].
Ein einfaches Grid-System
$grid-width: 960px; $num-columns: 12; @mixin grid($column) { $column-width: $grid-width / $num-columns; width: $column * $num-columns; }
Alternativ gibt es auch Grid-Systeme, die von Anfang an auf genau dieser Idee beruhen und direkt für die Nutzung mit Pre-Prozessoren entwickelt wurden, etwa Semantic.gs [6]. Diese Logik führt dann auch dazu, nicht mit Klassen in HTML zu gestalten, sondern stattdessen dort zu gestalten, wo es auch sinnvoll ist: in CSS.
Compass
Das auf 100 Zeilen SASS eingedampfte 960gs ist – zugegeben – kein pures SASS. Es handelt sich dabei um ein Compass-Plugin [7]. Compass ist ein CSS-Projekt-Framework; ganz im Gegensatz zu Layout-Frameworks wie 960gs. Projekt-Frameworks legen SASS sozusagen tiefer und schrauben Spoiler dran. Wofür? Um die Arbeit zu erleichtern.
Compass speichert dafür seine Einstellungen in einer Datei, der config.rb, im Wurzelverzeichnis des Projekts. Darin sind sämtliche Pfade zu relevanten Unterverzeichnissen, etwa dem für Bilder, CSS und JavaScript, abgelegt. So kann Compass direkter bei der Arbeit mit einem Projekt unterstützen, als dies ein einfacher CSS-Pre-Prozessor kann.
Mixins in Compass
Compass bringt bereits zahlreiche Mixins mit. Nach der Installation genügt deshalb ein „@import „compass“;“ am Dateianfang, um unzählige Mixins für zahlreiche CSS3-Funktionen nutzbar zu machen.
Egal ob „border-radius(3px)“, „box-flex(3)“ oder „background-size(100% auto)“ – um Prefixe kümmert sich fortan Compass. Entsprechend gilt die Faustregel, dass CSS3-Anweisungen in „@include anweisung(wert);“ umgeschrieben werden können. Die nötigen Vendor-Prefixe liefert Compass.
Verläufe in Compass
Einen Schritt weiter als das einfache Voranstellen von Browsernamen geht es bei der Nutzung von Verläufen in Hintergründen. Auch hier gibt es einige ältere Browser, die noch eine andere Syntax nutzen. Doch auch diese Variation übernimmt Compass. So lässt sich auch hier mit einer einfachen Schreibweise die Syntax aller Browser abdecken.
Hintergrund-Verläufe mit Compass
header { @include background( linear-gradient(top left, #333, #0C0), radial-gradient(#C00, #FFF 100px) ); }
Bilder mit Compass
Auch für in CSS genutzte Bilder hat Compass einige Funktionen im Gepäck. So ergänzt Compass etwa mit der Funktion „image-url(‚datei.png‘)“ Dateinamen um den richtigen Pfad, den es ja über die Konfigurationsdatei kennt. Das spart Tipparbeit und ermöglicht, das Verzeichnis auch nachträglich noch zu verschieben. Zugegeben, letzteres ist ein eher seltener Anwendungsfall. Trotzdem lohnt der Einsatz, denn außerdem hängt Compass einen Zeitstempel an den Bilderpfad, um die Probleme durch Browser-Caching zu minimieren.
Compass erlaubt es auch, Bildinformationen auszulesen. Um etwa eine Grafik im Hintergrund einzubauen, eignen sich die Funktionen zum dynamischen Auslösen von Breite und Höhe. Auf diese Weise lassen sich später Grafiken austauschen, ohne eine
Änderung am CSS-Code vornehmen zu müssen. Das ist bei einzelnen
Grafiken zwar ein wenig hilfreich, wirklich lohnenswert wird es aber beispielsweise bei einer Logoübersicht oder einer Bildergallerie.
Hintergrundbilder dynamisch einpassen
.logo { background: image-url('logo.png'); width: image-width('logo.png'); height: image-height('logo.png'); }
CSS-Sprites mit Compass
Bei einer CSS-Sprite werden viele
kleine Bilder zu einem einzigen zusammengefügt und anschließend in CSS
über Hintergrund-Positionen angesteuert. So wird nicht für jede
einzelne Datei eine Verbindung zum Server aufgebaut und die Ladezeit der
Webseite dadurch beschleunigt.
Doch wer schon einmal CSS-Sprites für sein Projekt angefertigt hat, weiß, wieviel Aufwand das ist. Und richtig viel Spaß machen Sprites, wenn nachträglich Icons mitten in der Sprite geändert oder ersetzt werden sollen oder sich Dimensionen verändern.
Hier springt Compass in die Bresche und erleichtert solche Anpassungen. Da das Bilderverzeichnis bekannt ist, landen CSS-Sprites, also alle Grafiken, die zusammengefasst werden sollen, einfach in einem Unterordner darin (im Beispiel „icon“). Nun kann Compass eine Sprite erstellen und die notwendigen Klassen im Stylesheet erzeugen. Der Aufwand: zwei Zeilen.
Sprites in Compass
@import 'icon/*.png'; @include all-icon-sprites();
Compass lädt nun alle PNG-Dateien aus dem icon/-Verzeichnis ins Sprite (erste Zeile) und fügt die entsprechenden Klassen hinzu (zweite Zeile). Angenommen, das Sprite besteht aus drei Bildern – new.png, edit.png und delete.png –, so sieht die CSS-Ausgabe der zwei Zeilen entsprechend aus.
CSS-Ergebnis der Compass-Sprites
.icon-sprite, .icon-delete, .icon-edit, .icon-new { background: url('/images/icon-sd26f725180.png') no-repeat; } .icon-delete { background-position: 0 0; } .icon-edit { background-position: 0 -16px; } .icon-new { background-position: 0 -32px; }
Nun also doch wieder auferlegte Klassennamen, die vorher bei den Layout-Frameworks mühsam beseitigt wurden? Auf keinen Fall. Mit einem Mixin lassen sich die einzelnen Grafiken der Sprite mit eigenen Selektoren verwenden. Ändern sich im Projektverlauf die Grafiken, ist dann nicht mehr nötig, als die einzelnen Icons auszutauschen. Den Rest erledigt Compass.
Eigene Namen für Compass-Sprites
.new { @include icon-sprite(new); } .edit { @include icon-sprite(edit); } .delete { @include icon-sprite(delete); }
Auf die Kompilatoren, fertig, los
SASS und Compass können große Hilfen im Alltag sein. Sie dienen dazu, Projekte übersichtlicher zu gestalten und die Wartung zu erleichtern. Doch die angerissenen Themen sind nur einige Ausgangspunkte für die eigene Arbeit mit Pre-Prozessoren. Weitere Schritte können Farbberechnungen sein oder durch die Nutzung von Bedingungen und Schleifen komplexe und projektspezifische Frontend-Lösungen, die das Leben des gemeinen Frontend-Entwicklers deutlich erleichtern. Wie viele der zur Verfügung stehenden Funktionen er dabei nutzen möchte, bleibt natürlich jedem selbst überlassen. Einen Blick sind die neuen Möglichkeiten aber allemal wert.