Ostatnio, w pierwszej części mojej mini-serii, przedstawiłem metodykę CSS o nazwie Object Oriented CSS (OOCSS). Dziś przyszedł czas na kolejną cześć tego cyklu, w której opiszę inną znaną metodykę: BEM!

Jest dość popularne podejście więc całkiem możliwe, że jest już Ci ono znane. Jeśli jednak nie jest, to mam nadzieję, że dzisiejszy wpis wszystko Ci wyjaśni… Zapraszam do lektury!

Co to jest BEM?

Nazwa BEM pochodzi od angielskiego: “Block Element Modifier” i jest to wbrew pozorom bardzo proste podejście do tworzenia modularnego kodu CSS. Opiera się ono przede wszystkim na poniższym podziale elementów na stronie na:

  • bloki - na przykład formularz albo menu
  • elementy - poszczególne elementy bloku takie jak: input czy guzik formularza albo też link w menu
  • modyfikatory - specyficzne warianty elementów: input do wpisywania hasła, guzik “Anuluj” lub aktywny link w menu

Konwencja nazewnicza

W BEM istnieje pewna konwencja nazewnictwa klas CSS. Ogólne zasady tego nazewnictwa przedstawiam poniżej:

  • .block - pierwsze słowo w nazwie oznacza, że klasa dotyczy danego bloku
  • __element - słowo poprzedzone dwoma “podkreślnikami” oznacza, że dana klasa dotyczy danego elementu
  • --modifier - słowo poprzedzone dwoma myślnikami określa kasę będącą modyfikatorem

Korzystając z powyższych zasad możemy tworzyć odpowiednie nazwy klas. I tak, dla całego bloku będzie to po prostu:

.block {
  /* code for the whole block */
}

Każdy blok może mieć wewnątrz różne elementy. Aby utworzyć klasę dla takiego elementu wykorzystujemy nazwę bloku oraz odpowiednią nazwę dla elementu (z dwoma “podkreślnikami”):

.block__element {
  /* code for element of the block */
}

Klasy dla bloków oraz elementów określają zwykle ich ogólne style. Jeśli jakiś blok lub element posiada też jakiś specyficzny wariant, wykorzystujemy do tego modyfikator:

.block__element--modifier {
  /* code for specific variant of the element */
}

.block--modifier {
  /* code for specific variant of the block */
}

Zwróć uwagę na drugą z powyższych klas - modyfikatorów można używać zarówno w kontekście elementów jak i całych bloków!

Oczywiście, oprócz tego co napisałem powyżej istnieje jeszcze kilka ogólnych zasad BEM. Można je sprowadzić do poniższych punktów:

  • w CSS używaj tylko klas - nie należy używać identyfikatorów ani nazw elementów
  • dla klas bloków i elementów nie należy używać selektorów potomka (jest to dozwolone dla modyfikatorów bloku: .block--modifier .block__element {})

Dzięki zastosowaniu opisanej konwencji nazewnictwa klas oraz przedstawionych ogólnych zasad BEM uzyskujemy kod CSS, który jest raczej płaski. Odpowiednie nazewnictwo pozwala też łatwiej zorientować się czego dotyczy dana klasa.

P.S. Więcej na temat nazewnictwa w BEM możesz przeczytać w tym artykule.

Przykład

Myślę, że aby dobrze pokazać o co chodzi, najlepiej będzie (jak zwykle) posłużyć się przykładem. Spójrz na poniższy kod HTML:

<form submit="/api/submit">
  <input type="text" />
  <input type="password"/>
  <button type="button">Cancel</button>
  <button type="submit">Submit</button>
</form>

Jeśli teraz chcielibyśmy, dla powyższego formularza, napisać style CSS zgodne z metodyką BEM, mogłyby by one wyglądać tak jak poniżej:

.login {
  background: #e8e8e8;
}

.login__input {
  padding: 5px 10px;
  border: 1px solid #a5a5a5;
}

.login__input--password {
  border: 1px solid #6babff; /* different border color */
}

.login__button {
  color: #fff;
  background: #00ad11;
  border-radius: 3px;
}

.login__button--cancel {
  background: #aaaaaa; /* different background color */
}

Zanim omówię powyższy kod CSS, spójrz jeszcze jak użyłem powyższych klas w naszym przykładowym HTML’u:

<form class="login" submit="/api/submit">
  <input class="login__input" type="text" />
  <input class="login__input login__input--password" type="password"/>
  <button class="login__button login__button--cancel" type="button">Cancel</button>
  <button class="login__button" type="submit">Submit</button>
</form>

Jak widzisz, w powyższym kodzie klasa .login określa style dla całego bloku kodu formularza. Blok ten zawiera dwa “inputy” oraz dwa przyciski.

Za pomocą klasy .login__input definiuję ogólne style dla wszystkich elementów input bloku login. Natomiast za pomocą klasy .login__input--password określam dodatkowe modyfikacje dla pola tekstowego służącego do wpisywania hasła. Zwróć uwagę, jak korzystam z tej klasy w kodzie HTML:

<input class="login__input login__input--password" type="password"/>

Użyłem tutaj zarówno klasy elementu jak i modyfikatora. Mamy więc tutaj zastosowanie zasady znanej z OOCSS, czyli oddzielenia struktury od stylu. Klasa .login__input odpowiada za strukturę (ogólny styl dla wszystkich elementów input danego bloku). Natomiast klasa .login__input--password dodaje specyficzny styl dla elementu input do wpisywania hasła.

Przy guzikach mamy zrobione to w ten sam sposób. Klasa .login__button to ogólny styl dla wszystkich przycisków bloku login. Natomiast klasa .login__button--cancel w specyficzny sposób traktuje przycisk “Anuluj”.

Zagnieżdżanie elementów

Korzystając z BEM, prędzej czy później natkniesz się na sytuację zagnieżdżenia elementów w obrębie bloku. Spójrzmy na przykład:

<section class="container">
  <header class="container__header">
    <h1 class="???">Title</h1>
  </header>
</section>

W powyższym kodzie, problemem jest jak nazwać klasę dla elementu h1. Generalnie nie powinno się nadawać klas w stylu container__header__title. Zamiast tego zaleca się aby elementy zagnieżdżone również przypisywać po prostu do bloku. W naszym przypadku problematyczną klasę wystarczy po prostu nazwać container__title.

W bardziej rozbudowanych przypadkach, można też rozważyć zagnieżdżenie bloku kodu:

<section class="container">
  <header class="header">
    <h1 class="header__title">Title</h1>
  </header>
  <article class="container__article">
    ...
  </article>
</section>

W powyższym kodzie, element header jest teraz blokiem zagnieżdżonym w bloku container, a klasa dla elementu h1 odnosi się teraz do tego zagnieżdżonego bloku. Natomiast inne elementy bloku container mogą nadal odnosić się bezpośrednio do niego.

Podsumowanie

BEM, podobnie jak OOCSS, jest bardzo prostym zestawem zasad, którymi należy się kierować tworząc style CSS. Mi osobiście podoba się w nim to, że wprowadza też specjalne konwencje nazewnicze. Może i wyglądają one dość dziwnie na pierwszy rzut oka, jednak po chwili przyzwyczajenia okazuje się, że ma to wszystko sens. A dzięki użyciu BEM, napewno łatwiej tworzyć modularny i re-używalny kod CSS!

P. S. Dosłownie parę dni temu pojawił się bardzo fajny artykuł na temat BEM - myślę, że również warto do niego zajrzeć jeśli interesuje Cię ten temat!


Wpis ten jest częścią serii na temat metodyk CSS - poniżej linki do wszystkich części serii: