Webkrauts Logo

Webkrauts Webkrauts Schriftzug

- für mehr Qualität im Web

Komplexe Stylesheets strukturieren

CSS-Modularisierung mit Sass

Komplexe Stylesheets strukturieren

Sass ist eine Meta-Sprache, mit der sich CSS cleverer schreiben lässt. Das neue Sass-Feature der Platzhalter-Selektoren macht insbesondere komplexes CSS wartbarer.

Im letztjährigen Adventskalender stellte ein zweiteiliger Artikel (Teil 1, Teil 2) den CSS-Präprozessor Sass sowie die zugehörige CSS3-Bibliothek Compass vor. Der folgende Artikel baut darauf auf und stellt Neuerungen vor.

Modularisierung mit Platzhalter-Selektoren

Seit Version 3.2, veröffentlicht im August 2012, bietet Sass ein neues Feature, das bei der Strukturierung von umfangreichem CSS hilft: Das Schlüsselwort @extend zusammen mit sogenannten Placeholder Selectors (Platzhalter-Selektoren). @extend alleine wurde bereits letztes Jahr vorgestellt, doch erst in Verbindung mit den neuen Platzhalter-Selektoren entfaltet es seine volle Wirkung.

Platzhalter-Selektoren sind vielseitig einsetzbar. Mit ihnen lassen sich abstrakte Module definieren, auf denen konkrete CSS-Regeln aufbauen können. Dabei werden alle Deklarationen des Moduls auf die konkrete Regel vererbt. Ein einfaches Beispiel in SCSS:

  1. // Generisches Modul für Buttons definieren
  2. %button {
  3.   display: inline-block;
  4.   border: none;
  5.   margin: 0;
  6.   padding: 0 10px;
  7.   background-color: #0cf;
  8.   cursor: pointer;
  9.   color: white;
  10.   text-decoration: none;
  11.   text-align: center;
  12.   font-family: inherit;
  13.   font-size: inherit;
  14.   line-height: 1.5;
  15.   vertical-align: middle;
  16. }

Ein Platzhalter-Selektor beginnt mit einem Prozentzeichen gefolgt von einem Bezeichner. Dieser kann wie z.B. Klassenselektoren aus Buchstaben, Ziffern, Unterstrichen, Bindestrichen usw. bestehen, jedoch nicht aus Leerzeichen. Das erste Zeichen darf keine Ziffer sein.

Das Beispiel enthält eine Regel mit dem Platzhalter-Selektor %button. Der zugehörigen Block enthält eine Reihe von Deklarationen (Eigenschaften plus Werte, siehe CSS-Vokabular). Damit wird ein Modul button definiert. Allein diese Definition hat noch keine Wirkung, sie erzeugt beim Kompilieren (Übersetzen) keinen CSS-Code.

  1. // Konkrete Anwendung: Formular-Submitbuttons
  2. .contact-form .submit {
  3.   // Modul button anwenden
  4.   @extend %button;
  5.   // Weitere Eigenschaften
  6.   border: 1px solid #222;
  7. }
  8.  
  9. // Konkrete Anwendung: Toolbar-Buttons
  10. .toolbar .button {
  11.   // Modul button anwenden
  12.   @extend %button;
  13.   // Weitere Eigenschaften
  14.   border-radius: 2px;
  15.   background-color: #777;
  16. }

Mit @extend %button; wird das Modul nun auf eine konkrete CSS-Regel angewendet. Dadurch werden die Deklarationen des Moduls auf die Treffer der Selektoren .contact-form .submit sowie .toolbar .button angewendet.

Der generierte CSS-Code sieht folgendermaßen aus:

  1. .contact-form .submit, .toolbar .button {
  2.   display: inline-block;
  3.   border: none;
  4.   margin: 0;
  5.   padding: 0 10px;
  6.   background-color: #0cf;
  7.   cursor: pointer;
  8.   color: white;
  9.   text-decoration: none;
  10.   text-align: center;
  11.   font-family: inherit;
  12.   font-size: inherit;
  13.   line-height: 1.5;
  14.   vertical-align: middle;
  15. }
  16.  
  17. .contact-form .submit {
  18.   border: 1px solid #222;
  19. }
  20.  
  21. .toolbar .button {
  22.   border-radius: 2px;
  23.   background-color: #777;
  24. }

