Uwaga! Trwają prace nad nową wersją serwisu. Mogą występować przejściowe problemy z jego funkcjonowaniem. Przepraszam za niedogodności!

Metodologia BEM w CSS i Sass

Nauka dobrych praktyk programistycznych

Front-end developer metodologię BEM (Block Element Modifier) powinien znać. Nie ma pewności, że będzie ją wykorzystywał w pracy – bowiem wszystko zależy od ustaleń zespołu – lecz ponieważ BEM uczy także dobrych praktyk, warto się z nią zapoznać już na etapie nauki HTML-a i CSS-a.

Spis treści

 Co to jest BEM

W wielu artykułach na temat stylowania znajdziesz informację, że BEM (Block Element Modifier) to jedna z metodyk CSS – obok np. SMACSS czy OOCSS. Tak jednak nie jest. BEM nie powstała tylko po to, by
ułatwić nam tworzenie stylów i zarządzanie nimi. To tak naprawdę cała metodologia rozciągająca się na HTML, CSS, JavaScript, a nawet strukturę plików.

BEM została opracowana przez Yandex, by tworzyć strony, które mają zostać szybko wypuszczone i być potem wspierane przez długi czas. Pomaga tworzyć elastyczne i reużywalne komponenty.

Więcej szczegółów i dokumentację znajdziesz na oficjalnej stronie BEM.

 Blok, element i modyfikator

Wiemy już, że BEM, to nie jedna z metodyk CSS, lecz mimo wszystko znajduje szerokie zastosowanie w tworzeniu klas elementów HTML oraz stylowaniu. Sprawdźmy, na czym to polega: rozłóżmy na czynniki pierwsze nazwę Block Element Modifier. Najpierw przyjrzyj się poniższym grafikom.

metodologia-bem-przyklad-pierwszy metodologia-bem-przyklad-drugi

Jak pewnie widzisz, niektóre komponenty użyte na stronach są takie same lub podobne, a czasem rozmieszczone w innych miejscach. Dzięki BEM nie musieliśmy tworzyć nowych bloków czy elementów, lecz skorzystaliśmy z tych, które już były, i ewentualnie trochę je zmodyfikowaliśmy.

 Block (Blok)

Jest to komponent samodzielny pod względem logicznym i funkcjonalnym. Może to być więc np. sekcja, artykuł, formularz czy FAQ. Gdziekolwiek ich nie wstawimy, będą działać i wyglądać tak samo, ponieważ zawierają już w sobie style, funkcjonalność (JavaScript) i inne niezbędne do ich działania technologie.

Na grafice jest to:

  • główny kontener (blok A, niebieskie tło), który zawiera resztę komponentów – może to być np. nasz element HTML main
  • sekcja (blok B, białe tło) stanowiąca pojemnik np. na tekst czy obrazy
  • komponent (blok C, zielone tło), który zawiera w sobie potrzebne elementy, np. rozwijane elementy listy w przypadku FAQ czy inputy w przypadku formularza.

 Element

Stanowi część bloku. Nie może być używany poza nim, ponieważ nie jest samodzielny (pozostaje zależny od stylu i funkcjonalności rodzica). Zaraz dowiesz się, że tę regułę można obejść za pomocą tzw. mixes, ale najpierw zobaczmy, czym element jest.

Na wcześniej przedstawionej grafice mamy dwa elementy:

  • dziecko sekcji, bloku B (element 1, czerwone tło) – jak już wcześniej wspomniałem, może to być np. tekst czy obrazy umieszczone chociażby w elementach div
  • dziecko komponentu, bloku C (element 2, żółte tło) – czyli np. inputy czy elementy li.

Takich elementów nie umieszczamy poza rodzicem. Ponieważ są od niego zależne, w innym miejscu nie będą działać (np. rozwijane elementy FAQ przestaną się rozwijać) i/lub będą źle się prezentować (np. obrazy umieszczone poza przeznaczoną do tego sekcją stracą proporcje).

 Modifier (Modyfikator)

To część definiująca (modyfikująca, zmieniająca) wygląd i/lub działanie bloku lub elementu. Jest tylko dodatkiem do nazwy (np. klasy czy pliku), czyli nie znajduje odzwierciedlenia w postaci osobnego komponentu.

Użycie modyfikatora nie jest obowiązkowe. Korzystamy z niego wtedy, gdy jest taka potrzeba. Jeżeli więc np. od razu wiemy, że jakiś komponent nigdy nie będzie miał innych wariantów, nie dodawajmy do niego modyfikatora.

Na grafice mamy dwa zmodyfikowane bloki:

  • blok A na pierwszym obrazie nie ma paddingu z lewej i prawej strony, a na drugim już tak
  • blok B na pierwszym obrazie umożliwia swoim dzieciom ustawienie się w jednej kolumnie, a po modyfikacji – w dwóch; na drugim obrazie blok ma dodatkowo ramkę.

 

Na stronie BEM znajdziesz obrazowy przykład wykorzystania bloków, elementów i modyfikatorów w rozbudowanym headerze.

 Klasy elementów HTML w metodologii BEM

Tak naprawdę stylowanie zgodne z BEM opiera się na klasach, które nadajemy elementom HTML. Zobaczmy, jak tworzyć poprawne nazwy klas.

 Klasa bloku

Nazwa klasy bloku powinna określać jego funkcję.

  • Jeżeli blok to galeria obrazów, nadajmy mu np. klasę .gallery.
  • W przypadku paska na dole strony z prawami autorskimi i kontaktem do autora możemy stworzyć klasę .footer.
  • Jeśli to box z reklamą, niech ma klasę .advert czy .banner.

 Klasa elementu

Klasę elementu składamy z klasy bloku (przodka) i dodatkowego członu charakteryzującego ten element. Obie nazwy łączymy za pomocą podwójnego znaku podkreślenia. Przykładowo:

  • Element HTML figure w galerii może mieć klasę .gallery__item.
  • Kontener ikon kontaktu w footerze: .footer__icons.
  • Tytuł reklamy w boksie: .advert__title.

 Klasa bloku/elementu z modyfikatorem

Modyfikator w przypadku klas to człon dołączany do klasy bloku lub elementu. Dodajemy go za pomocą podwójnego znaku minusa. Na przykład:

  • Element HTML figure w galerii może mieć klasę .gallery__item--frame – co będzie oznaczać, że przy stylowaniu otrzyma obramowanie.
  • Kontener ikon kontaktu w footerze możemy zmodyfikować tak: .footer__icons--large i otrzymywać w ten sposób większe ikony (oczywiście jeśli tak je ostylujemy).
  • Blok .advert może dostać modyfikatory .advert--row oraz .advert--column dla boksów w postaci wiersza i kolumny.

 

Powyższe wskazówki usystematyzujesz za pomocą przykładu komponentu o klasie .card. Natomiast na stronie 9elements.com znajdziesz sciągę BEM z graficzną reprezentacją kodu HTML, która pomoże Ci lepiej zrozumieć relacje między rodzicami (blokami) a dziećmi (elementami).

A tutaj zamieszczam przykładowy fragment kodu HTML dla footera z klasami BEM. Listę ikon, która ma dwie klasy, zaraz sobie omówimy.

<footer class="footer">
  <ul class="footer__sm-icons sm-icons">
    <li class="sm-icons__item"><a href="" class="sm-icons__link">FB</a></li>
    <li class="sm-icons__item"><a href="" class="sm-icons__link">LI</a></li>
    <li class="sm-icons__item"><a href="" class="sm-icons__link">I</a></li>
  </ul>
  <ul class="footer__links">
    <li class="footer__link-item"><a href="" class="footer__link footer__link--important">Regulamin</a></li>
    <li class="footer__link-item"><a href="" class="footer__link">Polityka prywatności</a></li>
  </ul>
  <small class="footer__copyrights">Copyright &#169 2022 by DevMentor.pl</small>
</footer>

 Mixes

Przy okazji elementów wspomniałem, że można obejść regułę, która „zakazuje” używania elementów poza blokami będących rodzicami. Do tego służą właśnie mixes. Można powiedzieć, że mixes to usamodzielnienie elementów w taki sposób, by można było je dopasować do innych rodziców.

