Webkrauts Logo

Webkrauts Webkrauts Schriftzug

- für mehr Qualität im Web

Struktur und Organisation in SASS-Projekten

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

  1. utilities/
  2. abstracts/
  3.     base/
  4.     extensions/
  5. sections/
  6.     website/
  7.         queries/
  8. vendor/
  9.     overrides/
  10. _bootstrap.scss
  11. _website-configuration.scss
  12. 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.

  1. utilities/
  2.     _functions.scss
  3.     _mixins.scss
  4.     ...

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.

  1. abstracts/
  2.     base/
  3.         _name-der-abstraktion.scss
  4.         ...
  5.     extensions/
  6.         _[ext]-name-der-abstraktion.scss
  7.         ...

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:

  1. @if $use-abstract-[name-der-abstraktion] == true {
  2.     ... Deklarationen ...
  3. } // eol condition

bzw. für erweiterte Abstraktionen externer Frameworks:

  1. @if $use-abstract-[ext]-[name-der-abstraktion] == true {
  2.     ... Deklarationen ...
  3. } // 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:

  1. ...
  2. sections/
  3.     website/
  4.         queries/
  5.     campaigns/
  6.         queries/
  7. ...
  8. _campaigns-configuration.scss
  9. campaigns.scss
  10. _website-configuration.scss
  11. 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.

  1. sections/
  2.     [name-des-projektbereichs]/
  3.         ...
  4.         queries/
  5.             ...

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.

  1. vendor/
  2.     // Bsp. prism.css => prism.scss
  3.     prism.scss
  4.     ...
  5.     overrides/
  6.         _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:

  1. 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.

  2. 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.

  3. 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 auf true 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.

  1. /**
  2.  * $CONFIGURATION
  3.  * Section: website
  4.  */
  5.  
  6. /**
  7.  * $ACOLORS
  8.  * Variablen bezogen auf ihren Farbwert
  9.  */
  10. $nearlyWhite: #f1f1f1;
  11. ...
  12.  
  13. /**
  14.  * $COLORS
  15.  * Variablen bezogen auf ihren Verwendungszweck
  16.  */
  17. $pageBackgroundColor: $nearlyWhite !default;
  18. ...
  19.  
  20. /**
  21.  * $INTERFACE
  22.  * Rahmen, Schatten, Rundungen, Schriftgrößen, Basismaße etc.
  23.  */
  24. $borderRadius: 5px !default;
  25. ...
  26.  
  27. /**
  28.  * $INCLUDES
  29.  * Listing aller verfügbaren Abstraktionen
  30.  * Inkludiere (value => `true`) nur die Abstraktionen,
  31.  * die auch tatsächlich im Projekt genutzt werden
  32.  *
  33.  * bool true|false(Standard)
  34.  */
  35. $use-abstract-[name-der-abstraktion]: false !default;
  36. $use-abstract-ext-[name-der-abstraktion]: false !default;
  37. ...

_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.

  1. /**
  2.  * $BOOTSTRAP
  3.  * Section: global
  4.  */
  5.  
  6. // Helfer wie functions, mixins importieren
  7. @import "utilities";
  8.  
  9. // Es werden nur die Abstraktionen importiert deren Wert in
  10. // der `_website-configuration.scss` auf `true` gesetzt ist
  11. @import "abstracts/base/*";
  12.  
  13. // Abgeleitete Abstraktionen aus Drittanbieter-Frameworks
  14. // einzeln importieren. "ext-" als Datei-Prefix verwenden
  15. @import "abstracts/extensions/ext-[name-der-abstraktion]";
  16. ...
  17.  
  18. // Vendor styles importieren
  19. @import "vendor/[name-der-datei]";
  20.  
  21. // Vendor overrides Partial importieren
  22. @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.

  1. /**
  2.  * $COMPILE
  3.  * Section: website
  4.  */
  5.  
  6. // Projektkonfiguration
  7. @import "website-configuration";
  8.  
  9. // Globaler bootstrap
  10. @import "bootstrap";
  11.  
  12. // Section styles importieren
  13. @import "sections/website/*";
  14.  
  15. // Media queries importieren
  16. @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

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

Permanenter Link

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

Permanenter Link

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...

Permanenter Link

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 einem require 'sass-globbing' in der config.rb. Ich bin unter Windows unterwegs. Für Hilfe wäre ich sehr dankbar.

Permanenter Link

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]

Permanenter Link

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.

Permanenter Link

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

Permanenter Link

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.

Permanenter Link
Jens Grochtdreis

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.

Permanenter Link

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.

Permanenter Link

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?

Permanenter Link

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 ;-)

Permanenter Link

Die Kommentare sind geschlossen.