SCSS-Styleguide mit BEM, OOCSS & SMACSS – Best Practice im modernen Frontend

von Tim Hartmann
21.05.2014

Mit skalierbaren Projekten nehmen Komplexität und höheren Anforderungen immer mehr zu. Standards erleichtern dir die tägliche Arbeit in verschiedenen Projekten mit deinem Team.

SCSS-Styleguide mit BEM, OOCSS und SMACSS

Best Practice im modernen Frontend

Mit Skalierbarkeit ist nichts anderes gemeint, als dass das Projekt wachsen kann - weitere Features, neue Module oder auch die Pflegbarkeit. Hat man keinen Workflow oder keine saubere Struktur für sich festgelegt, kann es dazu kommen dass unnötig viel Zeit für die Weiterentwicklung des Projekts aufgewendet werden muss. Um diesem entgegenzuwirken entwerfen Entwickler Arbeitsweisen, testen und verbessern sie im Laufe der Zeit. Der sogenannte Styleguide.

Styleguides bekommen immer mehr Popularität in der Entwickler-Community. Die Projekte werden komplexer, größer und den Code zu pflegen wird schwerer. Meistens bist du nicht allein, sondern arbeitest in einem Team mit diversen Leuten zusammen. Wenn du selbst das Projekt aufbaust, dann ist es für dich einfach neue Features zu entwickeln oder auch Bugs zu fixen. Denn du weißt ganz genau, wie dein Projekt aufgebaut ist. Ohne eine gemeinsame Basis weiß es dein Kollege leider nicht.

Wechselt der Entwickler während eines Projektes, muss er sich erst in die Materie einlesen und die Arbeit seines Vorgängern nachvollziehen. Meistens ist es sehr schwer sich in ein großes Projekt einzuarbeiten, um einen guten Überblick über die Module zu bekommen. Wenn man an diesem Punkt angelangt ist, ist es oftmals schon zu spät. Der Code bläht sich unnötig auf, da gegebenenfalls Entwickler B das baut, was schon Entwickler A gemacht hat - redundanter Code wird erzeugt.

Mögliche Inhalte für deinen Styleguide

  • \n/Tabs oder Leerzeichen?
  • Ordnerstruktur
  • Struktur für Module
  • Inkludieren von Dateien / Komponenten
  • Namens-Konventionen
  • Handhabung mit Variablen
  • Arbeiten mit Breakpoints
  • Kategorisieren von CSS-Eigenschaften
  • Allgemeine CSS-Regeln

Ordnerstruktur dient als Grundlage

Eine gute Struktur für deinen Entwicklungsprozess zu haben ist sehr wichtig und die Basis von allem. Du solltest besonders darauf achten, dass sie in allen Projekten gleich ist, sofern das möglich ist - es wird dir und deinen Kollegen helfen sich einfacher in die Projekte einzuarbeiten. Hier ist meine persönliche Ordnerstruktur, basierend auf SMACSS:

<code>scss/</code><code>|- _base/</code><code>| |- _config.scss</code><code>| |- _presets.scss</code><code>| |- _headings.scss</code><code>| |- ...</code><code> </code><code>|- _layouts/</code><code>| |- _l-base.scss</code><code>| |- _l-grid.scss</code><code> </code><code>|- _modules/</code><code>| |- _m-buttons.scss</code><code>| |- _m-tabs.scss</code><code> </code><code>|- _states/</code><code>| |- _s-buttons.scss</code><code>| |- _s-tabs.scss</code><code>|- application.scss</code><code> </code><code>stylesheets/</code><code>|- application.css</code>

Zusammenfassung für eine gute Ordnerstruktur im Frontend-Projekt:

  • Pro Modul eine eigene Datei
  • Prefix für die Dateinamen (erleichtert die Suche in deinem Editor)
  • Komponenten (Partials) erhalten einen Unterstrich
  • Module und Status werden getrennt
  • Eine einzige Datei die alle Teile inkludiert

Strukturiere dein Modul

Eine Struktur für ein Modul? Ist das nicht etwas zu viel? In meinen Augen definitiv nicht. Es kann so viele Typen beinhalten oder an Komplexität zunehmen, sodass eine gute Übersicht wichtig ist.

<code>@charset "UTF-8";</code><code> </code><code>// Config</code><code>$button-bgcolor: $blue;</code><code>$button-fontcolor: $white;</code><code> </code><code>// Base</code><code>.m-button {}</code><code>.m-button--primary {}</code><code>.m-button--secondary {}</code><code> </code><code>// States</code><code>@import "../_states/_s-buttons";</code>