Jak to zrobić? Taki komponent dostaje samodzielną klasę – jak blok. Może mieć także elementy potomne. Poniżej widzisz właśnie taki przykład. Lista ul ma dwie klasy .footer__sm-icons (jak dla elementu) oraz .sm-icons (jak dla bloku). Jej dzieci otrzymują już klasy wg drugiej nazwy, np. .sm-icons__item.

<footer class="footer">
  <ul class="footer__sm-icons sm-icons">
    <li class="sm-icons__item"><a href="" class="sm-icons__link">FB</a></li>
    <li class="sm-icons__item"><a href="" class="sm-icons__link">LI</a></li>
    <li class="sm-icons__item"><a href="" class="sm-icons__link">I</a></li>
  </ul>
</footer>

Dzięki temu lista ikon może zostać wykorzystana w innych miejscach, ponieważ podstawowe style i funkcjonalności zapewni jej klasa .sm-icons. Jeśli natomiast będzie należało coś zmodyfikować na potrzeby nowego rodzica, zrobi się to w klasie dla elementu, czyli .footer__sm-icons.

Załóżmy, że nasza lista ikon w klasie .sm-icons otrzymuje style odpowiadające za jej wygląd: poziome ułożenie elementów i stałe odstępy między nimi – czyli coś, co rodzica za bardzo „nie obchodzi”.

W klasie .footer__sm-icons otrzyma dodatkowo informację o tym, by układała się na środku rodzica i miała 200 px szerokości (to już rodzica „obchodzi”). Jeśli umieścimy nasze ikony w headerze, gdzie znajdą się obok innych elementów (logo, nawigacji, przycisków), to klasa .header__sm-icons będzie określać odległość ikon np. od menu (lewy margines) oraz inną szerokość: 100 px.

.sm-icons {
  display: flex;
  align-items: center;
  justify-content: space-around;
}

.footer__sm-icons {
  margin: 0 auto;
  width: 200px;
}

.header__sm-icons {
  margin-left: 30px;
  width: 100px;
}

W ten sposób unikamy duplikowania kodu, a nasz BEM pozwala na jeszcze większą elastyczność.

Na stronie BEM znajdziesz krótką dokumentację dla mixes na przykładzie przycisku.

 Stylowanie z BEM w plikach CSS i SCSS (Sass)

Stylowanie w CSS-ie po klasach pisanych według BEM może się wydawać nieporęczne – w końcu klasy takie nieraz tworzą długie ciągi znaków, a my mamy wrażenie, że piszemy ciągle to samo, np.:

.advert {
    //styles
}
.advert__title {
    //styles
}
.advert__container {
    //styles
}
.advert__image {
    //styles
}
.advert__description {
    //styles
}
.advert__description--large {
    //styles
}
.advert__description--small {
    //styles
}

Lecz jeśli używamy preprocesora CSS, który pozwala zagnieżdżać style należące do jednego elementu, zapis się upraszcza i staje bardziej intuicyjny. Tak działa np. preprocesor Sass, gdzie znak & zastępuje część klasy przodka (w przykładzie znak & zastąpił fragment .advert i .advert__description).

.advert {
    //styles

    &__title {
        //styles
    }
    &__container {
        //styles
    }
    &__image {
        //styles
    }
    &__description {
        //styles

        &--large {
            //styles
        }
        &--small {
            //styles
        }
    }
}

Taki sposób tworzenia stylów ma jeszcze jedną dużą zaletę: znacznie zmniejsza ryzyko przypadkowego nadpisania stylów innego elementu – czy to ze względu na wyższą specyficzność, czy omyłkowe utworzenie klasy o tej samej nazwie.

 Popularne wątpliwości i błędy przy korzystaniu z BEM

 Czy mogę umieścić blok w bloku?

Jak najbardziej. Przykładowo blok .page-container może zawierać w sobie bloki .article, .gallery, .advert itp. Zapewne w takim momencie zauważysz potrzebę skorzystania z mixes (o których była mowa wyżej), by móc dopasować blok do warunków danego rodzica.

 Czy zagnieżdżony element mogę nazwać go po jego rodzicu, który też jest elementem, np. .block__list__list-item?

