pasjaonline.pl
BEM
BEM
BEM
BEM
Krótki przykład na żywo
autor

Comandeer

date

Dobrze, wiemy już co kryje się za skrótem BEM, lecz jak tą wiedzę wykorzystać w praktyce? Dzisiaj szybki przykład jak przerobić prosty formularz logowania z „normalnego” kodu na BEM-owy!

Każdy z nas choć raz w życiu tworzył formularz logowania. Jest to na tyle rutynowa czynność, że na pewno mamy gdzieś na dysku jakiś prosty snippet od tego. Czy aby jednak na pewno jest idealny? Zobaczmy jak można przedstawić taki formularz w stylu BEM-owym i jakie korzyści to ze sobą niesie.

Co mamy?

Mamy prosty formularz logowania: dwa pola na login i hasło oraz przycisk do logowania.

See the Pen epbaYM by Comandeer (@Comandeer) on CodePen.

Całkowicie niewinny i zwykły kod, prawda? Błąd! Okazuje się, niesie ze sobą wiele problemów, które nie są widoczne na pierwszy (a czasami i drugi) rzut oka, ale w przyszłości, gdy projekt zacznie się rozrastać, staną się o wiele bardziej zauważalne. O jakich kłopotach mowa?

  • Część stylów jest zbyt ogólna. Dopóki na stronie mamy tylko jeden formularz, to style dla fieldset nic nam nie szkodzą. Jasne, można próbować to naprawić przez dodanie .login do selektora, ale to prowadzi do kolejnego problemu.
  • Część stylów ma niepotrzebnie dużą specyficzność. Weźmy taki .login button. Znów – na pierwszy rzut oka całkowicie niegroźny, ale im większa specyficzność, tym większe problemy. Wyobraźmy sobie, że gdzieś na stronie używamy przycisku typu reset i chcemy go także użyć w naszym formularzu logowania. Dostawiamy ładnie przycisk i nadajemy mu klasę .reset, w którym definiujemy mu czerwony kolor tła, po czym… widzimy jak w formularzu logowania jest zielony. Czemu? Bo reguła dla .reset jest słabsza niż reguła dla .login buttont. W tym miejscu musimy zatem zastosować jakąś sztuczkę… Najprościej będzie dostawić .login do selektora, ale to prowadzi do kolejnego problemu: polimorficzności. Zostają zatem różne kombinacje typu .reset.reset (tak, to działa) lub .reset[type=reset], ale nie oszukujmy się – wyglądają tragicznie. Chociaż wciąż są lepsze od !important, którego tutaj prawdopodobnie większość użyłaby dla świętego spokoju.
  • Część elementów zachowuje się polimorficznie. Wystarczy popatrzeć co dzieje się w przypadku elementów z klasą .error: jeśli jest to akapit, to dostaje czerwone tło, jeśli zaś input, to tylko jego obramowanie jest czerwone. A teraz pomyślmy, że tak klasa .error powinna zachowywać się tylko w formularzu logowania albo tylko dla pól formularzy… I znów zaczynamy grzęznąć w dziwnych selektorach, które zaczynają równocześnie cierpieć na poważną chorobę przespecyfikowania.
  • Elementów nie da się użyć w innym miejscu na stronie. .login div nadaje się tylko do użytku wewnątrz formularza logowania. Można użyć zamiast tego form div, ale tym samym wrócimy do pierwszego problemu. W tym kodzie nie ma możliwości „złapania” pojedynczego elementu formularza logowania. To po prostu monolityczna całość, co nie jest zbyt dobrym rozwiązaniem.
  • Trudno ustalić, który element należy do danego komponentu. .error pasuje do wszystkiego, tak samo fieldset. Nie ma żadnego wyznacznika, który pozwala powiedzieć z całą stanowczością, że dany element powinien być w tym konkretnym miejscu.

Przepisywanie na BEM

Jak na tak mały kod, to sprawia całkiem dużo problemów, prawda? Przepiszmy go zatem na BEM i zobaczmy czy uda nam się ich pozbyć!

Konwencja nazewnicza

Na sam początek kilka słów na temat tego, w jaki sposób BEM przejawia się w kodzie strony. Wszystko sprowadza się do nadania poszczególnym tagom odpowiednich klas. Tylko tyle i aż tyle. Przykładowa klasa w BEM może wyglądać tak:

.nazwa-bloku__element_modyfikator

Jak można zauważyć, klasa składa się z co najmniej dwóch elementów(bloku i elementu), a maksymalnie – z trzech elementów (czasami, gdy jest potrzebny, pojawia się modyfikator). Element od bloku oddzielony jest podwójnym znakiem podkreślnika (__), a modyfikator od elementu – pojedynczym podkreślnikiem. Dodatkowo można zauważyć, że jeśli nazwa jakiegoś członu ma więcej niż jeden wyraz, to są one łączone myślnikiem (-).