Sass zieht alle Regeln, die das Modul button einbinden, zu einer zusammen und reiht die Selektoren aneinander. Der durch @extend vererbte Code wird nur einmal erzeugt und wiederverwendet. Vom abstrakten Modul button ist nach dem Kompilieren von SCSS zu CSS nichts mehr direkt zu sehen.

Komplexe Module

Im obigen Beispiel werden bloß einige Deklarationen vererbt. Innerhalb von %button {…} können jedoch weitere Regeln verschachtelt werden, z.B. für Nachfahrenelemente und Pseudoklassen:

  1. %button {
  2.   // … wie oben …
  3.   &:hover, &:focus, &:active {
  4.     background-color: lighten(#0cf, 10%);
  5.   }
  6.   &:focus, &:active {
  7.     border-color: blue;
  8.   }
  9.   .symbol {
  10.     font-weight: bold;
  11.   }
  12. }

Das Zeichen & bedeutet hier eine Wiederaufnahme des aktuellen Selektors, um diesen zu ergänzen. Die obige Verschachtelung führt dazu, dass drei weitere Regeln generiert werden:

  1. .contact-form .submit:hover, .toolbar .button:hover,
  2. .contact-form .submit:focus, .toolbar .button:focus,
  3. .contact-form .submit:active, .toolbar .button:active {
  4.   background-color: #33d6ff;
  5. }
  6.  
  7. .contact-form .submit:focus, .toolbar .button:focus,
  8. .contact-form .submit:active, .toolbar .button:active {
  9.   border-color: blue;
  10. }
  11.  
  12. .contact-form .submit .symbol, .toolbar .button .symbol {
  13.   font-weight: bold;
  14. }

An die Stelle von %button werden die beiden Selektoren .contact-form .submit sowie .toolbar .button eingefügt. Jetzt ist auch der Name »Platzhalter-Selektor« verständlicher geworden: %button ist ein Platzhalter für alle Selektoren der Regeln, die @extend %button; enthalten.

Das Beispiel zeigt bereits, wie umfangreich der generierte Code wird, wenn die Selektoren komplex werden und ein Modul vielfach angewendet wird. Das ist meist ein Zeichen, dass der Sass-Code vereinfacht werden sollte.

Was ist so nützlich an Platzhalter-Selektoren?

Platzhalter-Selektoren und @extend sind ein leistungsfähiges Werkzeug zur Modularisierung von CSS-Code. Dabei wird das Stylesheet in kleine Bausteine mit einer definierten Aufgabe zerlegt.

Dies hat zahlreiche Vorteile: Code kann wiederverwendet werden, anstatt ihn immer wieder zu verdoppeln – »Don’t repeat yourself« lautet eine Grundregel der Programmierung. Über zentrale Grundmodule kann das Design schnell geändert werden. Die Auswirkungen einer Änderung sind absehbar, da sie diejenigen Regeln betreffen, die das Modul einbinden.

Module sollten möglichst unabhängig von der zugrunde liegenden HTML-Struktur und dem Kontext sein, in dem sie erscheinen. Sie sollten flexibel anwendbar sein, beispielsweise indem Klassen anstatt Element-Typen selektiert werden (z.B. .toolbar .button anstatt .toolbar > a). So lässt sich das HTML ändern, ohne dass notwendig das CSS angepasst werden muss.

@extend-Anweisungen können auch innerhalb von %modul {…} stehen. Damit lassen sich Module auf Basis anderer definieren, z.B. ein Modul %submit-button auf Basis von %button. Zudem sind mehrere @extend-Anweisungen innerhalb einer Regel möglich, um von verschiedenen Modulen zu erben.

Einsatzbereiche von Platzhalter-Selektoren

Unabhängig davon, ob die OOCSS-Regeln (siehe unten) angewendet werden, hilft @extend dabei, modulares und gleichzeitig effizientes HTML und CSS zu schreiben. Platzhalter-Selektoren sind kein Allheilmittel, sondern ergänzen Sass’ bestehende Möglichkeiten zur Strukturierung:

@mixin und @include
Ein Sass-Mixin generiert bei jeder Anwendung neuen Code. Mixins sind durch Parameter konfigurierbar, eignen sich daher zum Erzeugen von spezifischem Code, der sich nicht wiederholt.
@extend mit normalen Regeln
Wenn bereits Regeln existieren, die direkt das HTML formatieren, können diese mit @extend abgeleitet werden. Beispielsweise: .article {…} und .featured-article { @extend .article; … }. Im HTML werden beide Klassen verwendet.
@extend mit Platzhalter-Selektoren
Eignet sich zur Vererbung von rein internen Modulen. Bietet sich für eine ganze Reihe wiederverwendeter Formatierungen an, z.B. visuelle und typographische Grundbausteine, Grid-Layout, Clearfix, barrierefreies Verstecken von Inhalten usw.

Sass und andere Ansätze zur Modularisierung

Verschiedene Webentwickler haben versucht, Richtlinien für wartbares und wiederverwendbares CSS aufzustellen. Das einflussreiche Konzept OOCSS etwa sieht vor, Module hauptsächlich mit Klassenselektoren (z.B. .article {…}) zu definieren und Module durch mehrere Klassen im HTML anzuwenden (z.B. class="article article-featured"). Ziel ist, dass der entstehende CSS-Code selbst bei großem Umfang einfach und konsistent bleibt.

Die meisten Layout- und Grid-Frameworks sowie Baukastensysteme stellen derartige Module bereit. Auch wenn OOCSS-Module möglichst semantische Namen tragen sollen, läuft es in der Praxis darauf hinaus, dass das HTML mit präsentationsbezogenen Klassen vollgestopft wird.

Bei OOCSS werden Module durch mehrere Klassen im HTML kombiniert. Mithilfe von Sass kann dies schon beim Kompilieren geschehen, sodass das HTML übersichtlich bleibt und das entstehende CSS mit möglichst wenigen Wiederholungen auskommt. Mittlerweile hat auch die Erfinderin von OOCSS, Nicole Sullivan, Sass entdeckt und spricht über Möglichkeiten, OOCSS und Sass zu vereinen. Verschiedene Artikel und Präsentationen schlagen eine Umsetzung von OOCSS mithilfe von Sass’ Platzhalter-Selektoren vor.

CSS-Entwicklung auf der Höhe der Zeit

Sass 3.2 bringt weitere Neuerungen mit, die das Schreiben von Stylesheets vereinfachen und die Übersichtlichkeit erhöhen. Darunter der komfortable Gebrauch von Media Queries sowie die Möglichkeit, Blöcke an Mixins zu übergeben. Wie nützlich beides ist, zeigt das Projekt Sass Media Queries.

Während andere Werkzeuge, die die Produktivität und Codequalität im Frontend verbessern, breit akzeptiert sind, so scheiden sich an CSS-Präprozessoren immer noch die Geister. Dabei ermöglichen sie nicht nur wartbareren Code zu schreiben, sondern bringen auch die Diskussion um Strukturierung und Modularisierung voran.

Glücklicherweise wird der Sass-Einsatz dank verschiedener Tools immer einfacher. Ist die anfängliche Hürde überwunden, so wird ein Webworker die Vorteile von Präprozessoren nicht mehr missen.

Weiterführende Links

Kommentare

Jens Grochtdreis

Jens Grochtdreis (Webkraut)
am 07.12.2012 - 09:28

Das Button-Beispiel zeigt schön, welche nette Neuerung seit der Version 3.2 bei Sass mitkommt. Es zeigt allerdings auch, dass die Idee des "semantischen CSS" die Komplexität nur vom HTML in das CSS verlagert. Könnte ich sonst über eine Klasse die hover-Zustände abfackeln, summieren sich die Selektorketten in Deinem Beispiel schon ganz enorm. Und das Beispiel ist gut gewählt, weil so am Ende die Praxis aussieht.

Ich frage mich sowieso, wie man auf die Idee semantischer Klassen kommt, da für die Semantik nunmal HTML zuständig ist.

Es kann also sein, dass mal die Verlagerung der Designentscheidungen über Klassen ins HTML die effektivere Lösung ist und mal die Konzentration im CSS. Sass bietet auf alle Fälle eine sehr gute Möglichkeit, wartbaren Code zu schreiben.

Permanenter Link

Gunnar Bittersmann
am 07.12.2012 - 10:42

„die hover-Zustände abfackeln“
Und dabei nicht den focus verlieren!

„summieren sich die Selektorketten“
Wayne interessierts? Sass ist nicht dazu da, damit das genierierte CSS Schönheitspreise gewinnt.
(Man sollte natürlich trotzdem einiges beachten, damit kein Unfug rauskommt.)

Permanenter Link

Paul
am 07.12.2012 - 16:05

Da die CSS-Klassen in HTML „aufgerufen“ werden, ist es notwendig, dass die Klassennamen semantisch vergeben werden. Natürlich wird dabei „die Komplexität nur vom HTML in das CSS verlagert“. Aber das war ja mal der Sinn hinter CSS, dass HTML reine Semantik sein soll und möglichst wenig komplex! Sonst kommt man schnell dazu, bei einem Redesign nicht nur das CSS sondern auch das HTML ändern zu müssen, weil präsentationsbezogene Klassennamen die Verwaltung des CSS unmöglich machen. Schlechtes HTML und Änderungen daran sind teuer!

Permanenter Link

Paul
am 07.12.2012 - 21:40

Jo. Toll. Jetzt hast du mich aber belehrt!

Permanenter Link

Gunnar Bittersmann
am 09.12.2012 - 14:29

Es war nicht meine Absicht, dich auf die Palme zu bringen.
Und ansonsten stimme ich deinem Posting auch voll zu.

Permanenter Link

Gunnar Bittersmann
am 07.12.2012 - 10:22

Guter Artikel.

„[D]och erst in Verbindung mit den neuen Platzhalter-Selektoren entfaltet [@extend] seine volle Wirkung“ find ich aber etwas übertrieben. Der Unterschied zwischen

  1. %foo { bar: baz }
  2. quz { @extend %foo }
und
  1. .foo { bar: baz }
  2. quz { @extend .foo }
ist ja, dass erstes
  1. quz { bar: baz }
generiert und zweites
  1. .foo, quz { bar: baz }

Durch Platzhalter-Selektoren spart man die Klassen im Stylesheet, die man sonst zur Modularisierung (und nicht zur Verwendung im Markup!) eingeführt hätte. Nicht mehr …

… aber auch nicht weniger. Ich bin freudestrahlend durch den Raum getanzt, als Platzhalter-Selektoren mit Sass 3.2 eingefüht wurden. Man kann damit nichts Neues machen, aber Besseres.

„Nicole Sullivan […] spricht über Möglichkeiten, OOCSS und Sass zu vereinen.“ Ich hab ihren Vortrag auf der Smashing Conference live gesehen und war enttäuscht. Bei ihr keine Rede davon, Sass dazu zu nutzen, den Wust an präsentationsbezogenen Klassen aus dem Markup zu verbannen. Dabei bietet sich das geradezu an, wie ich auch letztens in einem Vortrag zeigte.

Bei ihr auch keine Rede davon, Sass dazu zu nutzen, schmutzige IE-Hacks zu umgehen. Dabei bietet sich das geradezu an

Permanenter Link

Muli
am 07.12.2012 - 10:25

Ich bin skeptisch bezüglich dieser CSS-Methoden. Was manchmal nett wirkt ist am Ende vielleicht doch eher eine Hürde. Nach 6 Monaten nochmal ins CSS schauen und dann wieder rausfinden was wo definiert wurde und wo überschrieben wurde ist viel umständlicher als ein sauber gegliedertes CSS. Selbst bei komplexen Portalen mit ein paar tausend Zeilen CSS halte ich eine saubere Struktur für lesbarer und besser wartbar als diese Überschreibungsorgie.

Permanenter Link
Mathias Schäfer

Mathias Schäfer (Autor)
am 07.12.2012 - 11:35

Was meinst du genau? Auch bei klassischem CSS können sich Regeln wild überschreiben, sogar ohne dass es dem CSS auf den ersten Blick anzusehen ist. Bei Sass lässt sich ein etwaiges Überschreiben kenntlich machen: Es steht explizit z.B. @extend %foo; in der Regel, und die gegebenenfalls ergänzten und überschriebenen Deklarationen stehen notwendig in %foo {…}. Diese Regel ist problemlos im Projekt auffindbar.

Permanenter Link
Henry Zeitler

Henry Zeitler (Webkraut)
am 07.12.2012 - 23:53

Semantik, UI und OOCSS: Ich bin ein Freund von OOCSS, aber mir stellt sich immer die Frage, warum gerade UI-Elemente wie z. B. Buttons, Input- und Text-Felder über Klassen formatiert werden. Diese sollten auf der gesamten Seite einen Wiedererkennungswert haben. Warum dann nicht gleich über die Attribute (oder im Fall des Buttons über das Tag) gehen. Also [role="button"], [type="button"], button. Somit halte ich das HTML frei von unnötigen Klassen, flexibel und produziere Semantik pur, auch OO...

Permanenter Link

Die Kommentare sind geschlossen.