Bem il Mostro Umano

BEM: Grammatica per la buona scrittura CSS

BEM: Cos’è?

La regola delle 5W (What, Who, Where, When, Why) ci insegna da dove partire per scrivere un buon articolo. Tuttavia la prima domanda in questione non è ‘che cos’è BEM?’ ma cosa non è BEM. BEM non è il mostro umano dell’omonima serie anime degli anni ’70 (a cui, per inciso, è dedicata la copertina di questo post); non è la Biblioteca di Economie e Management dell’università; non è nemmeno un framework.

BEM, o Block Element Modifier, è una metodologia per i nomi, una convenzione di denominazione, utilizzata quando si scrive codice CSS.

Sintassi base di BEM

There are only two hard problems in Computer Science: cache invalidation and naming things

Phil Karlton

Evitare l’effetto farfalla

Non utilizzare un metodo, sul lungo periodo, porta la manutenzione del codice a un’impresa colossale. Quando uno sviluppatore deve modificare un CSS è come un artificiere che disinnesca una bomba: la modifica su un componente può farne esplodere un altro.

Dove alberga il caos è quasi impossibile prevedere l’esito di determinati eventi e, come nell’effetto farfalla, capita quel codice dove:

[…] people don’t know what things do any more. People daren’t make changes because they don’t know how far reaching the effects will be.

Harry Roberts

Ed è qui che interviene BEM permettendoci di creare un codice pulito, leggibile e mantenibile con 3 vantaggi specifici:

La classe comunica lo scopo o la funzione e la struttura dei componenti

Stabilisce specificità per i selettori da utilizzare nel CSS, evitando selettori di discendenza che portano ad attribuire uno stile specifico sul “::before all’hover del secondo strong del li diretto dell’ul figlio del nav con classe ‘c-nav’ discendente del div con classe ‘c-container‘”. Rende abbastanza l’idea?

.c-container nav.c-nav ul > li strong:nth-of-type(2):hover::before {}

Modularità e flessibilità: utilizzando blocchi indipendenti, questi possono essere ricomposti e riconfigurati in base alle esigenze permettendo un riutilizzo del codice con conseguente modularità.

Sintassi di BEM

Sebbene l’ortografia sia interpretata in diversi modi, come approfondiremo in seguito, la sintassi di BEM segue la struttura [block]__[element]–[modifier]:

Block: è un componente indipendente, rappresenta il padre e può essere annidato all’interno di altri block ma non è dipendente da questi. Il nome della classe può essere composto di lettere, numeri o trattini. Esempio una card:

<div class="c-card"></div>

Element: è un elemento dipendente da un block. Il nome della classe può essere composto di lettere, numeri o trattini e underscore (di solito, il nome è composto dal nome del blocco e dal nome dell’elemento figlio). Esempio un titolo di una card:

<div class="c-card">
   <h2 class="c-card__title"></h2> 
</div>

Nella scelta dei nomi degli elementi bisogna far sempre riferimento al Block nonostante un annidamento di elementi su più livelli.

Nomenclatura corretta

<div class="c-card">
   <header class="c-card__header"> 
      <h2 class="c-card__title"></h2> 
   </header> 
</div>

Nomenclatura scorretta

<div class="c-card"> 
   <header class="c-card__header"> 
      <h2 class="c-card__header__title"></h2> 
   </header> 
</div>

Modifer: è la variante che spesso modifica solamente una proprietà di un element o di un block: colore, font, background. Nell’HTML la classe del modifier si inserisce subito dopo quella che identifica il blocco o l’elemento, e ripete al suo interno il nome della classe del blocco o dell’elemento stesso seguito da 2 trattini e nome della variazione. Esempio una card che si differenzia per il colore verde o un titolo che fa eccezione per essere più grande rispetto agli altri:

<div class="c-card c-card--green">
   <h2 class="c-card__title c-card__title--big"></h2>
   <a href="#" class="c-card__button c-button c-button--ghost"></a>
</div>

Utilizzare BEM con SASS

L’accoppiata BEM e SASS a questo punto appare naturale. Grazie all’uso del Nesting e della direttiva & che permette un’interpolazione con il nome del blocco è possibile rendere ancora più modulare il codice.

Per esempio:

/*SCSS*/
.c-card {
   font-size:16px;
   &__header{
      text-align:center;
   }
   &--big {
      font-size:20px;
   }
   &:hover {
      color: green;
   }
}

/*CSS COMPILATO*/
.c-card {
   font-size:16px;
}
.c-card__header {
   text-align:center;
}
.c-card--big {
   font-size:20px;
}
.c-card:hover {
   color:green;
}

