CSS-Präprozessoren
Struktur und Organisation in SASS-Projekten
Alle Webworker, die CSS-Präprozessoren wie SASS nutzen, müssen sich die Frage beantworten: »Wie organisiere ich meine Dateien, um mir das (Arbeits-)Leben so einfach wie möglich zu machen?« Wer als Antwort auf diese Frage irgendwo das Ei des Kolumbus vermutet, wird erstaunt sein über die Vielfalt der Ansätze.
Viele Webworker haben sich hierzu kluge Gedanken gemacht und sind naturgemäß zu unterschiedlichen Ergebnissen gekommen (siehe auch die weiterführenden Links). Was allein deshalb nicht verwundert, da Strukturansätze immer auch unmittelbar die Gestaltungsprinzipien reflektieren, unter deren Prämisse ein Autor seine Styles schreibt. Und die sind oftmals sehr verschieden.
Es führen also mal wieder viele Wege nach Rom. Im Artikel stelle ich meinen Weg vor, Ordner und Dateien in einem SASS-Projekt zu organisieren und freue mich, wenn er Anregung ist, eigene Ideen zu entwicklen.
Viele Fliegen, eine Klappe
Ich vermeide es seit geraumer Zeit, eine individuelle Ordner- und Dateistruktur für neue Projekte anzulegen. Bis zu dieser Entscheidung musste ich meine Entwicklungsumgebung permanent anpassen; die gedankliche Umstellung kostete Zeit und war anstrengend. Im Laufe der kreativen Arbeit an einem Projekt permanent Entscheidungen über Organisation, Dateibenennung etc. treffen zu müssen, hemmt zudem den Arbeitsfluss und ist ineffizient.
Daher habe ich mir eine Mastervorlage erstellt, die seitdem Grundlage für jedes neue Projekt ist. Diese Mastervorlage besteht vorrangig aus einer bestimmten Ordner- und Dateistruktur, die sich bislang in sehr unterschiedlichen Projekten bewährt hat und mir erlaubt, mich ganz auf die Entwicklung des CSS eines Projektes zu konzentrieren. Ich habe mit ihr sowohl sehr einfach gehaltene Webseiten, als auch komplexere Projekte mit mehreren Teilbereichen umgesetzt.
Die Ordner- und Dateistruktur der Mastervorlage
- utilities/
- abstracts/
- base/
- extensions/
- sections/
- website/
- queries/
- vendor/
- overrides/
- _bootstrap.scss
- _website-configuration.scss
- website.scss
Für den Artikel ist die Mastervorlage schon um einen beispielhaften Projektbereich (website
) erweitert. Was genau ein Projektbereich ist und was er enthält, beschreibt der Artikel im weiteren Verlauf.
utilities/
Hier liegen globale Mixins und Funktionen, die übergreifend in allen Projektbereichen einsetzbar sind. Als Teil der Mastervorlage stehen sie in jedem neuen Projekt unmittelbar zur Verfügung.
- utilities/
- _functions.scss
- _mixins.scss
- ...
abstracts/
In diesem Ordner befinden sich ausschließlich Abstraktionen. Eine Abstraktion ist eine Art CSS-Blaupause, die die grundsätzliche Darstellung eines HTML-Elementes festlegt, aber keine Vorgaben zu dessen Aussehen macht. Dieses wird im CSS des jeweiligen Projektbereiches festgelegt. Harry Roberts hat das Prinzip anhand der Nav Abstraktion sehr anschaulich erklärt.
Um von Anfang an für die Einbindung von externen CSS-Frameworks gerüstet zu sein, ist der Hauptordner abstracts/
in zwei Unterordner (base/
, extensions/
) unterteilt. Während die eigenen Abstraktionen ihr Zuhause im Ordner base/
haben, liegen die erweiterten Abstraktionen eines externen Frameworks im Ordner extensions/
. Durch diese Unterteilung des Hauptordners ist die Herkunft einer Datei auf einem Blick ersichtlich. Ein nicht zu unterschätzender Vorteil, wird ein Projekt zu einem späteren Zeitpunkt erneut bearbeitet.
Sehr hilfreich ist, den Namen der im Ordner extensions/
liegenden Dateien mit einem Prefix zu versehen (hier: ext-
). Werden in der Entwicklung Sourcemaps genutzt, ist es so ein leichtes, zwischen Original und angelegter Datei im Browser zu unterscheiden.
- abstracts/
- base/
- _name-der-abstraktion.scss
- ...
- extensions/
- _[ext]-name-der-abstraktion.scss
- ...
Webworker sollten soviel Zeit wie möglich auf die Erstellung eigener Abstraktionen verwenden! Da der Ordner abstracts/
Bestandteil der Mastervorlage ist, profitiert ihr davon in jedem neuen Projekt.
Tipp: Wurde während eines Projekts eine neue Abstraktion erstellt, solltet ihr immer überlegen, ob es ggf. sinnvoll ist, sie in die Mastervorlage zu übernehmen.
Abstraktionen vorbereiten
In jeder dieser Dateien, werden die eigentlichen Deklarationen mit einer @if
Kontrollanweisung umschlossen, die so aussehen sollte:
- @if $use-abstract-[name-der-abstraktion] == true {
- ... Deklarationen ...
- } // eol condition
bzw. für erweiterte Abstraktionen externer Frameworks:
- @if $use-abstract-[ext]-[name-der-abstraktion] == true {
- ... Deklarationen ...
- } // eol condition
Wozu diese Kontrollanweisungen genutzt werden, wird zu einem späteren Zeitpunkt im Abschnitt zur Konfigurationsdatei erklärt.
sections/
Der Ordner sections ist das Herz eines Projektes. In ihm liegt mindestens ein Bereichsordner (im Artikel: website
) oder beliebig viele. Jeder Bereichsordner enthält sämtliche (S)CSS-Partials, die für die Struktur, das visuelle Erscheinungsbild und das Verhalten auf unterschiedlichen Geräten des jeweiligen Bereiches zuständig sind.
Zu jedem Projektbereich gehört sowohl eine korrespondierende Konfiguration (im Artikel: _website-configuration.scss
) als auch die zu kompilierende Projektdatei (im Artikel: website.scss
). Dies ermöglicht, Stylesheets für jeden einzelnen Projektbereich separat zu erstellen.
Dass das sehr sinnvoll sein kann, macht folgendes Beispiel deutlich: Der Kunde möchte für das Weihnachtsgeschäft einige Kampagnenseiten lancieren, die sich in Struktur und visueller Gestaltung signifikant von der generellen Webseite unterscheiden und unter jeweils eigenständigen Domains laufen sollen. Dies könnte mit dem schon vorhandenen Projektbereich website
realisiert werden. Das aber hätte gleich mehrere Nachteile: Die Konfigurationsdatei würde unübersichtlich, dadurch schwer zu verwalten und das generierte CSS würde weder für die Webseite noch die Kampagnenseiten optimal. Beide Seitenbereiche würden Styles laden, die nicht genutzt werden.
Der deutlich bessere Weg ist das Anlegen eines zusätzlichen, neuen Bereich, der hier naheliegend campaigns genannt wird. Die Projektstruktur würde anschließend so aussehen:
- ...
- sections/
- website/
- queries/
- campaigns/
- queries/
- ...
- _campaigns-configuration.scss
- campaigns.scss
- _website-configuration.scss
- website.scss
Media Queries
Jeder Bereichsordner beinhaltet einen Unterordner mit Namen queries/
. Wie unschwer zu erraten, werden hier die media queries für den jeweiligen Bereich deklariert. Werden Media Queries in einer separaten Datei auf Ebene des Projektbereichs definiert, kann für jeden Teil des Projekts entschieden werden, wie er sich auf unterschiedlichen Geräten verhält und aussieht. So lange es in SASS nicht möglich ist Media Queries zu gruppieren, bevorzuge ich das Arbeiten in eigenständigen Dateien.
- sections/
- [name-des-projektbereichs]/
- ...
- queries/
- ...
vendor/
Im globalen Ordner vendor/ liegen CSS-Dateien externer Bibliotheken. Damit der CSS-Präprozessor diese Dateien einbinden kann, wird das Suffix jeweils von .css
auf .scss
geändert. In der _overrides.scss
Datei werden die Styles der externen Bibliotheken bei Bedarf überschrieben. So gehen die Änderungen nicht verloren, wenn die originalen CSS-Dateien dieser Bibliotheken aktualisiert werden.
- vendor/
- // Bsp. prism.css => prism.scss
- prism.scss
- ...
- overrides/
- _overrides.scss
_website-configuration.scss
Die _[name-des-bereiches]-configuration.scss
ist die Schaltzentrale eines jeden Projektbereiches (im Artikel: website
). Ausschließlich hier werden die im jeweiligen Projektbereich genutzten Variablen deklariert. Die Datei teilt sich in mindestens drei Bereiche auf (zu viele kann es prinzipbedingt nicht geben. Seid kreativ!). Ein paar Tipps:
-
Farben
Die Definition von Farben sollte in zwei Bereiche unterteilt werden. Der Erste enthält Variablen, deren Benennung sich nach ihrem Wert richten (Bsp.
$nearlyWhite: #f1f1f1;
). Im zweiten deklariert ihr die Variablen, deren Name das widerspiegelt, wofür sie eingesetzt werden (Bsp.$pageBackgroundColor: $nearlyWhite!default;
). Diese – und nur diese – werden im CSS genutzt. Was zuerst etwas umständlich wirkt, ist tatsächlich der Schlüssel zur Vermeidung von semantischen Sackgassen. -
Interface
Das visuelle Erscheinungsbild seitenübergreifender Elemente (bspw. Schatten, Rahmen etc.) ist in der Regel überall identisch. Sie sind somit perfekte Kandidaten um als Konfigurations-Variablen deklariert zu werden. Teures Nachjustieren kann vermieden werden, indem zu Beginn des Projektes überlegt wird, für welche Elemente/Attribute eine Abstrahierung als Variable sinnvoll ist.
-
Includes
Wart ihr fleißig, liegen im Ordner abstracts/ (bzw. dessen Unterordnern) viele Dateien mit Abstraktionen. Um zu vermeiden, die globale
_bootstrap.scss
Datei (siehe nachfolgender Abschnitt) immer wieder erneut anpassen zu müssen, sobald eine neue Abstraktion erstellt worden ist, werden diese Dateien dort per Globbing (Sass Plugin) importiert. Was dazu führt, dass grundsätzlich alle – auch die im CSS nicht genutzten Abstraktionen – im generierten CSS auftauchen und die Dateigröße des kompilierten Stylesheets dadurch unnötig aufblähen.Wie im Abschnitt zu den Abstraktionen erwähnt enthält jede dieser Dateien eine
@if
Kontrollanweisung: Zeit, sie zu nutzen! Es werden nur die Variablen explixit auftrue
gesetzt, deren Abstraktionen auch tatsächlich im CSS genutzt werden.Tipp: Unabhängig davon, ob sie eingesetzt werden oder nicht: Es müssen grundsätzlich alle im Ordner
abstracts/
vorhandenen Abstraktionen in der Konfigurationsdatei definiert werden, da der Compiler sich sonst über nicht definierte Variablen mokiert.
- /**
- * $CONFIGURATION
- * Section: website
- */
- /**
- * $ACOLORS
- * Variablen bezogen auf ihren Farbwert
- */
- $nearlyWhite: #f1f1f1;
- ...
- /**
- * $COLORS
- * Variablen bezogen auf ihren Verwendungszweck
- */
- $pageBackgroundColor: $nearlyWhite !default;
- ...
- /**
- * $INTERFACE
- * Rahmen, Schatten, Rundungen, Schriftgrößen, Basismaße etc.
- */
- $borderRadius: 5px !default;
- ...
- /**
- * $INCLUDES
- * Listing aller verfügbaren Abstraktionen
- * Inkludiere (value => `true`) nur die Abstraktionen,
- * die auch tatsächlich im Projekt genutzt werden
- *
- * bool true|false(Standard)
- */
- $use-abstract-[name-der-abstraktion]: false !default;
- $use-abstract-ext-[name-der-abstraktion]: false !default;
- ...
_bootstrap.scss
Die globale _bootstrap.scss
Datei (die nichts mit dem gleichnamigen CSS-Framework zu tun hat) importiert alle Dateien, die keinen unmittelbaren Bezug zu einem dezidierten Projektbereich haben. Sie ist Teil der Mastervorlage. Auch werden hier die Styles der externen Bibliotheken inklusive der _overrides.scss
Datei eingebunden.
Wichtige Regel: In der Bootstrap-Datei werden keine CSS-Styles deklariert! Nur so bleibt der globale Charakter der Bootstrap-Datei erhalten.
- /**
- * $BOOTSTRAP
- * Section: global
- */
- // Helfer wie functions, mixins importieren
- @import "utilities";
- // Es werden nur die Abstraktionen importiert deren Wert in
- // der `_website-configuration.scss` auf `true` gesetzt ist
- @import "abstracts/base/*";
- // Abgeleitete Abstraktionen aus Drittanbieter-Frameworks
- // einzeln importieren. "ext-" als Datei-Prefix verwenden
- @import "abstracts/extensions/ext-[name-der-abstraktion]";
- ...
- // Vendor styles importieren
- @import "vendor/[name-der-datei]";
- // Vendor overrides Partial importieren
- @import "vendor/overrides/overrides";
website.scss
Hier nun kommt Dampf in den Kessel. Die website.scss
ist die einzige Datei, die kompiliert und später im HTML verlinkt wird. Auch hier gilt: Vermeidet an dieser Stelle das Definieren von Styles! Diese gehören ausschließlich in die Dateien des jeweiligen Bereichsordners.
Die Reihenfolge der Importe ist relevant. In einem ersten Schritt wird die Konfiguration des Projektbereichs geladen, anschließend die _bootstrap.scss
, die die globalen Funktionen und Abstraktionen sowie die Vendor-Styles einbindet und bereitstellt. Die bereichsbezogenen Partials werden zum Schluss importiert.
- /**
- * $COMPILE
- * Section: website
- */
- // Projektkonfiguration
- @import "website-configuration";
- // Globaler bootstrap
- @import "bootstrap";
- // Section styles importieren
- @import "sections/website/*";
- // Media queries importieren
- @import "sections/website/queries/*";
Wie war das nochmal mit dem Ei?
Zu guter Letzt möchte ich dazu ermutigen, unterschiedliche Ansätze zur Strukturierung – damit ist auch der hier vorgestellte gemeint – immer wieder mit Blick auf die eigene Arbeitsweise und dem eigenem Arbeitsumfeld kritisch zu hinterfragen. Es gibt keinen allgemeingültigen richtigen Ansatz. Die Entscheidung für eine bestimmte Art der Organisation ist und sollte durchaus sehr subjektiv sein. Fühlt sich die gewählte Struktur für mich richtig, fühlt sie sich natürlich an? Nur wenn dies der Fall ist, wird man effektiv, weil ohne innere Widerstände, arbeiten können.
Weiterführende Links
- Artikel über Organisation und Strukturierung von CSS/SASS
- Artikel über Organisation und Strukturierung - SASS Homepage
- SMACSS, Ansatz, CSS schreiben und organisieren
- SASS Globbing Plugin
- Artikel von Harry Roberts: die Nav-Abstraktion
- Artikel zu SASS Globbing
- Artikel über effiziente Nutzung von Variablen mit SASS
- SASS, CSS Sourcemaps in Google Chrome
- Firefox Plugin für SASS
- Github Issue Eintrag zur Gruppierung von Media Queries
Kommentare
Marc Mintel
am 11.12.2013 - 10:18
Hallo,
sehr schöner Artikel! Aber warum werden Mediaqueries in separaten Dateien gespeichert und nicht verschachtelt in den jeweiligen Klassen?
Viele Grüße
Marc
Olaf Gleba
am 11.12.2013 - 13:25
Hallo Marc,
aus Gründen der effizienteren Wartbarkeit während der Entwicklung eines Projektes . Und auch um Redundanzen von @media Direktiven zu vermeiden (was in Zeiten von gzip für mich weniger mit Dateigröße zu tun hat, sondern eher mit meiner Auffassung, wie effizienter Code aussehen sollte).
Organisiere ich Media Queries händisch in einem eigenen Partial, behalte ich zu jeder Zeit den Überblick über die in verschiedenen Media Query Direktiven genutzten Klassen/Deklarationen. Anstatt mich durch ggf. unzählige, verteilte Zeilen in u.U. vielen Partials wühlen zu müssen (ich kommentiere übrigens an jeder Klassendeklaration innerhalb einer @media Direktive den Kontext, in dem die originale Klasse zu finden ist. Erleichtert die schnelle Zuordnung zusätzlich) .
Ob man Inline Media Queries oder eigene Partials für die Queries einsetzt, ist auch stark von der eigenen Arbeitsweise abhängig. Je modularer man arbeitet, desto weniger machen Inline Media Queries aus meiner Sicht Sinn. Baue ich Komponenten, die in unterschiedlichen Kontexten einer Seite identisch in Funktion und Verhalten sein sollen, macht ein Inline-Ansatz durchaus Sinn.
gruss
Olaf
Philip
am 06.01.2014 - 22:46
Hi, zur Vermeidung von media query Redundanzen bin ich gestern auf folgendes Grunt-Plugin gestoßen: https://github.com/buildingblocks/grunt-combine-media-queries/blob/maste...
Tim
am 11.01.2014 - 15:26
Sehr guter Beitrag. auch wenn ich meine persönliche SASS-Ordnerstruktur anders aufgebaut habe. Das Globbing-Plugin will bei mir nur überhaupt nicht. Weder per Sass-Befehl
sass -r sass-globbing --watch sass/website.scss:style.css
, noch per Compass und einemrequire 'sass-globbing'
in der config.rb. Ich bin unter Windows unterwegs. Für Hilfe wäre ich sehr dankbar.Olaf Gleba
am 12.01.2014 - 00:45
Hallo Tim,
in Hinblick auf Windows als Plattform kann ich dir leider nicht weiterhelfen. Da es aus deinem Kommentar nicht ganz deutlich wird: Du hast das Gem als solches installiert?
[code]gem install sass-globbing[/code]
Tim
am 12.01.2014 - 14:23
Ja, habe ich. Ich kenn mich mit Ruby nicht so aus, aber das require 'sass-globbing' funktioniert wohl nicht. Hab auch nach vielem googlen nichts gefunden. Es liegt wohl irgendwie am Pfad. Wenn ich diesen komplett eingebe (require 'E:\Program Files\Ruby\lib\ruby\gems\1.9.1\gems\sass-globbing-1.1.0\lib\sass-globbing') kommt zwar keine Fehlermeldung mehr, aber Globbing funktioniert trotzdem nicht und spuckt eine Fehlermeldung aus.
Olaf Gleba
am 14.01.2014 - 00:19
Falls du nicht weiter kommst und du davon ausgehst, dass du alles korrekt gemacht hast, wäre mein Tipp im Issue Tracker das Problem zu schildern: https://github.com/chriseppstein/sass-globbing/issues
Philip
am 22.01.2014 - 18:15
Hi, in welche Datei würdet ihr Compass-Mixins importieren? Für mein Empfinden würde ich im utillities Ordner eine _compass.scss anlegen und dort alle Mixins reinpacken.
Jens Grochtdreis (Webkraut)
am 23.01.2014 - 07:23
Compass-Mixins werden nirgends reinkopiert, sondern einfach nur Compass (am Besten global alles) zu Beginn der zentralen Datei mit der import-Direktive importiert. Compass ist so komplex, das kannst und willst Du nicht auseinandernehmen.
Olaf Gleba
am 23.01.2014 - 10:33
Wie Jens schon sagt, importierst du compass, respektive die compass partials, die die bereitgestellten mixins und Funktionen liefern, am besten global. In der hier vorgestellten Struktur böte es sich an, sie als erste Import Direktive in der
_bootstrap.scss
einzubinden.Philip
am 25.02.2014 - 19:50
Wie nutzt ihr die _overrides.scss? Wenn ich z. B. Farb-Angaben überschreiben möchte, muss ich mit !important arbeiten, da die Farb-Variablen gemäß Artikel mit !default versehen werden und somit in der _overrides.scss nicht greifen.
Lässt man !default in der _website-configuration.scss besser weg, um sie später nicht mit !important überschreiben zu müssen?
Olaf Gleba
am 18.03.2014 - 10:15
Die _overrides.scss ist im beschriebenen Ansatz ausschließlich dafür zuständig, eingebundene vendor styles zu überschreiben. Nicht, um Farben der Konfiguration zu überschreiben. Alles was mit Farben zu tun hat, sollte in der jeweiligen _xxx-configuration.scss stehen. Brauche ich eine neue/angepasste Farbe, deklariere ich sie als Variable dort.
Trotzdem danke für den Fingerzeg zu "warum haben die Farben in der Konfiguration ein !default". Ein "!default" brauche ich in dem hier im Artikel vorgestellten Ansatz nicht. Das stammt aus meiner realen Mastervorlage, in der ich ggf. vor der xxx-configuration.scss noch Themes-Stylesheets einbinde, in der (gleichnamige) Variablen definiert sind, die beim Vorhandensein die Werte der Variablen in der Konfiguration überschreiben. Ist sinnvoll, damit Dritte/Kunden ggf. Ein paar Farbwerte ändern können, sonst aber gefälligst die Finger von Struktur und Konfiguration lassen ;-)
Die Kommentare sind geschlossen.