BEM für Späteinsteiger
11. November 2016
Es gibt bei uns eine gewisse Tradition, sich erst recht spät mit dem ganzen heißen Scheiß aus der WebDev-Hipster-Toolchain zu beschäftigen. Nachdem ich 2013 den staunenden Kollegen im Working-Draft-Podcast noch berichtete, dass wir außer jQuery quasi keine nennenswerten Frontend-Tools oder -Techniken verwenden, gab es in den letzten drei Jahren schon noch die eine oder andere Aufrüstungsmaßnahme in unserem Workflow. Aber es ist uns eben wichtig, nicht zuviel Energie in Lösungen zu stecken, die sich mittelfristig als Sackgasse erweisen. Gut abgehangene Technik schlägt jeden Hype.
Im Falle von BEM war es insbesondere die unfassbar schlechte Didaktik, mit der das System allerorts beworben wurde. Ich konnte schlicht keinen vernünftigen Fachartikel finden, der mich wirklich von den Vorzügen überzeugen konnte. Mit den nachfolgenden Zeilen kann ich diesen Zustand hoffentlich ein wenig verbessern!
Kurzer Hinweis
Ich werde hier aus Gründen auf die Bezeichnung „Element“ verzichten, wenn damit die nackten HTML-Strukturelemente wie <h1>Headline</h1>
oder <img src="" alt="" />
gemeint sind. Ich nenne sie stattdessen Bausteine, um sie von den BEM-Elementen zu unterscheiden.
Das Problem
CSS ist zwar auf den ersten Blick kein sonderlich komplexes Styling-System, doch der Schein trügt: Jeder, der ein Projekt mit mehr als 2000 Zeilen CSS-Code gebaut hat oder gar vererbt bekommt, hat sich sicherlich schon darüber geärgert, dass es in CSS keine eingebaute, verpflichtende Systematik oder Architektur gibt, mit der sich große Projekte mit vielen unterschiedlichen, aber natürlich visuell verwandten Elementen besser organisieren lässt. Das einzige, was man sich üblicherweise zu Nutze macht, sind:
- HTML: semantische, aber kurze Klassennamen, die irgendwie trotzdem das Styling erahnen lassen
- HTML: multiple Klassen für unterschiedliche Stylings, die aus unterschiedlichen Kontexten geerbt werden können
- CSS: lockere Gruppierung von irgendwie zusammengehörigen Selektoren
- CSS: grobe Reihenfolge der Selektoren von allgemein zu speziell, von außen nach innen, und von oben nach unten
- CSS: verschachtelte Selektoren mit zunehmender Spezifität – durch übersichtliche Einrückung in Präprozessoren verführerisch einfach!
Dies sind alles keine grundsätzlich schlechten Ideen, und die Fähigkeiten von CSS ermutigen uns ja auch, Dinge wie die Kaskade, die Hierarchie und die Spezifität auszunutzen. Jedoch stoßen viele der einstigen best practices schlichtweg an ihre Grenzen, wenn es um mehr gehen soll als nur das neue Blogdesign oder den One-Pager für den Friseurladen nebenan. Ganz speziell sind es drei Dinge, die auch mir als wirklich erfahrenen CSS-Artisten immer wieder Schwierigkeiten bereiten:
- Konsistentes Namenskonzept für Klassen
- Wiederverwendbarkeit von Styling-Regeln (in funktionierend)
- Überschreiben von Selektoren mit bereits zu starker Spezifität
Und genau bei diesen drei Dingen kann BEM uns behilflich sein. Es lohnt sich also, dranzubleiben!
Die Grundprinzipien
BEM ist eine freiwillige Konvention und eine Systematik, wie man Klassen in HTML vergibt und diese in CSS nutzt. Es ist eine Empfehlung und ein Vorschlag, aber kein festes Framework. Man könnte zwar theoretisch Software schreiben, die BEM irgendwie automatisch generiert, aber das ist alles Quatsch – wir nutzen BEM einfach als eine Möglichkeit, unseren CSS-Code gut zu organisieren, damit wir uns nicht mehr so oft an den Kopf greifen müssen.
Mit BEM vereinheitlichen und vereinfachen wir die Prinzipien von CSS noch stärker, so dass man von einer wirklich minimalistischen Struktur sprechen kann. Es wird auf so vieles verzichtet!
- Alle(!) CSS-Selektoren bestehen aus lediglich je einer Klasse
- Es gibt keine id-Selektoren (
#header {}
) und auch keine Baustein-Selektoren (h2 {}
) - Verschachtelungen jeglicher Art sind ebenfalls nicht erlaubt
- Selbstredend ist auch
!important
kein Thema
Somit besitzt jeder Selektor die gleiche Spezifität und es entscheidet nunmehr ausschließlich die Reihenfolge im Quelltext, welche Regel greift. Peace of mind – die Spezifitätskriege sind vorbei!
Gleichzeitig hat dies aber zur Folge, dass jeder Baustein, welchen ich gezielt stylen möchte (also wirklich jeder!), mindestens eine Klasse erhalten muss, selbst wenn es sich um ein blödes a
innerhalb eines li
einer Navigation handelt. Das ist ganz schön hart! Es bläht den HTML-Code auf, macht ihn weniger gut lesbar und nimmt dem CSS-Code scheinbar seine Eleganz. Früher hätten wir beispielsweise so etwas geschrieben:
<ul class="navi">
<li><a href="ueberuns.html">Über uns</a></li>
<li><a href="vision.html">Vision</a></li>
<li class="active"><a href="kontakt.html">Kontakt</a></li>
</ul>
ul.navi a {
color: #090;
text-decoration: none;
}
ul.navi .active > a {
color: #000;
}
Mit BEM sieht das im Vergleich erstmal schlimm aus:
<ul class="navi">
<li class="navi__item">
<a class="navi__link" href="ueberuns.html">Über uns</a>
</li>
<li class="navi__item">
<a class="navi__link" href="vision.html">Vision</a>
</li>
<li class="navi__item navi__item--active">
<a class="navi__link navi__link--active" href="kontakt.html">Kontakt</a>
</li>
</ul>
.navi__link {
color: #090;
text-decoration: none;
}
.navi__link--active {
color: #000;
}
Was stört uns intuitiv daran? Ich vermute, es ist zum einen die schiere Menge an langen, redundanten Klassen, und zum anderen die Klassennamen, die die Hierarchie des HTML-Konstruktes noch einmal doppeln. Warum nutzen wir nicht einfach die bereits vorhandene HTML-Hierarchie (ul
als Container von li
sowie li
als Container von a
) und machen damit sowohl das HTML als auch das CSS kürzer und eleganter?
Weil wir uns mit dieser „physischen“ Hierarchie jede Menge Ärger einhandeln, der am Anfang noch nicht als Ärger erkennbar ist, aber ab einer gewissen Projektgröße das Potenzial hat, den CSS-Code quasi unpflegbar zu machen. Auch wenn es ein Kernkonzept von CSS ist: Hierarchie auf Selektor-Ebene skaliert nicht. Deshalb verzichtet BEM darauf und bildet eine (vereinfachte) Hierarchie ausschließlich in den Klassennamen ab.
Die BEM-Klassennamen
Ich habe ganz bewusst darauf verzichtet, das Akronym BEM gleich am Anfang meines Textes zu erläutern, denn es verwirrt nur. (Genauso wie es keinen Sinn macht, die Cascade von CSS gleich am Anfang zu erläutern.) Doch nun ist der richtige Zeitpunkt gekommen! BEM steht für Block, Element und Modifier und beschreibt die Systematik, nach der die Klassennamen ausgedacht und vergeben werden. Keine große Sache.
Ein Block ist ein Container von grundsätzlich beliebiger Komplexität. Er enthält mehrere Elemente, die sinnvoll zum Block passen und diesen mit Leben füllen. Jeder Block und jedes Element kann außerdem einen optionalen Modifier haben, der das entsprechende Basis-Styling mit zusätzlichen CSS-Eigenschaften abwandelt und verfeinert, so dass es eben mehrere unterschiedliche Styling-Varianten dafür geben kann.
Die Schreibweise der BEM-Klassen ist zwar nicht platzsparend, aber einfach:
- Blöcke haben ganz normale semantische Klassen:
class="pagination"
- darin enthaltene Elemente werden mit zwei Unterstrichen notiert:
class="pagination__link"
- Abwandlungen der Elemente erhalten, zusätzlich zur Originalklasse, eine mit Doppeltrennstrich ergänzte Variante:
class="pagination__link pagination__link--active"
- Aber auch Blöcke können direkt als Ganzes abgewandelt werden, insbesondere bei gewünschter Positionierung an anderer Stelle:
class="pagination pagination--aside"
Übrigens: Styling-Varianten, die man im CSS über den Kontext eines im übergeordneten HTML-Bausteins realisiert, sind verboten, von daher kann es durchaus mal sein, dass man mehrere Elemente eines Blockes einzeln modifizieren muss, wie im oberen Beispiel mit dem --active
-Modifier.
Übrigens 2: Natürlich zwingt euch niemand, absolut jeden Baustein mit einer Klasse zu versehen. Was ihr nicht stylen müsst, muss auch nicht verBEMt werden.
BEM als Parallel-„DOM“
Wichtig zu beachten ist, dass es im Verhältnis vom Block zu seinen Elementen nur eine einzige Hierarchie-Stufe gibt: alle Elemente eines Blocks sind – mit der BEM-Brille gesehen – auf einer Ebene, unabhängig davon, wie tief sie innerhalb des HTML tatsächlich verschachtelt sind. Somit bauen wir uns mit BEM im Grunde eine Parallel-Struktur auf, die sich auf zweierlei Arten vom HTML emanzipiert:
- Sie schafft eine eigene Hierarchie, die sich im Extremfall nur locker am vorhandenen HTML orientiert.
- Sie ist komplett „Tag-agnostisch“: Das Herumspielen mit
h1
- bish6
-Ebenen (SEO, anyone?) und das Anreichern mit HTML5-Semantik in Form vonaside
-,section
- odersubhead
-Tags hat keinerlei Auswirkungen auf das Styling. Ihr ahnt nicht, wie befreiend das ist!
Elemente, die gleichzeitig Blöcke sind
Ich schrieb oben, dass Blöcke eine grundsätzlich beliebige Hierarchie ausweisen können – und das stimmt auch. Denn wenn beispielsweise eine Gruppe von drei gleichartigen Teaserboxen nebeneinander dargestellt werden soll (für mobile natürlich untereinander), so habe ich ja zum einen das gesamte Teaserkonstrukt als Block, in dem die drei Teaser meine Elemente bilden. Zum anderen kann man sich aber auch jeden einzelnen Teaser als Block mit seinen unterschiedlichen Elementen wie Headline, Vorschaubild usw. vorstellen
Eine beispielhafte HTML-Struktur sähe nackt so aus:
<section>
<article>
<h3>Teaser 1</h3>
<img src="" alt="" />
<p>…</p>
</article>
<article>
<h3>Teaser 2</h3>
<img src="" alt="" />
<p>…</p>
</article>
<article>
<h3>Teaser 2</h3>
<img src="" alt="" />
<p>…</p>
</article>
</section>
Unsere article
-Bausteine könnten also Elemente der section
sein, gleichzeitig aber als Blöcke für h3
, img
und p
fungieren. Dies sähe in der Praxis dann so aus:
<section class="teasers">
<article class="teasers__box box">
<h3 class="box__headline">Teaser 1</h3>
<img src="" alt="" class="box__image" />
<p class="box__intro">…</p>
</article>
<article class="teasers__box box box--highlight">
<h3 class="box__headline">Teaser 2</h3>
<img src="" alt="" class="box__image" />
<p class="box__intro">…</p>
</article>
<article class="teasers__box box">
<h3 class="box__headline">Teaser 3</h3>
<img src="" alt="" class="box__image" />
<p class="box__intro">…</p>
</article>
</section>
Es hat sich als gute Konvention erwiesen, wenn der Name des untergeordneten Blocks identisch mit seinem Element-Anhängsel ist, in diesem Beispiel eben teasers__box
und box
. Somit macht man die Übergabe sichtbar. Es ist aber keine Pflicht.
Grundsätzlich lässt sich allerdings auch argumentieren, dass es hier keiner weiteren Block-Ebene bedarf. Man könnte auch den teasers
-Block als Alleinherrscher walten lassen, etwa so:
<section class="teasers">
<article class="teasers__box">
<h3 class="teasers__headline">Teaser 1</h3>
<img src="" alt="" class="teasers__image" />
<p class="teasers__intro">…</p>
</article>
<article class="teasers__box teasers__box--highlight">
<h3 class="teasers__headline">Teaser 2</h3>
<img src="" alt="" class="teasers__image" />
<p class="teasers__intro">…</p>
</article>
<article class="teasers__box">
<h3 class="teasers__headline">Teaser 3</h3>
<img src="" alt="" class="teasers__image" />
<p class="teasers__intro">…</p>
</article>
</section>
Modularität auf Block-Ebene
Welches der obigen Beispiele euch besser gefällt, macht ihr am besten davon abhängig, wie komplex das Geschehen in den einzelnen Teasern sein soll, und ob man die Boxen auch evtl. einmal einzeln benötigt, ohne den teaser
-Block drum herum.
Denn dies ist der Kern des Modularitäts-Versprechen von BEM: Wenn wir einen Baustein als Block ansehen, lässt er sich im HTML überallhin verpflanzen und sieht für sich genommen an jeder Stelle auch gleich aus. Sprechen wir hingegen den Baustein als untergeordnetes Element an, kann er von der Platzierung her mit ebenbürtigen Elementen interagieren – beispielsweise eine horizontale Reihe bilden, oder als Flexbox-Item noch viel interessanter im Kontext positioniert werden.
Ich nenne dies die duale Natur von BEM-Bausteinen: Sie können gleichzeitig Welle und Teilchen Block und Element sein. Oder, als CSS-Code formuliert:
.box { border: 1px solid #000; background: #ccc; }
.box--highlight { background: yellow; }
.teasers { display: table; }
.teasers__box { display: table-cell; }
BEM und Sass
Die unhandlichen Klassennamen von BEM-Projekten sind natürlich nicht nur im HTML ein visuelles Problem, sondern auch in handgeschriebenem CSS. Wer fuhrwerkt schon gerne in Code herum, der so aussieht:
.newsteaser {
border: 1px solid #000;
background: #ccc;
padding: 1em;
}
.teasergroup {
width: 80%;
overflow: hidden;
}
.teasergroup__headline {
font-size: 1.5em;
}
.teasergroup__newsteaser {
float: left;
width: 33%;
}
.teasergroup__newsteaser--emphasized {
border-color: red;
}
Da viele von euch für das Schreiben von CSS sowieso einen Präprozessor verwenden, kann man beispielseise mit Sass die BEM-Hierarchie auch mit den entsprechenden Einrückungen abbilden:
.newsteaser {
border: 1px solid #000;
background: #ccc;
padding: 1em;
}
.teasergroup {
width: 80%;
overflow: hidden;
&__headline {
font-size: 1.5em;
}
&__newsteaser {
float: left;
width: 33%;
&--emphasized {
border-color: red;
}
}
}
Wichtig dabei: Auch bei dieser scheinbar hierarchischen Verschachtelung verändert sich keineswegs die Spezifität der Selektoren. Alle Bausteine werden absolut gleichwertig angesprochen, lediglich die Reihenfolge entscheidet bei Konflikten, welche Regel gewinnt. Deshalb ist es auch sinnvolle Praxis, das für sich stehende Block-Styling eines dualen Bausteines zuerst zu behandeln, und erst im Anschluss daran die Platzierung und Anpassung im Kontext eine übergeordneten Blockes. Diese ist spezifischer und gehört von daher weiter nach hinten.
Fazit
Genau wie einige andere Ansätze (OOCSS oder SMACSS) versucht BEM, die negativen Erfahrungen, die jeder CSS-Entwickler in komplexen und sich agil entwickelnden Layouts unweigerlich macht, zu mindern. Dabei geht es nicht darum, eine Metasprache zu schaffen, wie es Sass tut. Sondern es geht darum, die vorhandenen Möglichkeiten von CSS sinnvoll zu nutzen sowie gefährliche Praktiken zu vermeiden.
CSS wurde, genau wie HTML, nicht von visuellen Designern erdacht, sondern von Informatikern. Trotzdem müssen wir heute Layouts damit umsetzen, die sehr wohl von Designern stammen. Manchmal sind es wir Designer selber, die mit CSS unsere Ideen umsetzen. Und die Projekte werden immer komplizierter, insbesondere in einem responsiven Web. Seien wir nicht traurig, dass wir superclever verschachtelte Child-Selektoren und mühsam erlernte Spezifitäten hinter uns lassen – sie haben sich einfach auf Dauer nicht bewährt und mehr Probleme aufgeworfen als gelöst.
Ich habe es selber ausprobiert und werde vorerst dabei bleiben; Ein Projekt mit BEM aufzubauen sorgt bei mir für gesunden Blutdruck und entspannte Gesichtszüge. Fast wie eine Wellness-Massage. Und das ist tatsächlich nur dezent übertrieben :-)