Non è tutto oro quel che luccica

BEM, come tutto, presenta comunque alcune criticità:

  • Ridondanza del codice HTML: i nomi delle classi possono diventare abbastanza lunghi
<a href="#" class="c-card__button c-button c-button--ghost"></a>
  • Dubbi sui nomi degli elementi dipendenti dal block o di un blocco contenuto in un altro blocco. Citando Linneo “Se non conosci il nome, muore anche la conoscenza delle cose”.
  • Si tratta di uno standard NON standard: BEM non è nato con una regolamentazione precisa e spesso è interpretato in modo differente da sviluppatore a sviluppatore come uso dei trattini, uso del doppio underscore, uso della doppia classe per il modifier. Su questo ultimo punto, infatti, secondo la sintassi BEM il componente avrà 2 classi, la prima con tutte le stilizzazioni, la seconda solo con le modifiche da applicare.
<a href="#" class="c-button c-button--ghost">

.c-button {
	display: inline-block;
	font-size: 16px;
	border-radius: 4px;
	padding:8px 16px;
	background-color:green;
	color: white;
} 

.c-button--ghost {
	color:green;
	background-color: transparent;
	border:1px solid green;
}

Utilizzando SASS, grazie alla funzione @extend, al modifier può essere applicato direttamente lo stile del block o dell’element.

<a href="#" class="c-button--ghost">

.c-button {
	display: inline-block;
	font-size: 16px;
	border-radius: 4px;
	padding:8px 16px;
	background-color:green;
	color: white;
} 

.c-button--ghost {
	@extend .c-button;
	color:green;
	background-color: transparent;
	border:1px solid green;
}

Questo renderebbe inutile richiamare anche la classe del blocco, snellendo il codice HTML ma scontrandosi con un’irregolarità della sintassi BEM.

Alternative a BEM

Ogni scelta implica l’abbandono di tutte le alternative. Non è proprio così ma esistono diverse alternative a BEM.

ABEM

Non si tratta di una vera alternativa quanto di un accomodamento introducendo un prefisso relativo alle convenzioni dell’Atomic Design.

Il prefisso a/m/o permette di organizzare i componenti in 3 tipologie:

  • Atomi: componenti semplicissimi che consistono in un singolo elemento (es. un pulsante);
  • Molecole: piccoli gruppi di elementi (es. un campo di un form con una label e un input);
  • Organismi: componenti grandi e complessi costituiti da molecole e atomi (es. un form di registrazione).

L’aggiunta del prefisso atomico all’inizio rende immediatamente evidente la tipologia del componente.

ABEM

SMACSS

Come dei blocchi Lego che ci permettono di assemblare ogni sorta di cosa, la metodologia Scalable and Modular Architecture per CSS rende il codice CSS scalabile e modulare.

La sua architettura si basa su 5 categorie di regole:

Base: sono le regole base, si tratta quasi esclusivamente dello stile applicato ai selettori (compresi selettori di attributi, pseudoclassi, selettori figli), senza l’uso di classi o id. Quello stile che spesso viene utlizzato per resettare lo stile di default degli elementi HTML o che deve applicarsi a quell’elemento in ogni pagina. Questo set di regole sono generalmente scritte a inizio pagina, ad aprire il nostro codice CSS.

html, body, form { 
   margin: 0; padding: 0; 
}
input[type=text] {
   border: 1px solid #999; 
} 
a { 
   color: #039; 
} 
a:hover { 
   color: #03C; 
}

Layout: suddivide la pagina in sezioni con elementi come header, footer, main ecc. Spesso gli sviluppatori prefissano gli elementi di layout con l-. <div class="l-container"></div>

Moduli: parti modulari e riutilizzabili come navbar, sidebar e gli elementi che si ripetono all’interno del sito.

Stati: utilizzati sia nei moduli che nei layout per descrivere un particolare stato (es. active, hidden ecc.) e sono prefissati, di solito, con “is-“.

<section class="c-accordion"> 
   <div class="c-accordion__item is-active"></div> 
   <div class="c-accordion__item"></div> 
   <div class="c-accordion__item"></div> 
</section>

Temi: descrive come i moduli o i layout possono apparire. Sono i set di regole CSS per creare uno stile specifico.

OOCSS

L’Object Oriented CSS afferma che le interfacce sono costituite da più componenti, che dovrebbero essere flessibili e riutilizzati il più possibile.

Si basa su 2 principi:

  • Separazione fra struttura e presentazione: significa separare la struttura di un elemento dal suo aspetto
#button {
   width: 200px;
   height: 50px;
   padding: 10px;
   border: solid 1px #ccc;
   background: linear-gradient(#ccc, #222);
   box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}

#box {
   width: 400px;
   overflow: hidden;
   border: solid 1px #ccc;
   background: linear-gradient(#ccc, #222);
   box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}

#widget {
   width: 500px;
   min-height: 200px;
   overflow: auto;
   border: solid 1px #ccc;
   background: linear-gradient(#ccc, #222);
   box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}

Ai 3 elementi è applicato uno stile tramite un ID non riutilizzabile sebbene abbiano diverse righe di stile in comune.

Dunque potrebbe essere trasformato così:

.button {
   width: 200px;
   height: 50px;
}

.box {
   width: 400px;
   overflow: hidden;
}

.widget {
   width: 500px;
   min-height: 200px;
   overflow: auto;
}

.skin {
   border: solid 1px #ccc;
   background: linear-gradient(#ccc, #222);
   box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}

Utilizzando le classi e combinando lo stile in comune in un riutilizzabile .skin non viene necessariamente ripetuto.

  • Separazione fra contenitore e contenuto: usare stili che sono specifici di un elemento e non dipendere in questa operazione dalla posizione.
    Prendiamo l’esempio seguente:
#sidebar h3 {
   font-family: Arial, Helvetica, sans-serif;
   font-size: .8em;
   line-height: 1;
   color: #777;
   text-shadow: rgba(0, 0, 0, .3) 3px 3px 6px;
}

Lo stile si applica a un H3 figlio di #sidebar. Se lo stesso stile si dovesse applicare a un h3 figlio di un footer non dovremmo essere obbligati a ripeterci.

OOCSS incoraggia a riflettere maggiormente su ciò che è in comune e separare quelle caratteristiche che possono essere riutilizzate ovunque.

ITCSS

Meno conosciuto ma comparato a SMACSS e OOCSS, l’idea dell’Inverted Triangle CSS è una suddivisione delle proprietà CSS a più livelli in base al loro livello di specificità e importanza che può essere rappresentata come i gironi infernali di Dante.

01 inverted triangle css itcss diagram

Il triangolo si sposta dall’alto verso il basso:

  • Setting: variabili e metodi del preprocessore, non c’è output css;
  • Tools: Mixin e funzioni, non c’è output css;
  • Generic: Reset del css di default;
  • Elements: selettori di elementi HTML singoli, senza classe
  • Objects: utilizzo di classi per la strutturazione della pagina seguendo la metodologia OOCSS;
  • Components: utilizzo di classi per lo styling di tutti i componenti;
  • Trumps: Stili più specifici che sovrascrivono ogni altra cosa nel triangolo.

CSS Semantico o funzionale?

BEM, così come OOCSS, si basano su un approccio semantico, sull’idea di separazione completa fra stile e markup. Le classi CSS non hanno riferimenti allo stile del componente ma ne rappresentano solo l’identità (es. .c-header, .c-card , c-block ecc.).

Dunque: niente #ID, pochi !important e usare il meno possibile i selettori a cascata.

Una conseguenza diretta è stata la duplicazione del codice per quei componenti che condividevano le stesse caratteristiche. Le metodologie sopra descritte come BEM oltre a pre e post processori CSS come SASS ci vengono in soccorso per arginare questo problema.

Con i primi framework (come Bootstrap) vengono introdotte delle classi per personalizzare i componenti, esempio .text-center o .hidden. Decisamente poco semantiche e chiaramente delle Utility Classes, ovvero delle classi funzionali che facilitano lo sviluppo di siti anche per chi non ha competenze avanzate di CSS. Ormai esistono framework composti unicamente da Utility Classes come Tailwind che si autodefinisce come un utility fist css framework.

Il miglior approccio? Probabilmente non c’è una risposta giusta e una scorretta. Forse la risposta, per quanto irritante, sfuggevole e misteriosa è ‘dipende’.

Conclusioni

Fra i metodi citati BEM è sicuramente quello fra i più utilizzati e conosciuto.

utilizzo di BEM

Nonostante i suoi difetti rimane un valido alleato del codice pulito, mantenibile e autoesplicativo: ciò consente un codice facilmente modificabile senza la paura che possa esplodere come una bomba su altri elementi. Un codice prevedibile al 100% potrebbe non essere possibile, per questo

[…] it’s important to understand the trade-offs you make with the conventions you choose. If you follow strict BEM conventions, you will be able to update and add to your CSS in the future with the full confidence that your changes will not have side effects.

Philip Walton