Es gibt drei verschiedene Bereiche in einem Modul:

  1. Als erstes gibt es den Konfigurationsbereich, "Config". Dort werden Farben oder andere Dinge bereitgestellt, mit denen ich mein Modul konfigurieren kann.
  2. Jetzt folgt der Hauptpart, "Base". Dort werden die Variationen des Moduls definiert.
  3. Der Bereich "States" bildet den Fuss der Datei. Dort wird die Datei inkludiert, in der die Status definiert sind.

Inkludiere die Dateien

In einem Projekt sind so viele kleine Teile vorhanden. Ich mag es nicht, wenn man wie wild die Dateien inkludiert. Besser hierfür ist eine Datei zu haben, die alle Teile zusammenfasst - zum Beispiel eine application.scss. Dafür habe ich eine Struktur verfasst, wie in diesem Beispiel:

<code>@charset "UTF-8";</code><code> </code><code>// Base</code><code>@import "_base/_config",</code><code> "_base/_presets",</code><code> "_base/_headings";</code><code> </code><code>// Layouts</code><code>@import "_layouts/_l-default";</code><code> </code><code>// Modules</code><code>@import "_modules/_m-buttons",</code><code> "_modules/_m-tabs";</code>

Zusammenfassung für die Strukturierung von Modulen:

  1. Strukturiere in "base""layouts" und "modules"
  2. Für jeden Bereich nur einmal "@import" deklarieren
  3. Dateiendungen entfernen

Namens-Konventionen mit Hilfe von BEM und SMACSS

Block, Element, Modifier & Scalable and modular architecture for CSS funktionieren in Kombination ausgesprochen gut.

Ich mag beide Ansätze. SMACSS ist ein Muss für jeden Frontend-Entwickler, der skalierbare Frontends schreiben möchte. Jonathan Snook ist der Autor und hat seine Erfahrungen in dem kleinen Buch zusammengefasst. Die Kombination dieser beiden Methoden ist für mich der beste Weg die Benennung zu handhaben.

<code>// Layouts</code><code>// Prefix "l-"</code><code>.l-default {}</code><code> </code><code>// Modules </code><code>// Prefix "m-"</code><code>.m-accordion {}</code><code> </code><code>// Child element of accordion</code><code>// Seperator: "__"</code><code>.m-accordion__trigger {}</code><code> </code><code>// Modifier of accordion</code><code>// Seperator: "--"</code><code>.m-accordion--plain {}</code><code> </code><code>// States</code><code>// Mostly with prefix like "is-"</code><code>.is-active {}</code><code>.is-hidden {}</code>

Benennung von Variablen

Als erstes wird eine Palette mit Farben gebaut, die überall im Projekt zur Verfügung steht. Deswegen kommt sie in die "_config.scss". Wenn spezielle Variablen benötigt werden, dann gehören diese immer in die Datei wo das Modul dafür definiert wurde. Lesbarkeit ist unglaublich wichtig und deswegen wird als Trenner der Unterstrich für Variablen-Namen verwendet.

<code>// Colors (global)</code><code>$white: #fff;</code><code>$blue: #1675d6;</code><code>$red: #e8402a;</code><code> </code><code>// Specific naming</code><code>$button-bgcolor: $blue;</code><code>$button-fontcolor: $white;</code>

Handhabung mit Breakpoints: Element Queries

Element Queries? Ja, Element Queries mit Sass sind deutlich besser als Mediaqueries. Weißt du wie es ist, wenn du das gesamte responsive Verhalten in einer einzigen Datei hast? Die Übersicht geht deutlich verloren. Außerdem schreibst du Module und das responsive Verhalten sollte auch in diesem bestimmt werden. Nicht an anderer Stelle.

Es ist für die Performance nicht wichtig, ob am Ende ein oder hundert Mediaqueries definiert sind. Genau deswegen kann man unbesorgt die Methodik der Element Queries verwenden. Wenn du mehr erfahren möchtest, dann solltest du dir den Blogpost von Anselm Hannemann durchlesen, der sich die selbe Frage stellte.

<code>// Scss</code><code>.m-navigation {</code><code> &:after {</code><code> @media screen and (min-width: 767px) {</code><code> content: '';</code><code> display: inline-block;</code><code> width: 100%;</code><code> }</code><code> }</code><code> li {</code><code> display: block;</code><code> </code><code> @media screen and (min-width: 767px) {</code><code> float: left;</code><code> }</code><code> }</code><code>}</code><code> </code><code>// Generated CSS</code><code>@media screen and (min-width: 767px) {</code><code> .m-navigation:after {</code><code> content: '';</code><code> display: inline-block;</code><code> width: 100%;</code><code> }</code><code>}</code><code> </code><code>.m-navigation li {</code><code> display: block;</code><code>}</code><code> </code><code>@media screen and (min-width: 767px) {</code><code> .m-navigation li {</code><code> float: left;</code><code> }</code><code>}</code>

Kategorisiere CSS-Eigenschaften

Wenn ich CSS / Scss schreibe, dann möchte ich sehr konsistent sein und möchte für fast alles Regeln festlegen. Genau deswegen strukturiere ich selbst die CSS-Eigenschaften.

<code>// Element-Base</code><code>.element {</code><code> margin: 10px;</code><code> padding: 10px;</code><code> </code><code> border: 1px solid red;</code><code> background: blue;</code><code> </code><code> color: white;</code><code> font-size: 16px;</code><code> </code><code> -webkit-transform: translate3d(0,0,0);</code><code>}</code>
  1. Box (margin, padding, display etc.)
  2. Border & Background (border, border-radius, background-color, etc.)
  3. Text (color, font-size, text-transform, etc.)
  4. Other stuff (animations, transforms, etc.)

Meine persönlichen CSS-Tipps

Vermeide ID's
ID's sind zu "schwer". Sie sind zu speziell und nur nützlich für Javascript-Hooks. Der bessere Weg ist Klassen zu verwenden.

Vermeide !important
Wenn du das benötigst, dann ist es mit deiner CSS-Architektur falsch gelaufen, weil du es dann benötigst um jegliche Dinge zu überschreiben - vergleichbar als würde man mit Kanonen auf Spatzen schießen. Wenn deine Struktur sauber aufgesetzt wurde, dann wirst du das nicht benötigen.

Vermeide den "child"-Selektor
Warum vermeide ich diesen Selektor? Ist es nicht ein gutes Feature von CSS? Ich denke nicht, aber als ich mit CSS angefangen habe, verwendete ich es sehr oft. Nach gewisser Zeit stellte ich dann fest, dass es das Modul zu spezifisch macht. Man macht es fest anhand des Markups, aber die Komponenten beziehungsweise das gesamte Modul sollte unabhängig davon funktionieren. Was passiert, wenn sich mal die Ordnung verändert? Benutze daher besser Klassen!

<code>// Bad example</code><code>// Child-selector</code><code>.m-tabs > li {}</code><code> </code><code>// Good example</code><code>// Classname </code><code>.m-tabs .m-tabs__trigger {}</code><code> </code><code>// Better example</code><code>// Less nesting</code><code>.m-tabs__trigger {}</code>

Vor einigen Tagen habe ich darüber getwittert, da ich an einem alten Projekt arbeiten durfte. Mit einer Erwähnung bei CSS Wizardy erhaschte ich direkt einen Retweet und fand viele Anhänger, die genauso dachten wie ich. Du solltest den Ansatz von ihm verfolgen und dir diesen Artikel durchlesen.

Tims Frontend Grundlagenrezept

  • Überall zwei Leerzeichen anstatt Tabs
  • Ein Leerzeichen zwischen Element und Klammer
  • Absatz zwischen CSS-Eigenschaften und neuem Element
  • Absatz nach der geschlossenen Klammer
  • HEX-Werte anstatt RGB verwenden
  • HEX-Werte via Sass zu RGB konvertieren lassen: negatives Beispiel = color: rgba(255,255,255,.3); positives Beispiel = color: rgba(#000000, .3);
<code>// Same styles for more than one element</code><code>// Separation with a line-break</code><code>.element,</code><code>.second-element {</code><code> color: red;</code><code>}</code><code> </code><code>.m-tabs {</code><code> background: rgba($black, .3);</code><code>}</code>

Wenn du Verbesserungen oder auch Ergänzungen hast und du mit uns darüber sprechen möchtest, dann schreib gerne eine Mail an hartmann@neoskop.de oder sprich uns via Twitter an. Wir freuen uns auf eure Meinungen und Erfahrungen mit persönlichen Styleguides.