Ciao BEM, Servus Vanilla CSS

Der Abschied von BEM ist mir nicht leicht gefallen, doch es wird Zeit für was Neues. Und was Altes. Irgendwie.

Wie kam es eigentlich dazu, dass die BEM-Konvention zur Formulierung von CSS-Stylingklassen vor 15 Jahren so großen Erfolg hatte? Es gab mehrere valide Gründe:

  1. Vollständige Eliminierung des fehleranfälligen Konzepts der Spezifität, denn bei BEM haben alle CSS-Selektoren exakt die gleiche „Eine-Klasse-Spezifität“ von 10 Punkten.
  2. Eine Art Styling-Kapselung voneinander getrennter Module durch je einen eindeutigen Namespace (das Block-Element)
  3. Unabhängigkeit des Stylings von Aufbau und Semantik des HTML

Doch es hat sich inzwischen ein bisschen überholt, das ganze. Auch dafür gibt es mehrere Gründe. Diesmal in Prosa statt als Liste. Zunächst mal nervt es ungemein, dass bei strenger BEM-Anwendung die Modifikations-Klasse zusätzlich zu der regulären Element-Klasse vergeben werden muss. Liest sich dann so:

<div class="multipass">
    <h3 class="multipass__headline multipass__headline--active">Leeloo</h3>
</div>

Sieht erstmal scheiße aus. Und wenn man die aktive Klasse per JavaScript dynamisch setzen möchte, macht es das nicht hübscher. Modifikatoren sollten irgendwie Zusatz-Klassen sein, aber dann brechen mehrere eiserne BEM-Konzepte.

BEM war auf CSS-Seite eigentlich nur in Kombination mit Sass und Nesting irgendwie erträglich. Hier konnte man die ganze Geschwätzigkeit des HTML-Codes wegabstrahieren:

.multipass {
    background: #ccc;

    &__headline {
        font-size: 2rem;

        &--active {
            color: #c00;
        }
    }
}

Da Sass ebenfalls nicht mehr cool ist (darüber gibt es bald einen gesonderten Artikel), könnte man das noch mit nativem CSS-Nesting probieren. Habe ich gemacht. Geht aber nicht! Dennobwohl der &-Platzhalter existiert, kann er in nativem CSS nicht mit Teil-Strings kombiniert werden. Ein Grund mehr, wieder vernünftige Klassen zu verwenden und das CSS wieder etwas stärker an die Struktur des HTML zu binden. In der Praxis hat sich da smit der absoluten Trennung der beiden Strukturen eh nie ganz eingelöst:

<div class="multipass">
    <h3 class="headline active">Leeloo</h3>
</div>

und im CSS dazu so:

.multipass {
    background: #ccc;

    .headline {
        font-size: 2rem;

        &.active {
            color: #c00;
        }
    }
}

Ein Hauch von 2006, mit einer Prise Modulkapselung, optisch angehübscht durch übersichtliches Nesting. Aber wir wollen mehr. Und zwar vor allem: Die coolen modernen Selektoren nutzen, die für viele bisher quasi außen vor waren, weil sie die orthodoxe BEM-Welt stören würden. Dinge, die ich seither sehr gerne nutze, weil ich es mir inzwischen zur Mission gemacht habe, möglichst wenig Klassen zu verwenden, da diese – sehen wir es ein – keine echte Semantik in sich tragen. Statt für jeden Scheiß eine Klasse oder einen geschwätzigen BEM-Modifier herbeizufantasieren, kann man auch folgende Freunde einsetzen:

Die Verwandschafts-Selektoren

.multipass + .multipass { /* Elemente direkt folgend */ }
.multipass ~ .multipass { /* Elemente nachfolgend mit Zeug dazwischen */ }
.multipass > .headline { /* Kind-Selektor */ }
.multipass > &:nth-child(2) { /* bestimmes Kind-Selektor }
[…]

Ich meine, wir haben das Zeug ja schon fast wieder vergessen gehabt, das ist alles Standardzeug von CSS3, das gibt es seit hundert Jahren und kein bisschen weniger cool geworden. Man darf halt nicht ständig sein HTML umstrukturieren, das ist der Nachteil, zugegebenerweise.

Die Aria-Selektoren

Wann immer ein Status sowieso per aria-Property ausgewiesen werden sollte, kann man den auch gleich als CSS-Hook mitverwenden und spart sich somit die Extra-Klasse im HTML:

<menu class="menulist">
    <li class="item">
        <a class="link" aria-current="page" href="element5.html">Leeloo</a></li>
    </li>
</menu>
.menulist {
    .item {}
    .link { 
        color: black;

        &[aria-current='page'],
        &:hover {
            background: black;
            color: white;
        }
    }
}

Die coolen Selektoren von heute

Großer Hype vor 3 Jahren, wahrscheinlich berechtigt. Ich beginne tatsächlich erst jetzt, ganz zaghaft (zu lange im BEM-Land verharrt), die Vorzüge von :has und :not zu schätzen:

.menulist {
    .item {
        background: blue;
        &:has([aria-current]) {
            box-shadow: 0 0 10px rgba(0,0,0,0.3);
        }
        &:not(:first-child) {
            font-size: 1.2rem;
        }
    }
}

Ihr merkt schon, ein paar Dinge sind geblieben aus der Lehre der letzten 15 Jahre:

  • Nach Möglichkeit keine Tag-Selektoren wie h2 {} verwenden – das fesselt uns dann doch zu sehr an die Semantik des HTML.
  • Aufteilung des Layouts in Blöcke bzw. Module mit einheitlichem Namespace, um voneinander getrennte Layout-Bestandteile auch getrennt zu stylen.

Und weil es alles natürlich nicht ganz so einfach ist, wenn die Websites komplexer werden, gibt es noch ein weiteres Konzept, was ich zu schätzen gelernt habe:

Die neue @layer Funktion