Nie. Nazwa może pochodzić jedynie od nazwy bloku, zatem prawidłowa klasa dla elementu listy to .block__list-item. Jeżeli widzimy, że dany komponent zyskuje samodzielność, możemy zdecydować się na utworzenie nowego bloku lub skorzystania z mixes. Wówczas klasa dla elementu listy wyglądałaby tak: .list__list-item. Wszystkie trzy opcje znajdziesz poniżej:

<!-- klasa dla <li> od nazwy bloku -->
<section class="block">
  <ul class="block__list">
    <li class="block__list-item">element listy</li>
  </ul>
</section>

<!-- klasa dla <li> od nazwy zagnieżdżonego bloku -->
<section class="block">
  <ul class="list">
    <li class="list__list-item">element listy</li>
  </ul>
</section>

<!-- klasa dla <li> od nazwy zagnieżdżonego bloku-elementu (wykorzystanie mixes) -->
<section class="block">
  <ul class="block__list list">
    <li class="list__list-item">element listy</li>
  </ul>
</section>

 Czy klasa elementu powstaje na podstawie klasy rodzica?

Może, ale nie musi. Element może być bardziej zagnieżdżony – wystarczy, że blok jest bezpośrednim przodkiem, a nie rodzicem. Spójrz na nasz przykład z footerem:

<footer class="footer">
  <ul class="footer__links">
    <li class="footer__link-item"><a href="" class="footer__link footer__link--important">Regulamin</a></li>
    <li class="footer__link-item"><a href="" class="footer__link">Polityka prywatności</a></li>
  </ul>
</footer>

Zauważ, że klasa linków (elementów a) stworzona jest na podstawie bloku (przodka) o klasie .footer, który co prawda nie jest rodzicem, lecz jest bezpośrednim przodkiem – czyli „po drodze” w głąb zagnieżdżenia nie mamy innego bloku.

 Czy blok/element może mieć przypisaną jedynie klasę z modyfikatorem?

Nie, nie powinien. Modyfikator zmienia tylko fragment wyglądu czy działania komponentu, a pozostałe cechy są takie same, jak we „wzorze”. Poniżej widzisz przykład elementów listy z linkami. Klasa .footer__link będzie więc decydować np. o rodzaju, kolorze i rozmiarze fontu, a modyfikator .footer__link--important doda jedynie wyboldowanie czy podkreślenie. W ten sposób unikamy powielania kodu.

<footer class="footer">
  <ul class="footer__links">
    <li class="footer__link-item"><a href="" class="footer__link footer__link--important">Regulamin</a></li>
    <li class="footer__link-item"><a href="" class="footer__link">Polityka prywatności</a></li>
  </ul>
</footer>

 Czy mogę zastosować BEM w nazwach tylko niektórych klas?

Jeśli chcesz zachować porządek w kodzie, to powinieneś trzymać się jednej konwencji i we wszystkich klasach zastosować BEM.

 Czy niektóre elementy HTML mogą nie posiadać żadnej klasy?

Lepiej ich tak nie zostawiać. Warto od razu dodać klasy do wszystkich elementów – nawet jeśli zakładasz, że nie będą Ci one potrzebne (bo np. nie zamierzasz stylować paragrafów jakiejś sekcji).

Jeśli projektem mamy z łatwością zarządzać i móc go rozwijać, to takie klasy znacznie ułatwiają sprawę – prędzej czy później mogą się przydać, a wtedy lepiej mieć je od razu, niż potem dodawać (zwłaszcza, gdy kodu jest dużo, a my po kilku miesiącach przerwy już tak sprawnie się po nim nie poruszamy – nie mówiąc już o kimś, kto tego kodu nie tworzył!).

 

Dzięki BEM możemy tworzyć kod reużywalny, elastyczny i zgodny z zasadą DRY. Warto już teraz przestawić się na myślenie o elementach HTML w kontekście bloków (komponentów) – podejście takie jest bowiem wykorzystywane np. w Reakcie.

Udostępnij ten artykuł:

Mentoring to efektywna nauka pod okiem doświadczonej osoby, która:

  • przekazuje Ci swoją wiedzę i nadzoruje Twoje postępy w zdobywaniu umiejętności,
  • uczy Cię dobrych praktyk i wyłapuje złe nawyki,
  • wspiera Twój rozwój i zwiększa zaangażowanie w naukę.