Polub bloga na fejsie!

Metodyki CSS #2 – BEM

22

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:

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”):

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:

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:

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:

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

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:

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:

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:

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:

REACT, REDUX, REACT-ROUTER - KURSY ON-LINE

Chcesz od podstaw poznać tajniki React, Redux oraz react-router? Zapraszam do moich szkoleń on-line:

Przejdź do szkoleń

Uwaga! Obecnie trwa przedsprzedaż kursów - premiera 1 sierpnia 2017!

  • „Generalnie nie powinno się nadawać klas w stylu container__header__title.” – czyli zasada „max 2 poziomy” ?

    • chyba można tak powiedzieć – tutaj chodzi tylko o nazewnictwo: bierzemy element i sprawdzamy do jakiego bloku należy; nie jest tutaj istotne czy w HTML są one jakoś zagnieżdżone, zarówno header jak i title należa do bloku contaier więc ich nazwy to container__header oraz container__title

      • Bazując na ostatnim przykładzie CSS powinien odnosić się do .container .header czy samo .header? Dobrze myślę, że opcja pierwsza?

        • nie – druga opcja będzie prawidłowa, ogólnie te metodyki dążą do tego aby eliminować „zagnieżdżenia” w selektorach CSS

          • W takim razie powinno być raczej .container-header, bo .header możemy mieć w kilku miejscach i ich wygląd może być od tego uzależniony

          • A może .container__header.header? Wówczas zaznaczamy kontekst, a równocześnie delegujemy wszystko do osobnego bloku. IMO najładniejsze rozwiązanie.

    • Max 2 poziomy to nie jest oficjalnie narzucona zasada, ale jedna z dobrych praktyk, które są opisane w standardzie BEM: https://en.bem.info/methodology/faq/#why-does-bem-not-recommend-using-elements-within-elements-block__elem1__elem2

  • veranoo

    Właśnie w BEM-ie mam największą zagwozdkę z tymi zagnieżdżeniami.

  • Bardzo mnie ciekawi, czemu BEM rozpatruje się niemal wyłącznie jako metodologię pisania CSS-a, w czym BEM tak po prawdzie nie wnosi nic ciekawego ponad choćby opisany wcześniej OOCSS.

    Gdyby wgryźć się głębiej w oficjalną dokumentację BEM, to bardzo szybko odkrylibyśmy istnienie takiego konceptu, jak BEM tree. To oczywiście od razu nasuwa skojarzenie z drzewkiem DOM – i bardzo dobrze. BEM nie jest niczym innym jak dodatkową warstwą abstrakcji na DOM, która pozwala nam oderwać się od myślenia o konkretnych elementach („fizycznych” składnikach strony) a przejść na poziom bloków („logicznych” składników strony). Tym samym pokuszę się o stwierdzenie, że BEM nie jest metodologią pisania CSS-a, a – architekturą służącą do budowy stron internetowych. Wprowadzenie jej na wszystkich poziomach developmentu (HTML, CSS, JS, ale także i projekt graficzny – PSD podzielony jako bloki – struktura katalogów itd.) tworzy bardzo spójną platformę do tworzenia aplikacji internetowych opartych na idei komponentyzacji (wszak blok to nic innego jak komponent). No i aż grzech z tego nie skorzystać, bo przecież oficjalne narzędzia istnieją od zawsze.

    • Poniekąd zgadzam się z tym co napisałeś na temat BEM jako architektury… BEM zdaje się powstał dużo wcześniej niż architektury oparte o komponenty, które znamy dzisiaj – była to więc próba wprowadzenia tego podejścia do CSS (i HTML bo trzeba to rozpatrywać jako naczynia połączone). Dzięki temu, że obecnie już dość powszechnie wykorzystuje się komponenty do budowy aplikacji, to BEM idealnie wpasowuje się w ten schemat…

      • Powiedziałbym inaczej: BEM było pierwszą tego typu architekturą, a dopiero później to nazwano. Tak bym to ujął 😉

  • Budzik94

    Myślę ze przy okazji BEMa warto wspomnieć o less czy sass. Wtedy kod CSSa zyskuje jeszcze więcej ! Sama struktura napisanego lessa/ sassa tworzy nam separacje elementów do siebie super się pracuje z takim kodem <3

    https://uploads.disquscdn.com/images/e350d94bf6aac0db3bd09c68b3de2924d5812c84d2f3eb5353a044f8d9aaf605.png

    Fajny artykuł 😉

    • Przemysław Szelenberger

      Oj, nie polecam takiego wydzielania w preprocesorach. Powoduje to trudność w przeszukiwaniu kodu. Łatwiej jest znaleźć klasę article__header, niż kombinować z &__header, albo &–cancel.

      • mercs600

        Ja mam inne odczucia, jeżeli w logiczny sposób wydzielasz swój kod na proste komponenty, czy mniejsze części, to nie ma tego aż tak dużo, żeby był nieczytelny, czy trudny w wyszukiwaniu. W przypadku preprocesorów bez sensu wg mnie jest pisać inaczej.

        • kriz

          Ja używam BEM razem z Saas i potwierdzam że struktura fajnie nam tworzy separację bloków kodu, ale w Twoim przykładzie to jest zupełnie niezgodne z założeniami BEM. Ja stosuję to w taki sposób i polecam jednak takie stosowanie 😉 https://uploads.disquscdn.com/images/7bff8c5d9c48146c610439bcabf7ff6b4d9c5501f3e7412743beae194f7368cc.jpg :

          • mercs600

            @kriz Dla mnie powyższe jest bez sensu. Zagnieżdżanie w taki sposób powoduje, że otrzymujesz wynikowo:

            a przecież właśnie tego zagnieżdżania pisząc w BEM chcemy się pozbyć. Komponenty pisane w metodologi BEM powinny być odseparowane, a w Twoim przypadku otrzymujemy dalej:

            Każda z tych klas wynikowo powinna być na pierwszym poziomie. Dlatego uważam, że rozsądniejsze pisanie w BEM z preprocesorami jest to co pokazuje @Budzik94

          • kriz

            Własnie nie są na jednym poziomie.. picker-form__select jest elementem bloku picker-form. A picker-form jest zagnieżdzony w kodzie w picker-container, ale jest jednocześnie osobnym blokiem. To co otrzymujemy wynikowo w pliku .css ma mniejsze znaczenie, bo wyszukiwanie i edycje robimy przeciez w scss, a taka struktura sprawia, że jest łatwiejsze do znalezienia. Ale tak naprawdę nie ma się co spierać, bo pewnie wszystko to kwestia przyzwyczajenia i w Twoim kodzie też akurat Ty łatwiej się odnajdziesz, bo stosujesz pewnie takie podejście od dłuższego czasu. Więc skoro spełnia swoje zadanie to ok 🙂

          • > To co otrzymujemy wynikowo w pliku .css ma mniejsze znaczenie, bo wyszukiwanie i edycje robimy przeciez w scss, a taka struktura sprawia, że jest łatwiejsze do znalezienia.

            Przedkładasz w tym momencie całkowicie Developer Experience (DX) przy tworzeniu nad DX przy utrzymywaniu produktu. Po to BEM **wymaga** płaskiej struktury, żeby specyficzność wynikowego CSS-a nie sprawiała problemów.

  • Tomek Różalski

    Poniekąd zgadzam się z @Budzik94:disqus że BEM bez preprocesora CSS niewiele w sumie pomaga. Z tym tylko, że po pierwsze LESS odszedł już jakiś czas temu do lamusa, a Sass czeka w pewnym sensie ten sam los. Wydaje mi się, że przyszłością jest PostCSS i SugarSS. Oczywiście w połączeniu z BEM.

    Piszę o tym dlatego, że @Comandeer:disqus wspomniał o tym, że BEM jest po prostu warstwą abstrakcji (którą można zastosować choćby w BEMQuery ;)). Jeżeli więc rozszerzamy określenie metodyki, to spokojnie złapać pod to możemy PostCSS, w którym de facto możemy mieszać CSS z JS.

  • Klonos

    Wg obecnej dokumentacji BEM modyfikator jest poprzedzony jedną podłogą „_” a nie dwoma myślnikami jak w artykule „–„. (url: https://en.bem.info/methodology/quick-start/#types-of-modifiers)

    btw. fajny cykl artykułów 😉

Google Analytics Alternative