BEM (Block Element Modifier) ist eine beliebte Konvention zur Benennung von CSS-Klassen, die CSS wartbarer macht. Dieser Artikel setzt voraus, dass Sie mit der Namenskonvention bereits vertraut sind. Wenn nicht, können Sie mehr darüber auf getbem.com erfahren, um sich mit den Grundlagen vertraut zu machen.
Die Standard-Syntax für BEM ist
block-name__element-name--modifier-name
Ich persönlich bin ein großer Fan der Methodik hinter der Namenskonvention. Das Aufteilen Ihrer Stile in kleine Komponenten ist wesentlich einfacher zu warten, als ein Meer von hoher Spezifität, die überall in Ihrer Stylesheet verstreut ist. Allerdings gibt es ein paar Probleme mit der Syntax, die zu Problemen in der Produktion sowie zu Verwirrung bei Entwicklern führen können. Ich ziehe es vor, stattdessen eine leicht abgewandelte Version der Syntax zu verwenden. Ich nenne sie ABEM (Atomic Block Element Modifier)
[a/m/o]-blockName__elementName -modifierName
Ein Atomic Design Präfix
Das a/m/o ist ein Atomic Design Präfix. Nicht zu verwechseln mit Atomic CSS, was etwas völlig anderes ist. Atomic Design ist eine Methodik zur Organisation Ihrer Komponenten, die die Wiederverwendbarkeit von Code maximiert. Es teilt Ihre Komponenten in drei Ordner auf: Atome, Moleküle und Organismen. Atome sind super einfache Komponenten, die im Allgemeinen nur aus einem einzigen Element bestehen (z. B. eine Button-Komponente). Moleküle sind kleine Gruppen von Elementen und/oder Komponenten (z. B. ein einzelnes Formularfeld, das ein Label und ein Eingabefeld anzeigt). Organismen sind große, komplexe Komponenten, die aus vielen Molekül- und Atomkomponenten bestehen (z. B. ein vollständiges Registrierungsformular).

Die Schwierigkeit, Atomic Design mit klassischem BEM zu verwenden, liegt darin, dass es keine Anzeige gibt, um welche Art von Komponente ein Block handelt. Dies kann es schwierig machen, zu wissen, wo sich der Code für diese Komponente befindet, da Sie möglicherweise in drei separaten Ordnern suchen müssen, um ihn zu finden. Das Hinzufügen des Atomic-Präfixes am Anfang macht es sofort offensichtlich, in welchem Ordner die Komponente gespeichert ist.
camelCase
Ermöglicht benutzerdefinierte Gruppierungen
Klassisches BEM trennt jedes einzelne Wort innerhalb eines Abschnitts mit einem einzelnen Bindestrich. Beachten Sie, dass das Atomic-Präfix im obigen Beispiel ebenfalls durch einen Bindestrich vom Rest des Klassennamens getrennt ist. Schauen Sie sich an, was passiert, wenn Sie BEM classic vs. camelCase ein Atomic-Präfix hinzufügen
/* classic + atomic prefix */
.o-subscribe-form__field-item {}
/* camelCase + atomic prefix */
.o-subscribeForm__fieldItem {}
Auf den ersten Blick sieht der Komponentenname bei der klassischen Methode so aus, als ob er "o subscribe form" genannt wird. Die Bedeutung des "o" geht völlig verloren. Wenn Sie jedoch die "o-" auf die camelCase-Version anwenden, ist klar, dass sie absichtlich als separates Informationsstück zum Komponentennamen geschrieben wurde.
Jetzt könnten Sie das Atomic-Präfix auf klassisches BEM anwenden, indem Sie das "o" großschreiben, wie hier
/* classic + capitalized atomic prefix */
.O-subscribe-form__field-item {}
Das würde das Problem lösen, dass das "o" im Rest des Klassennamens verloren geht. Es löst jedoch nicht das grundlegende Problem der klassischen BEM-Syntax. Durch die Trennung der Wörter mit Bindestrichen ist das Bindestrichzeichen nicht mehr für Sie als Gruppierungsmechanismus verfügbar. Durch die Verwendung von camelCase können Sie das Bindestrichzeichen für zusätzliche Gruppierungen freigeben, auch wenn diese Gruppierung nur das Hinzufügen einer Zahl am Ende eines Klassennamens ist.
Ihr Gehirn verarbeitet die Gruppierungen schneller
camelCase hat auch den zusätzlichen Vorteil, dass die Gruppierung der Klassennamen leichter mental verarbeitet werden kann. Bei camelCase stellt jede Lücke, die Sie in einem Klassennamen sehen, eine Art Gruppierung dar. Bei klassischem BEM könnte jede Lücke entweder eine Gruppierung oder ein Leerzeichen zwischen zwei Wörtern derselben Gruppe sein.
Betrachten Sie diese Silhouette einer klassischen BEM-Klasse (plus Atomic-Präfix) und versuchen Sie herauszufinden, wo die Präfix-, Block-, Element- und Modifier-Abschnitte beginnen und enden
Ok, versuchen Sie es jetzt mit dieser. Es ist genau dieselbe Klasse wie die obige, nur dass diesmal camelCase anstelle von Bindestrichen verwendet wird, um jedes Wort zu trennen
Das war viel einfacher, oder? Diese Silhouetten sind im Wesentlichen das, was Ihr Gehirn sieht, wenn es durch Ihren Code scrollt. All die zusätzlichen Bindestriche im Klassennamen machen die Gruppierungen viel weniger klar. Wenn Sie Ihren Code lesen, versucht Ihr Gehirn zu verarbeiten, ob die aufgetretenen Lücken neue Gruppierungen oder nur neue Wörter sind. Dieser Mangel an Klarheit belastet Ihre kognitive Last während der Arbeit.
klassisches BEM + Atomic-Präfix
camelCase BEM + Atomic-Präfix
Verwenden Sie Multi-Klassen-Selektoren (verantwortungsbewusst)
Eine der goldenen Regeln in BEM ist, dass jeder Selektor nur eine einzige Klasse enthalten soll. Die Idee ist, dass dies CSS wartbar hält, indem die Spezifität von Selektoren niedrig und überschaubar bleibt. Einerseits stimme ich zu, dass niedrige Spezifität besser ist als wuchernde Spezifität. Andererseits bin ich entschieden dagegen, dass eine strenge Regel von einer Klasse pro Selektor das Beste für Projekte ist. Die Verwendung einiger Multi-Klassen-Selektoren in Ihren Stilen kann die Wartbarkeit tatsächlich verbessern und nicht verschlechtern.
„Aber das führt zu höherer Spezifität! Wissen Sie nicht, dass Spezifität von Natur aus böse ist?!?
Spezifität != böse.
Unkontrollierte Spezifität, die außer Kontrolle geraten ist = böse.
Einige Deklarationen mit höherer Spezifität bedeuten nicht sofort, dass Ihr CSS schwieriger zu warten ist. Wenn sie auf die richtige Weise verwendet werden, können bestimmte Regeln mit höherer Spezifität CSS sogar einfacher zu warten machen. Der Schlüssel zum Schreiben von wartbarem CSS mit ungleicher Spezifität besteht darin, Spezifität gezielt hinzuzufügen und nicht nur, weil ein Listenelement zufällig in einem Listenelement vorkommt.
Außerdem wollen wir nicht eigentlich, dass unsere Modifier-Stile mehr Macht über Elemente haben als Standardstile? Rückwärts zu biegen, um Modifier-Stile auf der gleichen Spezifitätsebene wie normale Stile zu halten, erscheint mir albern. Wann wollen Sie eigentlich, dass Ihre regulären Standardstile Ihre speziell ausgewiesenen Modifier-Stile überschreiben?
Die Trennung des Modifiers führt zu saubererem HTML
Dies ist die größte Änderung an der Syntax, die ABEM einführt. Anstatt den Modifier mit der Element-Klasse zu verbinden, wenden Sie ihn als separate Klasse an.
Eines der Dinge, über die sich praktisch jeder beschwert, wenn er zum ersten Mal anfängt, BEM zu lernen, ist, wie hässlich es ist. Es ist besonders schlimm, wenn es um Modifier geht. Sehen Sie sich dieses Gräuel an. Es hat nur drei Modifier angewendet und sieht trotzdem wie ein Zugwrack aus
B__E–M
<button class="block-name__element-name block-name__element-name--small block-name__element-name--green block-name__element-name--active">
Submit
</button>
Schauen Sie sich die ganze Wiederholung an! Diese Wiederholung macht es ziemlich schwierig zu lesen, was sie eigentlich zu tun versucht. Schauen Sie sich jetzt dieses ABEM-Beispiel an, das die gleichen Modifier wie im vorherigen Beispiel hat
A-B__E -M
<button class="a-blockName__elementName -small -green -active">
Submit
</button>
Viel sauberer, oder? Es ist viel einfacher zu sehen, was diese Modifier-Klassen aussagen sollen, ohne all den repetitiven Kram, der im Weg ist.
Beim Untersuchen eines Elements mit den Browser-Entwicklertools sehen Sie immer noch die vollständige Regel im Styling-Panel, sodass die Verbindung zur ursprünglichen Komponente auf diese Weise erhalten bleibt
.a-blockName__elementName.-green {
background: green;
color: white;
}
Es ist nicht viel anders als das BEM-Äquivalent
.block-name__element-name--green {
background: green;
color: white;
}
Die Verwaltung des Zustands wird einfach
Ein großer Vorteil, den ABEM gegenüber klassischem BEM hat, ist, dass die Verwaltung des Zustands einer Komponente immens einfacher wird. Nehmen wir als Beispiel ein einfaches Akkordeon. Wenn ein Abschnitt dieses Akkordeons geöffnet ist, sagen wir, wir möchten diese Änderungen am Styling vornehmen
- Ändern Sie die Hintergrundfarbe der Abschnittsüberschrift
- Zeigen Sie den Inhaltsbereich an
- Lassen Sie einen Pfeil nach oben zeigen
Wir werden uns für dieses Beispiel strikt an die klassische B__E–M-Syntax halten und die Regel „eine Klasse pro CSS-Selektor“ einhalten. Das ist es, was wir am Ende erhalten (beachten Sie, dass dieses Akkordeon der Kürze halber nicht zugänglich ist)
Siehe Stift Akkordeon 1 – reines BEM von Daniel Tonon (@daniel-tonon) auf CodePen.
Das SCSS sieht ziemlich sauber aus, aber schauen Sie sich all die zusätzlichen Klassen an, die wir dem HTML für nur eine Zustandsänderung hinzufügen müssen!
HTML, während ein Segment mit BEM geschlossen ist
<div class="revealer accordion__section">
<div class="revealer__trigger">
<h2 class="revealer__heading">Three</h2>
<div class="revealer__icon"></div>
</div>
<div class="revealer__content">
Lorem ipsum dolor sit amet...
</div>
</div>
HTML, während ein Segment mit BEM geöffnet ist
<div class="revealer accordion__section">
<div class="revealer__trigger revealer__trigger--open">
<h2 class="revealer__heading">One</h2>
<div class="revealer__icon revealer__icon--open"></div>
</div>
<div class="revealer__content revealer__content--open">
Lorem ipsum dolor sit amet...
</div>
</div>
Schauen wir uns nun an, was passiert, wenn wir zu dieser schicken neuen A-B__E -M-Methode wechseln
Siehe Stift Akkordeon 2 – ABEM-Alternative von Daniel Tonon (@daniel-tonon) auf CodePen.
Eine einzige Klasse steuert jetzt das zustandsspezifische Styling für die gesamte Komponente, anstatt dass eine separate Klasse auf jedes Element einzeln angewendet werden muss.
HTML, während ein Segment mit ABEM geöffnet ist
<div class="m-revealer o-accordion__section -open">
<div class="m-revealer__trigger">
<h2 class="m-revealer__heading">One</h2>
<div class="m-revealer__icon"></div>
</div>
<div class="m-revealer__content">
Lorem ipsum dolor sit amet...
</div>
</div>
Auch, schauen Sie, wie viel einfacher der JavaScript geworden ist. Ich habe den JavaScript so sauber wie möglich geschrieben und das war das Ergebnis
JavaScript bei Verwendung von reinem BEM
class revealer {
constructor(el){
Object.assign(this, {
$wrapper: el,
targets: ['trigger', 'icon', 'content'],
isOpen: false,
});
this.gather_elements();
this.$trigger.onclick = ()=> this.toggle();
}
gather_elements(){
const keys = this.targets.map(selector => `$${selector}`);
const elements = this.targets.map(selector => {
return this.$wrapper.querySelector(`.revealer__${selector}`);
});
let elObject = {};
keys.forEach((key, i) => {
elObject[key] = elements[i];
});
Object.assign(this, elObject);
}
toggle(){
if (this.isOpen) {
this.close();
} else {
this.open();
}
}
open(){
this.targets.forEach(target => {
this[`$${target}`].classList.add(`revealer__${target}--open`);
})
this.isOpen = true;
}
close(){
this.targets.forEach(target => {
this[`$${target}`].classList.remove(`revealer__${target}--open`);
})
this.isOpen = false;
}
}
document.querySelectorAll('.revealer').forEach(el => {
new revealer(el);
})
JavaScript bei Verwendung von ABEM
class revealer {
constructor(el){
Object.assign(this, {
$wrapper: el,
isOpen: false,
});
this.$trigger = this.$wrapper.querySelector('.m-revealer__trigger');
this.$trigger.onclick = ()=> this.toggle();
}
toggle(){
if (this.isOpen) {
this.close();
} else {
this.open();
}
}
open(){
this.$wrapper.classList.add(`-open`);
this.isOpen = true;
}
close(){
this.$wrapper.classList.remove(`-open`);
this.isOpen = false;
}
}
document.querySelectorAll('.m-revealer').forEach(el => {
new revealer(el);
})
Dies war nur ein sehr einfaches Akkordeon-Beispiel. Denken Sie darüber nach, was passiert, wenn Sie dies auf etwas wie eine Sticky-Header erweitern, die sich beim Sticky-Sein ändert. Ein Sticky-Header muss möglicherweise 5 verschiedenen Komponenten mitteilen, wann der Header sticky ist. Dann müssen in jeder dieser 5 Komponenten möglicherweise 5 Elemente auf den Sticky-Header reagieren. Das sind 25 element.classList.add("[componentName]__[elementName]--sticky") Regeln, die wir in unserer JS schreiben müssten, um die BEM-Namenskonvention strikt einzuhalten. Was ist sinnvoller? 25 eindeutige Klassen, die auf jedes betroffene Element angewendet werden, oder nur eine -sticky Klasse, die auf den Header angewendet wird und auf die alle 5 Elemente in allen 5 Komponenten leicht zugreifen und sie lesen können?
Die BEM-„Lösung“ ist völlig unpraktisch. Die Anwendung von Modifier-Styling auf große, komplexe Komponenten wird zu einem Graubereich. Ein Graubereich, der zu Verwirrung bei allen Entwicklern führt, die versuchen, sich strikt an die BEM-Namenskonvention zu halten.
ABEM-Modifier-Probleme
Die Trennung des Modifiers ist nicht ohne ihre Tücken. Es gibt jedoch einige einfache Möglichkeiten, diese Tücken zu umgehen.
Problem 1: Verschachtelung
Also haben wir unser Akkordeon und es funktioniert perfekt. Später möchte der Kunde ein zweites Akkordeon in das erste verschachteln. Also tun Sie das… das passiert
Siehe Stift Akkordeon 3 – ABEM-Verschachtelungsfehler von Daniel Tonon (@daniel-tonon) auf CodePen.
Das Verschachteln eines zweiten Akkordeons in das erste verursacht einen ziemlich problematischen Fehler. Das Öffnen des übergeordneten Akkordeons wendet die Open-State-Styling auch auf alle untergeordneten Akkordeons in diesem Segment an.
Das ist etwas, das Sie offensichtlich nicht wollen. Es gibt aber einen guten Weg, dies zu vermeiden.
Um es zu erklären, spielen wir ein kleines Spiel. Angenommen, diese beiden CSS-Regeln sind auf dasselbe Element angewendet, welche Farbe denken Sie, würde der Hintergrund dieses Elements haben?
.-green > * > * > * > * > * > .element {
background: green;
}
.element.-blue {
background: blue;
}
Wenn Sie Grün sagen, wegen der ersten Regel, die eine höhere Spezifität als die zweite Regel hat, würden Sie tatsächlich falsch liegen. Sein Hintergrund wäre blau.
Fun Fact: * ist der niedrigste Spezifitätsselektor in CSS. Er bedeutet im Grunde „alles“ in CSS. Er hat eigentlich keine Spezifität, was bedeutet, dass er einem Selektor, den Sie ihm hinzufügen, keine Spezifität hinzufügt. Das bedeutet, selbst wenn Sie eine Regel verwenden, die aus einer einzelnen Klasse und 5 Sternen besteht (.element > * > * > * > * > *), könnte sie trotzdem leicht durch eine einzelne Klasse in der nächsten CSS-Zeile überschrieben werden!
Wir können uns diesen kleinen CSS-Trick zunutze machen, um einen zielgerichteteren Ansatz für den Akkordeon-SCSS-Code zu erstellen. Dies ermöglicht es uns, unsere Akkordeons sicher zu verschachteln.
Siehe Stift Akkordeon 4 – ABEM-Verschachtelungsfehler behoben von Daniel Tonon (@daniel-tonon) auf CodePen.
Durch die Verwendung des .-modifierName > * > & Musters können Sie direkte Nachkommen ansprechen, die mehrere Ebenen tief sind, ohne dass Ihre Spezifität außer Kontrolle gerät.
Ich verwende diese direkte Zieltechnik nur, wenn sie notwendig wird. Standardmäßig schreibe ich ABEM, wie im ursprünglichen ABEM-Akkordeon-Beispiel. Die nicht-zielgerichtete Methode ist in den meisten Fällen ausreichend. Das Problem mit dem zielgerichteten Ansatz ist, dass das Hinzufügen eines einzigen Wrappers um etwas das gesamte System potenziell brechen kann. Der nicht-zielgerichtete Ansatz leidet nicht unter diesem Problem. Er ist viel nachgiebiger und verhindert, dass die Stile brechen, wenn Sie das HTML später ändern müssen.
Problem 2: Namenskollisionen
Ein Problem, auf das Sie bei der Verwendung der nicht-zielgerichteten Modifier-Technik stoßen können, sind Namenskollisionen. Nehmen wir an, Sie müssen eine Reihe von Tabs erstellen und jeder Tab enthält ein Akkordeon. Beim Schreiben dieses Codes haben Sie sowohl die Akkordeons als auch die Tabs auf die -active Klasse reagieren lassen. Dies führt zu einer Namenskollision. Alle Akkordeons im aktiven Tab werden ihre aktiven Stile angewendet. Dies liegt daran, dass alle Akkordeons Kinder der Tab-Container-Elemente sind. Es sind die Tab-Container-Elemente, auf die die tatsächliche -active Klasse angewendet wird. (Weder die Tabs noch das Akkordeon im folgenden Beispiel sind aus Gründen der Kürze zugänglich.)
Siehe Stift Akkordeon in Tabs 1 – fehlerhaft von Daniel Tonon (@daniel-tonon) auf CodePen.
Nun, eine Möglichkeit, diesen Konflikt zu lösen, wäre, das Akkordeon einfach auf eine -open Klasse anstatt auf eine -active Klasse reagieren zu lassen. Ich würde diesen Ansatz empfehlen. Der Einfachheit halber nehmen wir jedoch an, dass dies keine Option ist. Sie könnten die oben erwähnte direkte Zieltechnik verwenden, aber das macht Ihre Stile sehr brüchig. Stattdessen können Sie den Komponentennamen vor dem Modifier hinzufügen, wie hier
.o-componentName {
&__elementName {
.-componentName--modifierName & {
/* modifier styles go here */
}
}
}
Der Bindestrich am Anfang des Namens signalisiert immer noch, dass es sich um eine Modifier-Klasse handelt. Der Komponentnename verhindert Namensraumkollisionen mit anderen Komponenten, die nicht betroffen sein sollten. Der doppelte Bindestrich ist hauptsächlich eine Anspielung auf die klassische BEM-Modifier-Syntax, um noch einmal zu bekräftigen, dass es sich um eine Modifier-Klasse handelt.
Hier ist das Akkordeon und Tab-Beispiel erneut, diesmal jedoch mit der Namensraum-Korrektur
Siehe Stift Akkordeon in Tabs 2 – korrigiert von Daniel Tonon (@daniel-tonon) auf CodePen.
Ich empfehle jedoch nicht, diese Technik standardmäßig zu verwenden, hauptsächlich um das HTML sauber zu halten und Verwirrung zu vermeiden, wenn mehrere Komponenten denselben Modifier gemeinsam nutzen müssen.
Die meiste Zeit wird eine Modifier-Klasse verwendet, um eine Zustandsänderung wie im obigen Akkordeon-Beispiel anzuzeigen. Wenn sich ein Element ändert, sollten alle Kindelemente, unabhängig von der Komponente, zu der sie gehören, diese Zustandsänderung lesen und leicht darauf reagieren können. Wenn eine Modifier-Klasse mehrere Komponenten gleichzeitig beeinflussen soll, kann es zu Verwirrung darüber kommen, zu welcher Komponente dieser Modifier speziell gehört. In diesen Fällen schadet die Namensraumierung des Modifiers mehr, als sie nützt.
Zusammenfassung der ABEM-Modifier-Technik
Um die ABEM-Modifier optimal zu nutzen, verwenden Sie standardmäßig die Syntax .-modifierName & oder &.-modifierName (hängt davon ab, welches Element die Klasse hat).
.o-componentName {
&.-modifierName {
/* componentName modifier styles go here */
}
&__elementName {
.-modifierName & {
/* elementName modifier styles go here */
}
}
}
Verwenden Sie direkte Zielerfassung, wenn die Verschachtelung einer Komponente in sich selbst ein Problem verursacht.
.o-componentName {
&__elementName {
.-nestedModifierName > * > & {
/* modifier styles go here */
}
}
}
Verwenden Sie den Komponentennamen im Modifier, wenn Sie gemeinsame Modifier-Namenskollisionen antreffen. Tun Sie dies nur, wenn Ihnen kein anderer Modifier-Name einfällt, der immer noch sinnvoll ist.
.o-componentName {
&__elementName {
.-componentName--sharedModifierName & {
/* modifier styles go here */
}
}
}
Kontextsensitive Stile
Ein weiteres Problem bei der strikten Einhaltung der BEM-Methode „eine Klasse pro Selektor“ ist, dass sie das Schreiben von kontextsensitiven Stilen nicht zulässt.
Kontextsensitive Stile sind im Grunde „wenn dieses Element innerhalb dieses Elternteils liegt, wende diese Stile darauf an“.
Bei kontextsensitiven Stilen gibt es eine Elternkomponente und eine Kindkomponente. Die Elternkomponente sollte diejenige sein, die Layout-bezogene Stile wie Ränder und Positionierung auf die Kindkomponente anwendet (.parent .child { margin: 20px }). Die Kindkomponente sollte standardmäßig immer keinen Rand außerhalb der Komponente haben. Dies ermöglicht die Verwendung von Kindkomponenten in mehr Kontexten, da die Eltern für ihr eigenes Layout verantwortlich sind und nicht ihre Kinder.
Genau wie bei echter Elternschaft sind die Eltern diejenigen, die die Kontrolle haben sollten. Sie sollten Ihre unartigen, ahnungslosen Kinder nicht die Entscheidungen treffen lassen, wenn es um das Layout der Eltern geht.
Um dieses Konzept weiter zu vertiefen, tun wir so, als würden wir eine brandneue Website erstellen und gerade die Abonnement-Formular-Komponente für die Website erstellen.
Siehe Stift Kontextsensitiv 1 – IE-unfreundlich von Daniel Tonon (@daniel-tonon) auf CodePen.
Dies ist das erste Mal, dass wir ein Formular auf dieser großartigen neuen Website erstellen mussten, die wir bauen. Wir wollen wie all die coolen Kids sein, also haben wir CSS Grid für das Layout verwendet. Wir sind aber schlau. Wir wissen, dass das Button-Styling an vielen weiteren Stellen auf der Website verwendet wird. Um uns darauf vorzubereiten, trennen wir die Abonnement-Button-Stile in eine eigene Komponente, wie gute kleine Entwickler.
Eine Weile später beginnen wir mit dem plattformübergreifenden Testen. Wir öffnen IE11 und sehen nur dieses hässliche Ding, das uns ins Gesicht starrt

IE11 unterstützt CSS Grid zwar irgendwie, aber es unterstützt grid-gap oder Auto-Platzierung nicht. Nach einigen kathartischen Schimpftiraden und dem Wunsch, dass die Leute ihre Browser aktualisieren, passen Sie die Stile an, sodass sie eher so aussehen
Siehe Stift Kontextsensitiv 2 – was man nicht tun sollte von Daniel Tonon (@daniel-tonon) auf CodePen.
Jetzt sieht es in IE perfekt aus. Alles ist richtig in der Welt. Was könnte schief gehen?
Ein paar Stunden später setzen Sie diesen Button-Component in eine andere Component auf der Website. Diese andere Component legt ihre Kinder ebenfalls mit CSS-Grid an.
Sie schreiben folgenden Code
Siehe Stift Kontextsensitiv 3 – die andere Komponente von Daniel Tonon (@daniel-tonon) auf CodePen.
Sie erwarten, dass ein Layout wie dieses auch in IE11 angezeigt wird

Aber stattdessen, wegen des früher geschriebenen grid-column: 3; Codes, sieht es am Ende so aus

Yikes! Was machen wir also mit diesem grid-column: 3; CSS, das wir früher geschrieben haben? Wir müssen es auf die Elternkomponente beschränken, aber wie sollen wir das machen?
Nun, die klassische BEM-Methode, dies zu behandeln, ist, eine neue Elternkomponenten-Elementklasse zum Button hinzuzufügen, wie hier
Siehe Stift Kontextsensitiv 4 – klassische BEM-Lösung von Daniel Tonon (@daniel-tonon) auf CodePen.
Oberflächlich betrachtet sieht diese Lösung ziemlich gut aus
- Sie hält die Spezifität niedrig
- Die Elternkomponente kontrolliert ihr eigenes Layout
- Die Formatierung wird wahrscheinlich nicht in andere Komponenten überschwappen, in die wir sie nicht haben wollen.
Alles ist großartig und alles ist richtig in der Welt… richtig?
Der Nachteil dieses Ansatzes liegt hauptsächlich darin, dass wir dem Button-Component eine zusätzliche Klasse hinzufügen mussten. Da die subscribe-form__submit Klasse in der Basis-button-Komponente nicht existiert, bedeutet dies, dass wir zusätzliche Logik zu dem hinzufügen müssen, was wir als unsere Template-Engine verwenden, damit sie die richtigen Stile erhält.
Ich liebe es, Pug zur Generierung meiner Seitenvorlagen zu verwenden. Ich zeige Ihnen, was ich meine, anhand von Pug-Mixins als Beispiel.
Zuerst ist hier der ursprüngliche IE-unfreundliche Code, umgeschrieben im Mixin-Format
Siehe Stift Kontextsensitiv 5 – IE-unfreundlich mit Mixins von Daniel Tonon (@daniel-tonon) auf CodePen.
Fügen wir nun diese IE 11 subscribe-form__submit Klasse hinzu
Siehe Stift Kontextsensitiv 6 – IE-sichere BEM-Lösung mit Mixins von Daniel Tonon (@daniel-tonon) auf CodePen.
Das war gar nicht so schwer, also was beschwere ich mich? Nun, sagen wir jetzt, wir wollen dieses Modul manchmal in einer Seitenleiste platzieren. Wenn wir das tun, wollen wir, dass das E-Mail-Eingabefeld und der Button übereinander gestapelt werden. Denken Sie daran, dass wir gemäß BEM nichts höher als eine einzelne Klasse in unseren Stilen verwenden dürfen.
Siehe Stift Kontextsensitiv 7 – IE-sicher BEM mit Mixins in der Seitenleiste von Daniel Tonon (@daniel-tonon) auf CodePen.
Dieser Pug-Code sieht jetzt nicht mehr so einfach aus, oder? Es gibt ein paar Dinge, die zu diesem Durcheinander beitragen.
- Container-Abfragen würden dieses Problem erheblich reduzieren, aber sie existieren noch nicht nativ in irgendeinem Browser
- Die Probleme rund um die BEM-Modifier-Syntax tauchen wieder auf.
Versuchen wir es jetzt noch einmal, aber diesmal mit kontextsensitiven Stilen
Siehe Stift Kontextsensitiv 8 – IE-sichere kontextsensitive mit Mixins in der Seitenleiste von Daniel Tonon (@daniel-tonon) auf CodePen.
Schauen Sie, wie viel einfacher die Pug-Markup geworden ist. Es gibt keine „Wenn dies, dann das“-Logik, um die sich in der Pug-Markup Sorgen gemacht werden müssen. All diese übergeordnete Logik wird an das CSS übergeben, das sowieso besser versteht, welche Elemente Eltern anderer Elemente sind.
Sie haben vielleicht bemerkt, dass ich im letzten Beispiel einen Selektor verwendet habe, der drei Klassen tief war. Er wurde verwendet, um dem Button 100% Breite zu geben. Ja, ein dreiklassen-Selektor ist in Ordnung, wenn Sie ihn rechtfertigen können.
Ich wollte nicht, dass 100% Breite dem Button jedes Mal angewendet wird, wenn er
- irgendwo verwendet wird
- im Abonnementformular platziert ist
- in der Seitenleiste platziert ist
Ich wollte nur 100% Breite anwenden, wenn er sowohl im Abonnementformular als auch in der Seitenleiste platziert war. Der beste Weg, damit umzugehen, war ein dreiklassen-Selektor.
Ok, in Wirklichkeit würde ich eher eine ABEM-ähnliche -verticalStack Modifier-Klasse für das subscribe-form Element verwenden, um die vertikalen Stack-Stile anzuwenden, oder vielleicht sogar über Element-Abfragen mit EQCSS. Dies würde bedeuten, dass ich die vertikalen Stack-Stile in mehr Situationen als nur in der Seitenleiste anwenden könnte. Der Einfachheit halber habe ich es jedoch als kontextsensitive Stile gemacht.
Nachdem wir nun kontextsensitive Stile verstehen, kehren wir zum ursprünglichen Beispiel zurück und verwenden kontextsensitive Stile, um die problematische grid-column: 3 Regel anzuwenden
Siehe Stift Kontextsensitiv 9 – kontextsensitives Verfahren mit Mixins von Daniel Tonon (@daniel-tonon) auf CodePen.
Kontextsensitive Stile führen zu einfacherer HTML- und Template-Logik und behalten gleichzeitig die Wiederverwendbarkeit von Kindkomponenten bei. Die Philosophie von BEM mit einer Klasse pro Selektor erlaubt dies jedoch nicht.
Da kontextsensitive Stile hauptsächlich mit dem Layout zu tun haben, sollten Sie sie je nach Umständen im Allgemeinen immer dann verwenden, wenn Sie sich mit diesen CSS-Eigenschaften beschäftigen
- Alles CSS-Grid-bezogene, das auf das Kindelement angewendet wird (
grid-column,grid-rowetc.) - Alles Flexbox-bezogene, das auf das Kindelement angewendet wird (
flex-grow,flex-shrink,align-selfetc.) margin-Werte größer als 0position-Werte außerrelative(zusammen mit den Eigenschaftentop,left,bottomundright)transform, wenn es zur Positionierung verwendet wird, wietranslateY
Sie können diese Eigenschaften auch in kontextsensitive Stile einfügen, aber sie werden seltener kontextsensitiv benötigt.
widthheightpaddingborder
Um ganz klar zu sein, kontextsensitive Stile sind nicht zum Verschachteln um des Verschachtelns willen da. Sie müssen sie sich vorstellen, als würden Sie eine if-Anweisung in JavaScript schreiben.
Also für eine CSS-Regel wie diese
.parent .element {
/* context sensitive styles */
}
Sie sollten sich vorstellen, als würden Sie diese Art von Logik schreiben
if (.element in .parent) {
.element { /* context sensitive styles */ }
}
Verstehen Sie auch, dass das Schreiben einer Regel, die drei Ebenen tief ist wie diese
.grandparent .parent .element {
/* context sensitive styles */
}
Sollte sich so vorstellen, als würden Sie Logik wie diese schreiben
if (
(.element in .parent) &&
(.element in .grandparent) &&
(.parent in .grandparent)
) {
.element { /* context sensitive styles */ }
}
Schreiben Sie also bitte einen CSS-Selektor, der drei Ebenen tief ist, wenn Sie wirklich denken, dass Sie dieses Maß an Spezifität benötigen. Verstehen Sie bitte die zugrundeliegende Logik des CSS, das Sie schreiben. Verwenden Sie nur ein Maß an Spezifität, das für die jeweilige Formatierung, die Sie erreichen möchten, sinnvoll ist.
Und noch einmal, nur um ganz klar zu sein, verschachteln Sie nicht um des Verschachtelns willen!
Zusammenfassung
Die Methodik hinter der BEM-Namenskonvention ist etwas, das ich von ganzem Herzen befürworte. Sie erlaubt es, CSS in kleine, leicht zu verwaltende Komponenten zu zerlegen, anstatt CSS in einem unhandlichen Durcheinander von hoher Spezifität zu hinterlassen, das schwer zu warten ist. Die offizielle Syntax für BEM hat jedoch viel zu wünschen übrig.
Die offizielle BEM-Syntax
- Unterstützt kein Atomic Design
- Ist nicht leicht erweiterbar
- Braucht länger, bis Ihr Gehirn die Gruppierung der Klassennamen verarbeitet hat
- Ist furchtbar inkompetent, wenn es um die Verwaltung von Zuständen auf großen Komponenten geht
- Ermutigt Sie, Einzelklassen-Selektoren zu verwenden, obwohl Doppelkeklassen-Selektoren zu einfacherer Wartbarkeit führen
- Versucht, alles zu benennen, selbst wenn die Namensraumierung mehr Probleme verursacht, als sie löst.
- Macht HTML extrem aufgebläht, wenn es richtig gemacht wird
Mein inoffizieller ABEM-Ansatz
- Erleichtert die Arbeit mit Atomic Design
- Gibt das Bindestrichzeichen als zusätzliche Methode zur Gruppierung frei
- Ermöglicht es Ihrem Gehirn, die Gruppierung der Klassennamen schneller zu verarbeiten
- Ist hervorragend geeignet, um Zustände auf Komponenten jeder Größe zu handhaben, egal wie viele Unterkomponenten sie hat
- Fördert kontrollierte Spezifität anstatt nur niedrige Spezifität, um Teamverwirrung zu vermeiden und die Wartbarkeit der Website zu verbessern
- Vermeidet Namensraumierung, wenn sie nicht benötigt wird
- Hält HTML mit minimalen zusätzlichen Klassen für Module recht sauber und behält dabei alle Vorteile von BEM bei
Haftungsausschluss
Ich habe die Idee des -modifier (einzelner Bindestrich vor dem Modifikatornamen) nicht erfunden. Ich habe sie 2016 entdeckt, als ich einen Artikel las. Ich erinnere mich nicht mehr, wer die Idee ursprünglich konzipiert hat. Ich werde sie gerne anerkennen, wenn jemand den Artikel kennt.
Update: 21. Januar 2018 (Kommentarantwort)
Niemand konnte den genauen Artikel verlinken, von dem ich über die -modifier Syntax gelernt habe. Was ich sagen kann ist, dass ich es gelernt habe, indem ich einen Artikel über BEVM (Block__Element–Variation -Modifier) gelesen habe.
Hier sind einige andere Leute, die die -modifier Syntax vor mir entwickelt haben
BEVM kann immer noch mit ABEM funktionieren, wenn Ihnen diese Methodik gefällt (was es zu ABEVM macht). Nachdem ich die -modifier Syntax eine Weile verwendet habe, habe ich die &--modifier Syntax schließlich ganz aufgegeben. Ich konnte keinen wirklichen Vorteil darin sehen, den doppelten Bindestrich beizubehalten, wenn der einfache Bindestrich in meinem CSS einfacher zu verwenden war und mein HTML viel sauberer machte.
Einige Leute haben BEMIT als sehr ähnlich bezeichnet. Sie haben Recht, es teilt einige Ähnlichkeiten mit BEMIT, hat aber auch einige Unterschiede.
Sie könnten ABEM und BEMIT bis zu einem gewissen Grad zusammenführen. Ich habe Leute gehört, die sagen, dass sie das explizite "is" der zustandsbasierten Klassen in BEMIT bevorzugen (z.B. .is-active). Das ist absolut in Ordnung, wenn Sie das "is" zu ABEM hinzufügen möchten, würde ich empfehlen, den Modifier so zu schreiben: .-is-modifierName. Sehen Sie, was ich mit camelCase meine, das benutzerdefinierte Gruppierungen ermöglicht?
Die Utilities können ebenfalls recht einfach aus BEMIT übernommen werden, sie würden immer noch als .u-utilityName geschrieben werden. Die „object“- und „component“-Namensräume in BEMIT würden nicht übernommen werden. Sie würden durch die Atomic Design-Namensräume in ABEM ersetzt werden.
Eine interessante Diskussion in den Kommentaren war die Verwendung der @extend-Funktionalität in Sass. Zum Beispiel die Verwendung von <button class='subscribe-form__submit'></button> und .subscribe-form__submit { @extend .button; grid-column: 3; }. Ich denke, kontextsensitive Stile sind der bessere Weg. Ich bin ziemlich stark gegen diese Implementierung von @extend, es sei denn, das CMS zwingt Sie dazu. Die vollständige Diskussion und meine Antwort finden Sie hier: https://css-tricks.de/abem-useful-adaptation-bem/#comment-1613824.
Eine Sache, mit der viele Leute Probleme hatten, war, dass ich nicht sehr tief auf das Konzept von Atomic Design eingegangen bin und wie man es verwendet. Es lag außerhalb des Rahmens dieses Artikels. Wenn ich versuchen würde, tief darauf einzugehen, wie Komponenten mit Atomic Design-Prinzipien kategorisiert werden, hätte dies leicht die Länge des Artikels verdoppelt (und dieser Artikel ist bereits sehr lang). Ich habe genug von einer Zusammenfassung gegeben, um das Konzept von Atomic Design einzuführen, und ich habe auf eine Ressource verlinkt, die viel tiefer in das Thema eintaucht. Das war ungefähr so viel Aufmerksamkeit, wie ich dem Erklären von Atomic Design widmen wollte.
Da die Kategorisierung von Atomic Design ein so verwirrendes Thema ist, habe ich vor, einen Artikel zu schreiben, der sich ausschließlich damit beschäftigt, wie man Komponenten von Atomic Design kategorisiert. Ich werde versuchen, klare Richtlinien zu erstellen, um herauszufinden, zu welcher Atomic Design-Kategorie eine bestimmte Komponente gehört. Erwarten Sie ihn jedoch nicht so bald. Es wird noch eine Weile dauern, bis er veröffentlicht wird.
Update: 2. April 2019 (Atomic-Kategorisierungswerkzeug)
Diese Pläne, einen Artikel über Atomic Design zu schreiben, wurden nie umgesetzt. Ich begann, einen zu planen, aber mir wurde klar, dass das Schreiben eines Artikels, um all die kleinen Dinge zu erklären, die bei der Kategorisierung von Modulen zu berücksichtigen sind, der falsche Weg war. Ein Werkzeug, das am Ende eine Empfehlung gibt, wäre weitaus nützlicher.
Also machte ich mich an die Arbeit und freue mich, den Start meines brandneuen Atomic Categorizer Tools ankündigen zu können!
Es ist ein einfaches Quiz, das Ihnen Punkte für jede Kategorie gibt, während Sie die Fragen beantworten. Die Fragen und Punkte sind vielleicht nicht 100% perfekt, aber ich denke, es macht die Kategorisierung der Komponenten meistens ziemlich gut. Probieren Sie es aus, es macht viel mehr Spaß, als ein paar tausend Wörter Text zu lesen. 😊
Nachdem ich BEM eine Weile verwendet hatte, habe ich meine Modifier natürlich in ihre eigene kleine Klasse verschoben, wie im Artikel beschrieben. Es fühlte sich für mich etwas seltsam an, eine Klasse zu haben, die mit einem
-beginnt, und der Modifier hatte nicht wirklich viel Kontext. Im Beispiel im Artikel kann ein Element die Modifier-small,-greenund-activehaben. Ich würde annehmen, dass-smallsGegenstück-large,-actives-disabledist und ein weiterer Farb-Modifier-redsein könnte. Ich würde auch annehmen, dass Sie niemals wirklich wollen, dass etwas-smallund-largeist, da dies keinen Sinn ergibt.Meine Entwicklung zu dieser Technik war die Verwendung von Datenattributen für Modifier. Ich glaube, es hat immer noch alle Vorteile der im Artikel vorgeschlagenen Modifier (kurz, abstrahierbar in JS) und ist auch besser darin, dass sie beschreibender und im CSS weniger unbeholfen aussehen. Zum Beispiel wäre ein Modifier
.block__element[data-size="large"][data-color="green"]. Jetzt sind meine Modifier nach der Art der Dinge gruppiert, die sie modifizieren.Gedanken zu dieser Methode?
Mein Hauptproblem damit wäre die Menge an zusätzlichem Tippaufwand.
.-greenist viel weniger Tipparbeit als[data-color=green]. Es bedeutet auch saubereres HTML.Ich glaube nicht, dass die Zuordnung eines Zustands zu einer bestimmten Eigenschaft wichtig ist, da die Eigenschaft impliziert werden kann. Und welche Art von Eigenschaft würde eine Klasse wie
.-stickyzugehören, wenn sie den Header beim Anwenden vollständig verändert?Klassen sind auch etwas einfacher in JS zu handhaben als Datenattribute. JS kann sie auch etwas schneller verarbeiten.
Außerdem sind die Leute daran gewöhnt, Klassen in CSS zu verwenden, als Datenattribute. Es ist viel einfacher, ein großes Dev-Team davon zu überzeugen, Klassen auf eine etwas andere Weise zu verwenden, als sie davon zu überzeugen, Klassen zugunsten von Datenattributen aufzugeben, besonders bei all dem zusätzlichen Tippaufwand, der mit diesem Ansatz verbunden ist. Es ist ein ziemlich harter Verkauf.
@Daniel Für mich ist weniger Tippaufwand kein großes Argument, besonders wenn ein paar Zeichen mehr die Absicht der Entwickler besser zeigen können. Ich verstehe, dass wir alle in sehr unterschiedlichen Teams arbeiten, aber für mich sollten Datenattribute etwas sein, das jeder kompetente Webentwickler handhaben kann, besonders wenn das Team eine strenge Namenskonvention wie BEM verwendet.
Für etwas wie sticky würde ich einfach
data-sticky="true"unddata-sticky="false"setzen.Ich würde argumentieren, dass Datenattribute in JS einfacher zu handhaben sind als Klassen.
datasethat heutzutage sehr gute Unterstützung. Anstatt einer Menge Logik zum Entfernen oder Hinzufügen von Klassen können Sie einfach das Dataset setzen. Zum Beispiel mit sticky: Anstatt Logik zum Hinzufügen oder Entfernen der Klasse können Sie einfach$element.dataset.sticky = isSticky;tun und es wird entsprechend auf true oder false gesetzt.Ich nehme an, der
-modifierwurde zuerst von http://rscss.io präsentiertInteressant. Es ist nicht das, woran ich gedacht habe, aber es scheint viele der Punkte zu wiederholen, die ich im Artikel gemacht habe, und es empfiehlt auch die Verwendung der
-modifierTechnik.Ich habe immer camelCase für JS verwendet. Nachdem ich diesen Artikel gelesen hatte, habe ich keine Ahnung, warum ich ihn nicht auch für CSS verwendet habe. Es ist leichter zu lesen. Ich bin überzeugt. Und übrigens, dieses ABEM-Konzept ist super.
Tatsächlich erklären Sie nicht viel über das Konzept von „Atomic“ Design.
Ich habe es vor ein paar Jahren ein wenig benutzt, aber ich denke, BEM veraltet dieses Konzept. Die Blockebene und die Benennung der darin enthaltenen Elemente sprechen meist für sich selbst.
Und Sie können immer noch gemeinsame „Atome“ oder „Moleküle“ mit eigenen Blocknamen haben.
Was die Änderung der Modifier angeht, stimme ich überhaupt nicht zu, weil ich glaube, dass sie mehr Probleme schafft, als sie zu lösen versucht (Namenskollision, was BEM explizit löst).
Wenn Sie eine Regel in BEM brechen wollen, bin ich bei Ihnen, aber brechen Sie die „einzelne Klasse pro Selektor“: Ich stimme vollkommen zu, dass ein Modifier auf N Elemente angewendet wird, weil der Block/Elternteil in einem bestimmten Zustand ist, macht keinen Sinn. Aber wenden Sie einfach einen Block/Element-Modifier auf diesen Eltern an:
accordion__section--open. Und wenden Sie dann kontextuelle Stile auf die Kindelemente an. Das ist viel einfacher.Die Spezifität wird minimal erhöht, die Verschachtelung des Selektors ist voll verständlich und BEM bleibt erhalten.
Übrigens verbietet BEM keine Selektor-Verschachtelung. Diese „Regel“ ist nur eine Best Practice in jeder Styling-Methode, da unnötige Verschachtelung die Spezifität ohne Vorteile erhöht.
Eine Sache, die mich wirklich stört, ist die Tatsache, dass Ihre Modifier unter „bestimmten Umständen“ „BEM-ähnlich“ sein werden und unter anderen nicht.
Abgesehen von der Inkonsistenz beginnen Sie mit „kurzen Modifikatoren“ zu entwickeln, aber wenn Sie ein Problem bemerken, wechseln Sie zu „BEM-ähnlichen Modifikatoren“, aber nur an einigen Stellen.
Diese Art der Namenswahl kann langfristig und in großen Projekten nicht aufrechterhalten werden. Die Regel muss jeden Fall berücksichtigen, sonst weiß man nie, was passieren wird.
Das Tolerieren von Verschachtelungen für kontextuelles Styling kann von Anfang an festgelegt und durchgehend eingehalten werden, es widerspricht BEM nicht, aber das Ändern Ihrer Namenskonvention innerhalb des Projekts je nach auftretender Namenskollision scheint mir kein valider Ansatz zu sein.
Es lag außerhalb des Rahmens des Artikels. Ich habe eine ausreichende Zusammenfassung gegeben, um das Konzept einzuführen, und ich habe auf eine Ressource verwiesen, die viel tiefer in das Thema eintaucht. Das ist ungefähr so viel Aufmerksamkeit, wie ich der Erklärung von Atomic Design widmen wollte.
Das ist ein fairer Punkt. Wenn Sie 100% Konsistenz wünschen, könnten Sie immer die
-componentName--modifierName-Methode für die Benennung von Modifiern wählen, aber Sie laufen in das Problem der Wiederholung, die die Bedeutung von Modifikatoren verschleiert, wie ich im Beispiel.-small.-green.-activeerklärt habe.Ich finde, dass das Problem, das es behebt, so selten ist, dass es sich nicht lohnt, den Komponentennamen überall anzugeben. Ich bevorzuge kürzere Klassennamen.
Danke dafür!
Ich habe nie verstanden, warum BEM die De-facto-Methode für CSS geworden zu sein scheint, viel zu viele Klassen.
Ich verwende nun schon seit einiger Zeit glücklich ._modifier-Klassen, finde sie leichter zu lesen als .-modifier (und weniger mehrdeutig?).
Guter Artikel, Daniel! Ich habe eine sehr ähnliche Namenskonvention in einem kürzlichen Projekt versucht, indem ich Klassennamen basierend darauf, wo das gegebene Element innerhalb des Atomic Design-Musters gehörte, vorangestellt habe. Ich fand, dass es sehr gut funktionierte und erwäge, es wieder zu verwenden. Danke fürs Teilen!
Dieser Ansatz erstellt jedoch nur obszön lange Klassennamen. Ich verstehe, dass viele Leute es nicht können, aber es sei denn, Sie stecken in einer älteren Codebasis fest, die Sie nicht refaktorieren können, verwenden Sie dies nicht. Verwenden Sie komponentenbasierte Verzeichnisstrukturen, bei denen Ihre CSS-Dateien im selben Verzeichnis wie ihre HTML/JS/was auch immer liegen.
Dieser Ansatz ist eine Methode, um viel kürzere Klassennamen zu erstellen, als es klassisches BEM Ihnen vorschreiben würde. :/
Ich verwende eine komponentenbasierte Verzeichnisstruktur, bei der meine CSS-Dateien im selben Verzeichnis wie die HTML/JS-Dateien liegen. Dieser Artikel schlägt nicht vor, das nicht zu tun. Dieser Artikel weist nur auf eine bessere Methode hin, BEM für Leute zu schreiben, die bereits Fans der BEM-Methodik sind.
Sie sehen in den Sass-Kommentaren, dass ich in den kontextsensitiven Stilbeispielen angebe, in welchem Komponenten-Datei-Abschnitt verschiedene Teile des Sass platziert werden.
Das Problem bei der Verwendung von CSS für Stile und der Nichtverwendung einer BEM/ABEM-Namenskonvention ist, dass Sie schnell auf Spezifitätsprobleme stoßen, bei denen alte Stile immer schwieriger zu überschreiben werden.
Danke fürs Teilen Ihrer Gedanken!
Nur ein paar Links zur Gerechtigkeit
https://en.bem.info/methodology/naming-convention/ und insbesondere https://en.bem.info/methodology/naming-convention/#alternative-naming-schemes — es kann hilfreich sein, Frieden zwischen der offiziellen BEM-Methodik und Ihrem Ansatz zu schließen (da sie sich nicht widersprechen).
Toller Artikel! Ich bin zum ersten Mal auf das
-modifierMuster in diesem Artikel gestoßen: https://www.viget.com/articles/bem-sass-modifiersNicht ganz der Artikel, an den ich gedacht habe, aber sehr ähnlich. Vielleicht schreibe ich eine Liste von Links zu allen, die es vor mir erfunden haben, lol.
Ich verstehe, dass dies eine kürzere Version von BEM ist, was gut ist. Dennoch fühlt es sich an, als müsste ich Raketenwissenschaft für das Schreiben von Klassennamen anwenden, was die Entwicklung sehr verlangsamt. Wir verwenden immer noch BEM im Unternehmen, aber nach einer bestimmten Größe wird alles seltsam, sowohl der CSS- als auch der HTML-Quellcode. Für JS sollte man auch kein BEM verwenden, sondern eine andere Klasse mit dem Präfix
js-. Seit über einem Jahr bevorzugen wir stattdessen CSS-Module und es ist, als würden wir Tonnen von Gewicht aus der Codebasis werfen, ich kann Klassen wie.activeoder.buttonwie in alten Zeiten verwenden. Sicher, ich verstehe, dass eine solche Lösung nicht immer für Projekte anwendbar ist (alte Codebasis, keine Transpilierung usw.), für diese BEM könnte eine Lösung sein, aber es wird die Tatsache nicht ändern, dass es nur mühsame Arbeit ist.Ich denke, das liegt daran, dass die BEM-Komponenten nicht in kleine genug Einheiten zerlegt werden. Deshalb mag ich Atomic Design. Es kann manchmal schwierig sein, herauszufinden, zu welcher Kategorie eine bestimmte Komponente gehört, aber der Akt des Versuchs, sie zu kategorisieren, hilft Ihrem Gehirn, in kleineren Komponenten zu denken. Kleinere Komponenten sind leichter wiederverwendbar als große Komponenten.
Ich denke, Sie sollten BEM immer noch für JS verwenden. Ich stimme zu, dass ein
js-Präfix zur Klassennamen hinzugefügt werden sollte. Ich bin mir noch nicht zu 100 % sicher, wie es zu ABEM hinzugefügt werden soll. Ich füge derzeit das JS-Präfix zum Atomic-Slot hinzu, wiejs-blockName__elementName, aber Sie verlieren die Verbindung dazu, zu welcher Kategorie es gehört.Wie unterstützen Sie IE11? Oder müssen Sie es nicht unterstützen?
Interessantes Konzept hier.
Ich stimme der Verwendung von .-open anstelle von .block–open oder .block__element–open nicht zu. Nach meinem Verständnis von BEM war die Verwendung von Modifiern nie der beabsichtigte Zweck für zustandsbasierte Änderungen. Ich denke, die Verwendung von zustandsbasierten Präfixen ist beschreibender, wie z. B.: .is-open, .is-closed, has-loaded, etc. Damit erhalten Sie .block.is-open oder .block__element.is-open anstelle von .block.block–open oder .blcok__element.block__element–open.
Man muss sagen, dass die meisten meiner Kenntnisse von BEM auf den Gedanken von CSSWizardry basieren, insbesondere BEMIT (https://csswizardry.com/2015/08/bemit-taking-the-bem-naming-convention-a-step-further/).
Ich denke jedoch, dass ABEM echtes Potenzial für eine Namenskonvention haben könnte, wenn Atomic Design Ihre bevorzugte Methodik ist.
Es ist immer gut zu sehen, wie Leute den aktuellen Status hinterfragen.
Eine Sache, die mir aufgefallen ist, ist, dass viele Leute versuchen, ATOMIC Design für CSS anzuwenden. Aber der Autor selbst hat erwähnt, dass es tatsächlich nichts mit CSS zu tun hat
http://atomicdesign.bradfrost.com/chapter-2/#atomic-design-is-for-user-interfaces
Die Verwendung von Organismen, Molekülen und Atomen ist meiner Meinung nach eine unnötige zusätzliche Komplexitätsebene. Jeder, der damit gearbeitet hat, weiß, dass es schwierig ist, die Unterschiede zwischen ihnen herauszufinden. Man verbringt oft viel Zeit damit zu streiten, ob etwas ein Molekül oder ein Atom ist oder nicht. Ich bevorzuge es, einfach alles
Componentzu nennen. Indem Sie sie Komponenten nennen, laufen Sie auch keine Probleme, wenn Sie ein Molekül in ein Molekül verschachteln müssen… denn das ist doch seltsam, oder?Ich empfehle jedem dringend, CSS-Namespacing und BEMIT (einschließlich ITCSS) von Harry Roberts zu verwenden
https://csswizardry.com/2015/03/more-transparent-ui-code-with-namespaces/
https://csswizardry.com/2015/08/bemit-taking-the-bem-naming-convention-a-step-further
Du hast Recht. Atomic Design ist lediglich ein Konzept zur Organisation Ihrer Komponenten. Das Einzige, was das Atomic-Präfix in ABEM tut, ist, Ihnen mitzuteilen, in welchem Ordner sich die Komponente befindet, damit die Komponente leichter zu finden ist. Es hat keine Auswirkungen auf das CSS, außer dass Ihnen zusätzliche Informationen angezeigt werden, wenn Sie im Element-Inspektor in den Entwicklertools nachschauen. Wenn Sie Atomic Design nicht mögen, funktionieren alle anderen von mir eingeführten Konzepte weiterhin perfekt, wenn das Atomic-Präfix entfernt wird.
Ich muss zugeben, dass ich auch Schwierigkeiten hatte, wenn es darum ging, zu kategorisieren, welche Art von Komponente etwas ist. Normalerweise stolpere ich darüber, ob etwas ein Organismus oder ein Molekül sein sollte. Es ist auch etwas seltsam, ein Atom um einen Organismus zu wickeln. Ich denke, ich werde irgendwann einen Artikel schreiben, der erklärt, wie man Komponenten von Atomic Design kategorisiert.
Auch wenn ich damit ein wenig zu kämpfen hatte, liebe ich es immer noch. Es hat die Wiederverwendbarkeit meines Codes erheblich verbessert. Selbst wenn Sie es ein wenig vermasseln, hilft der Prozess des Versuchs, Ihre Komponenten in Atome, Moleküle und Organismen zu kategorisieren, Ihrem Gehirn, Dinge in kleinere, wiederverwendbarere Komponenten zu zerlegen.
Zum Beispiel habe ich früher immer ein Akkordeon als eine einzige große Komponente codiert. Jetzt denke ich an ein Akkordeon als eine große "Akkordeon"-Organismus-Komponente, die eine Reihe von kleinen "Revealer"-Molekül-Komponenten enthält. Wann immer ich etwas benötige, das zusätzliche Details enthüllt, kann ich jetzt ein Revealer-Molekül getrennt vom Akkordeon-Organismus verwenden, anstatt jedes Mal benutzerdefinierten Code schreiben zu müssen, wenn ich Revealer-Funktionalität benötige.
Das Problem, die Kategorisierung zu vermasseln, ist der Grund, warum das Atomic-Präfix im Klassennamen benötigt wird. Wenn eine Atomkomponente wirklich als Molekül kategorisiert hätte werden sollen, dann müssen Leute mit dem Atomic-Präfix auf dem Klassennamen zumindest nicht Zeit damit verschwenden, den gesamten Ordner der Moleküle zu durchsuchen, nur um festzustellen, dass er im Ordner der Atome gespeichert wurde. Wenn es spät im Projekt ist, könnte es zu riskant sein, den Ordner zu ändern, in dem sich eine Komponente befindet.
Es läuft wirklich auf Übung hinaus. Zuerst wirst du die Kategorisierung oft vermasseln. Nachdem du Atomic Design jedoch eine Weile verwendet hast, wirst du instinktiv genau wissen, welche Komponenten als was kategorisiert werden sollten.
Hallo,
Mir scheint, dass, so sehr der einzelne Modifikator (
-active) alles vereinfacht, er in manchen Fällen einfach viele Dinge vermasselt, und ich bin mir nicht sicher, ob jeder in meinem Team die Nerven hätte, nachzuschauen, wo er ein> * >hinzufügen muss, um es zu beheben.Ich glaube, wir bleiben lieber beim normalen B.E.M.
Ein weiterer toller Trick, den ich bei der Verwendung von B.E.M mag, ist die Verwendung der
@extend-Funktionalität.Du könntest es in deinem Beispiel so verwenden
Anstatt dem Submit-Button zwei Klassen zu geben (1.
button, 2.subscribe-form__submit), kannst du ihm einfach die Klassesubscribe-form__submitgeben und dann das .button-Styling innerhalb seines Selectors mit@extendhinzufügen..subscribe-form__submit {@extend .button;
grid-column: 3;
}
Das HTML würde so aussehen
<button class='subscribe-form__submit'>Anstatt diesem
<button class='button subscribe-form__submit'>Was denkst du?
Mein Hauptkritikpunkt an dieser Methode ist, dass sie eine riesige Menge an zusätzlichem Kram in deinen CSS-Stylesheet erzeugt. Du zwingst dann deine Benutzer, all diesen zusätzlichen Kram herunterzuladen, wenn sie deine Seite besuchen.
Nehmen wir an, du hast 50 Buttons auf deiner Seite, die die
.button-Komponente erweitern. Das ist die Art von CSS, mit der du am Ende dastehst.Das war nur ein Button mit Standard-Styling und Hover-/Focus-Styling. Du siehst, wie lächerlich das werden kann.
Vergleiche das nun mit diesem
Wenn du Klassen so verwendest, wie sie gedacht sind, bleiben deine CSS schlank und sauber.
Außerdem, niemals eine Klasse direkt erweitern. Erweitere immer nur Sass
%-Klassen. Das direkte Erweitern von Klassen kann zu wirklich seltsamen und unerwarteten Nebeneffekten führen.Also anstatt diesem
Solltest du dieses tun
Danke, dass du deine Methodik teilst. Ich erinnere mich, dass ich letztes Jahr versucht habe, dasselbe Problem mit mehreren fast identischen Klassennamen zu lösen, indem ich nur "element--modifier" verwendet und es in CSS mit "element, element--modifier { ... }" definiert habe. Dein Ansatz scheint viel besser zu sein!
Nichts Dramatisches, aber Selektoren wie ":first-child" verletzen deine Camelcase-Regel.
Zuerst einmal muss ich sagen, dass ich diesen Artikel wirklich genossen habe und seitdem mit der Idee spiele. Der frustrierende Teil daran ist, dass ich in diesem oder anderen Artikeln keine Beispiele dafür finden kann, wie Atome und Moleküle miteinander zusammenhängen.
Zum Beispiel, wenn ich ein Headline-Atom
a-headlinehabe und dann dieses Atom und eina-text-Atom in ein Molekülm-section-headerpacken muss, aber der Text verschiedene Farben haben soll, wo wird dieser Modifikator angewendet? Auf Molekül- oder Atomebene? Würde ich dieh3- undp-Tags in einen Div packen und diesem Div etwas wiem-section-header__titleundm-section-header__textgeben und die Farbänderungen diesen Divs hinzufügen? Ich benutze BEM seit etwa zwei Jahren und liebe die Idee, Atomic Design zu integrieren, aber es verwirrt mich ein wenig.Siehe den Stift ABEM Experiments von Kendall (@KendallWhitman) auf CodePen.
Ich freue mich, dass dir der Artikel gefallen hat :)
Ich wollte in diesem Artikel nicht zu tief darauf eingehen, wie Atomic Design damit funktioniert, da das ein so tiefes Thema ist, dass es einen eigenen Artikel füllen könnte. Es war für diesen hier außer Reichweite.
Ich frage mich, warum du nicht CamelCase verwendest? Ich habe ein Drittel des Artikels damit verbracht, zu erklären, warum du CamelCase verwenden solltest, wenn du ABEM verwendest :/
Was deine Frage angeht, hängt es stark vom Kontext ab. Unter welchen Umständen möchtest du, dass der Text unterschiedlich gefärbt ist?
Wenn du die Farbe nur bei diesen spezifischen Atomen ändern möchtest, wenn sie sich in diesem spezifischen Molekül befinden, dann ist es vielleicht am besten, die Farbänderung als kontextsensitive Stile vorzunehmen. Kontextsensitive Stile müssen nicht nur für Layouts verwendet werden, es ist nur der häufigste Bedarf dafür. Sie sollten immer dann verwendet werden, wenn du Stile basierend auf den Eltern anwenden musst.
Wenn du diese Änderung der Farbe allgemeiner anwenden möchtest, ist es wahrscheinlich besser, die
-colourName-Klasse direkt auf die Atome anzuwenden.Wenn du die beiden verknüpfen möchtest, könntest du die
.-colourName-Klasse auf das Molekül anwenden und dann in den Atom-scss-Dateien Folgendes tun:&.-colourName, .- colourName & { ... }. Das wendet die Farbe an, wenn der Modifikator entweder auf das Element selbst *oder* einen Elternteil angewendet wird.Ich denke, der sauberste Weg, dies in deinem Beispiel zu handhaben, wäre, den Modifikator auf das Molekül anzuwenden und
&.-colourName { color: [colour-name]; }zu verwenden, um die Farbänderung anzuwenden, da Farbe vererbt werden kann.Es hängt alles vom Kontext und den Umständen ab, unter denen die Farbe angewendet werden soll.
Ich denke, der sauberste Weg, dies in deinem Beispiel zu handhaben, wäre, den Modifikator auf das Molekül anzuwenden und
&.-colourName { color: [colour-name]; }zu verwenden, um die Farbänderung anzuwenden, da Farbe vererbt werden kann.Danke, dass du deine Methodik teilst. Ich würde auch empfehlen, einen Blick auf die Namenskonventionen von SUIT CSS zu werfen.
https://github.com/suitcss/suit/blob/master/doc/naming-conventions.md
Meiner Meinung nach sind die SUIT-Komponenten-Zustandsklassen expliziter, indem sie das Präfix
is-anstelle eines einzelnen Bindestrichs hinzufügen.Es gibt keinen Grund, warum du
is-nicht in die ABEM-Modifikatorsyntax integrieren kannst. Du könntest etwas wie.-is-modifierNamemachen. Siehst du, was ich mit CamelCase meine, das es dir ermöglicht, deine eigenen benutzerdefinierten Gruppierungen zu erstellen?Ich mag es, den Bindestrich am Anfang zu behalten, da es immer noch ein Modifikatorname ist und ein Bindestrich am Anfang = Modifikator.
Harry Roberts hat vor ein paar Jahren etwas Ähnliches vorgeschlagen. Ich benutze seitdem eine Form davon.
https://csswizardry.com/2015/03/more-transparent-ui-code-with-namespaces/
Wahrscheinlich stammt dein
-modifier-Konzept aus der Lektüre eines Artikels über BEVM. BEVMblock__element--variation -modifierist eine BEM-Variante, die verkettbare Modifikatoren hinzufügt. Ich benutze eine Form davon seit ein paar Jahren.Ja, es war ein BEVM-Artikel :)
Ich habe versucht, die --V-Syntax eine Weile zu verwenden, aber ich habe sie schließlich zugunsten der konsequenten Verwendung der -M-Syntax aufgegeben. Ich konnte keine Situation finden, in der --V nützlicher war als die -M-Syntax, und der Artikel, den ich gelesen habe, deutete darauf hin, dass --V ohnehin nicht kritisch war und man es vergessen konnte, wenn man wollte.
Bezüglich des
-modifierDu bist vielleicht auf meinen Artikel auf Medium gestoßen, den ich im März 2016 veröffentlicht und nach einer Unterhaltung mit einem Entwickler von Yandex gelöscht habe.
Wir haben
-modifieraus unserem Projekt aus mehreren Gründen entfernt1. IE und einige mobile Browser unterstützen keine Klassen, die mit
-beginnen.2. Das Problem mit der Spezifität ist, dass jemand anfängt, verkettete Modifikatoren zu verwenden, und wir kehren zur Spezifitäts-Hölle zurück.
(zum Beispiel
btn -icon -tinyund Überschreibungen von Padding-Regeln von-tinymit fester Höhe/Breite für kleinere Icon-Buttons unter Verwendung von etwas wie.btn.icon.tinyund andere Modifikatoren hören wegen der Spezifität auf zu funktionieren)Übrigens
BEM ist eine Methodik, Namenskonvention ist nur ein Teil davon.
https://en.bem.info/
Zu Punkt 1 weiß ich, dass IE11 den Bindestrich-Präfix am Anfang von Klassen unterstützt. Alle anderen IEs sind im Grunde tot.
Ich bin daran interessiert zu sehen, welche mobilen Browser ihn nicht unterstützen. Ich vermute Opera Mini, da Opera Mini fast nichts unterstützt. Zum Glück ist es kein Browser, den ich persönlich unterstützen muss.
Was Punkt 2 angeht, denke ich immer noch, dass die Trennung des Modifikators besser ist. Die von dir angesprochenen Probleme klingen eher nach einem Problem mangelnden Trainings oder mangelnder Ressourcen (sodass BE-Entwickler FE-Code schreiben müssen). Wenn es richtig verwendet wird, macht es Websites leichter zu warten und das HTML viel leichter zu lesen.
Hallo,
Danke für diesen Artikel! Ich habe BEM nur oberflächlich kennengelernt und hatte immer ein schlechtes Gefühl bei dieser Regel für Einzelselektor-Klassen in OOCSS im Allgemeinen und angewendet in BEM mit furchtbar aussehenden Klassen.
Jetzt, da das Hinzufügen von Spezifität durch kombinierte Klassen perfekt logisch erscheint, wäre es möglich, anstatt Klassen zu verwenden, Aria-Attribute zu verwenden, wenn es sinnvoll und möglich ist, wie hier vorgeschlagen: http://alistapart.com/article/meaningful-css-style-like-you-mean-it#section3
Die Spezifität wäre die gleiche wie bei Klassen und wir würden von einem semantischeren Ansatz profitieren.
Was denkst du?
Ich bin nicht gegen die Idee.
Ich stimme nicht mit der gesamten Prämisse des Artikels überein, wo er uns dazu bringen will, Klassen so weit wie möglich zu vermeiden. Ich mache mir Sorgen, dass die Spezifität außer Kontrolle gerät. Auch [type=text] deckt nicht alle anderen Textfeld-Stil-Eingaben wie E-Mail ab. Ich denke auch, dass es das Stylesheet etwas unordentlich machen würde.
Es ist keine schlechte Idee, die semantischen Aria-Attribute für zustandsbasierte Stile zu verwenden, anstatt wo immer möglich Modifikatorklassen.
Immer noch schlimm genug, um BEM zu verwenden, warum kann Functional CSS für diese Fälle funktionieren, wo du eine Vorlagen-Engine oder React/Vue/Hyperapp verwenden könntest, um deine Komponente und Modifikatoren nach Daten wiederzuverwenden?
In den meisten Fällen ist Functional CSS wie semantisches HTML einfacher zu betrachten.
Ich bin kein Fan von Atomic CSS-Bibliotheken wie Basscss und Tachyons.
Dieser Artikel gibt eine großartige Zusammenfassung aller Probleme mit dieser Art der Klassennamengebung.
"The Problem with Atomic CSS" https://medium.com/simple-human/the-problem-with-atomic-css-d0c09c7aa38e
Es klingt, als wärst du ein Fan von CSS-in-JS.
Erstens funktioniert CSS-in-JS nur, wenn die Website vollständig in etwas wie React/Vue erstellt ist. Eine Website auf diese Weise zu erstellen, macht nur Sinn, wenn ständig eine immense Menge an Zustandsänderungen stattfindet. Etwas wie eine einfache Broschüren-Website, bei der praktisch keine Zustandsänderungen zu befürchten sind, sollte nicht in React/Vue erstellt werden.
Zweitens bin ich einfach kein Fan von CSS-in-JS. Es ist hauptsächlich eine persönliche Präferenz, aber dieser Artikel skizziert ganz gut, warum man CSS-in-JS vermeiden sollte.
"Stop using CSS in JavaScript for web development" https://medium.com/@gajus/stop-using-css-in-javascript-for-web-development-fa32fb873dcc