Tak wygląda oficjalne nazewnictwo zalecane przez metodologię BEM (takie też będę stosował w tym cyklu), jednak powstało wiele jej wariacji. Osobiście stosuję taką:

.nazwaBloku-element--modyfikator

Bardzo podobną konwencję używa SUIT CSS.

Na pierwszy rzut oka klasy BEM-owe wydają się niezwykle rozwlekłe i mogą odrzucać, lecz dzięki takiej swojej konstrukcji pozwolą nam rozwiązać wymienione wcześniej problemy.

Ustalanie podziału elementów

Wiedząc już jak przenieść strukturę BEM-ową na kod HTML i CSS, zastanówmy się jak podzielimy nasz formularz:

  • Tak naprawdę formularz składa się z jednego bloku (cały formularz) i 7 elementów (fieldset, który obejmuje wszystkie pola, legend, który jest nagłówkiem tego formularza, dwóch pól input dla logina i hasła oraz przycisku do logowania oraz komunikatu o poprawnym/niepoprawnym logowaniu).
  • Równocześnie trzeba pomyśleć jak potraktować div, które otaczają pola w tym formularzu. Czy przypadkiem nie są to osobne bloki, które będzie można użyć w każdym możliwym formularzu? To samo dotyczy przycisku: czy on przypadkiem nie jest zarówno przyciskiem, który można wstawić wszędzie, jak i specjalnie oznaczonym przyciskiem służącym do logowania? I czy przypadkiem taka sama sytuacja nie zdarzy się z input? Można pójść jeszcze dalej: czy nie da się wszystkich elementów tego formularza potraktować jako osobnych bloków, które tutaj po prostu współgrają ze sobą? Czy będzie to korzystne dla naszego projektu?

Osobiście proponuję taki formularz w „BEM style”:

See the Pen LpMoKG by Comandeer (@Comandeer) on CodePen.

Jak widać, wewnątrz formularza zamieniłem div na .input-group (czyli samodzielne bloki). Dzięki temu zabiegowi możliwe będzie użycie ich we wszystkich innych formularzach. Podobnie postąpiłem z przyciskiem, który, oprócz bycia elementem bloku .login-form, jest także samodzielnym blokiem .button. Taki podział elementów zwiększa reużywalność poszczególnych części formularza logowania poza nim.

Dodatkowo warto zwrócić uwagę na sposób użycia modyfikatorów. Gdy ich używamy, przy elemencie pojawiają się dwie klasy: podstawowa, bez modyfikatora (np. .input-group__input) oraz ta z modyfikatorem (np .input-group__input_error). Pozwala to m.in. na ograniczenie stylów CSS (modyfikator nadpisuje jedynie te własności, które potrzebują zmiany).

Rozwiązane problemy

Na pierwszy rzut oka kod stał się o wiele bardziej skomplikowany. Czy opłacało się? Zobaczmy.

  • Style dokładnie wskazują czego dotyczą. Stylując .login-form__container zamiast fieldset dokładnie wiemy, który element się zmieni i przy okazji nie zepsujemy nic w innym miejscu strony.
  • Specyficzność selektorów jest ograniczona do minimum. Wszystkie selektory to po prostu klasy. Kaskada została „spłaszczona” i, nie licząc kilku specjalnych przypadków, taka już pozostanie. Nigdy więcej !important!
  • Elementy mają jasno określone zachowanie. Widać wyraźnie, że .login-form__message_error to całkowicie inny element niż .input-group__input_error. Dodatkowo zachowanie zmienić można wyłącznie dzięki modyfikatorom, co znacząco zwiększa kontrolę nad tym, co dzieje się w kodzie… i na stronie.
  • Elementy są możliwe do użycia w innym miejscu strony. Podział na poszczególne bloki pozwala traktować formularz logowania jako budowlę z klocków, które można dowolnie przekładać. Precz z betonowymi monolitami!
  • Widać wyraźnie, które elementy należą do danego komponentu. Dzięki BEM-owej kownencji nazewniczej nawet mając jedynie wyrwany z kontekstu element można go przyporządkować do konkretnej części strony.

Choć BEM zwiększa poziom skomplikowania kodu, jest sensownym sposobem na zwiększenie organizacji kodu, a w dłuższej perspektywie – polepszenia możliwości zarządzania całym projektem.

Co dalej?

Tak, jak obiecałem ostatnio: przyjrzymy się blokom, elementom i modyfikatorom i poszukamy ich odpowiedników poza BEM. A później będzie BEM Tree i jeszcze więcej zabawy 😉

  • http://albert221.cba.pl/ Albert221

    Wkradło się kilka błędów ortograficznych. Osobiście rozjaśniłeś mi wiedzę o BEMie, bo jak wspomniałeś, te nazwy mogą odrzucać, na czym się przyłapałem, ale niosą ze sobą dużo dobrego :)