CSS w JS – mity o mitach
Wszyscy, którzy mnie znają, wiedzą doskonale, że w przypadku Sieci należę raczej do konserwatystów, będących wyznawcami starego porządku. To uwielbienie dla tradycji rozciąga się także na używane technologie. Jeśli coś jest nowe, lecz niewystarczająco dobre, po prostu tego nie używam. Prawda jest taka, że przeżyłem już niejedną super nowoczesną, gorącą technologię (prawda, Angular?) i widziałem śmierć niejednego standardu (a nawet przeglądarki!). I mam wrażenie, że kolejny z trendów również przeżyję: CSS w JS-ie.
Trend ten narodził się, rzecz jasna, wraz z powstaniem Reacta i miał rozwiązywać problemy, których dotąd w CSS-ie nie rozwiązano. Problem polega na tym, że, owszem, czysty CSS ich nie rozwiązywał, ale dobre praktyki i całe ekosystemy skonstruowane wokół różnych metodologii – jak najbardziej. Niemniej powstały mity o tym, jaki CSS jest zły, które pozwoliły ugruntować się nowemu trendowi. Nie przeczę, rozwiązania pokroju CSS Modules czy styled-components
mają swoją niszę – zwłaszcza w ekosystemie Reacta. Problem polega na tym, że ludzie ich używają z całkowicie bezsensownych powodów lub – o zgrozo! – “bo tak”. Doskonale to pokazują komentarze do artykułu o mitach odnośnie CSS-a w JS-ie, które zafrapowały mnie na tyle, że części z nich postanowiłem się przyjrzeć bliżej.
Najbardziej w oczy rzuca się fakt, że broniący CSS-a w JS-ie nienawidzą BEM. Problem polega na tym, że, gdy przeglądam komentarze, odnoszę wrażenie, że nienawidzą BEM, bo go… nie znają. Jakub Gawlas stwierdza wprost, że nie używa BEM, bo
[BEM] makes a mess in a bigger projects, over styled-components.
Saying BEM is the solution is like saying we don’t need javascript modular system because we can just namespace all the craps in the window object, not to mention we need to give it extra brain power to come up with a hierarchy for styles because they are separated from the javascript code.
Najdobitniej jednak ujmuje to Resist Design:
ewwwww, BEM is shit man, sorry.
No tak, z argumentem takiego kalibru nie ma nawet jak dyskutować…
Wszyscy wyżej wymienieni komentujący nie wiedzą, czym jest BEM – mimo że zdaje się im inaczej. Sprowadzają go bowiem wyłącznie do konwencji nazewniczej. I tutaj muszę się z nimi zgodzić: jeśli rozpatrujemy BEM wyłącznie jako konwencję nazewniczą, nie ma on sensu… Niemniej sama oficjalna dokumentacja nazywa BEM metodologią, a ja powiedziałbym nawet więcej: BEM jest architekturą, wokół której zbudować można cały front aplikacji (i dywagowałbym, czy wepchanie BEM po stronie serwera również nie przyniosłoby korzyści). BEM jest przecież niczym innym, jak warstwą abstrakcji na HTML + CSS + JS – dokładnie tym samym, czym jest React.js + styled-components
! Różnica przebiega na samym poziomie owej abstrakcyjności i rzekłbym, że tutaj nowe rozwiązanie przegrywa z kretesem. Abstracja w przypadku Reacta i styled-components
sprowadza się do przetłumaczenia HTML-a i CSS-a do formy JS-owej. W przypadku BEM tworzone jest coś na wzór DSL-a dla konkretnego projektu, który można użyć na każdym etapie tworzenia: od głupiego projektu na kartce (na szkicu już przecież widać bloki, elementy i ich warianty), poprzez projekt graficzny (organizacja warstw/elementów w Photoshopie/Sketchu według konwencji nazewniczej BEM nie brzmi źle), na faktycznej implementacji kończąc. W przypadku abstrakcji wprowadzanej przez React.js + styled-components
mamy do czynienia tylko z “ułatwianiem” ostatniego etapu.
Jeśli zatem patrzy się całościowo na BEM, wówczas argumenty o tym, że jest “brudny” i zaśmieca kod nie dają się za bardzo utrzymać. Dłuższa nazwa klasy w odniesieniu do całego “języka projektu” (jakby można to nazwać) to wszak i tak o wiele mniejsze ustępstwo niż zależność od narzędzi i bibliotek w przypadku CSS Modules czy styled-components
.
Inne argumenty odnoszą się bezpośrednio do CSS-a i HTML-a i… w gruncie rzeczy są przerażające. Najpierw przyjrzyjmy się komentarzowi Vanyi Yania:
Doesn’t even sound like a myth to me. Styled-components does solve the global namespace problem. Doesn’t it?
No ok, to nie odnosi się bezpośrednio do CSS-a, ale w sumie jego dotyczy. Wszystko rozbija się o to, czy rozwiązania JS-owe rozwiązują problem globalności stylów. Odpowiedź brzmi prosto: nie. Jedyne, co te narzędzia robią, to generują całkowicie losowe nazwy klas, po czym aplikują do konkretnych elementów. To nie jest rozwiązanie problemu, a jego obejście przy pomocy brudnego hacku. W gruncie rzeczy konwencja nazewnicza BEM robi dokładnie to samo – z tym, że w niej to my ustalamy nazewnictwo, co często jest wręcz zaletą (brak udziwnionego debuggingu).
Why do you need to know which tag is used? To me all tags are all the same with only difference the default styling. If you don’t need a special styling, why create a dedicated component?
A to jest po prostu przerażające. Rozumiem, że tagi w JSX-ie nie przekładają się 1:1 do kodu HTML, ale twierdzenie, że nowy tag jest związany wyłącznie z inną prezentacją, aż boli. Tak ścisłe wiązanie semantyki z prezentacją prowadzi ostatecznie do tego, że wynikowy kod HTML z semantyką się nawet nie widział (pozdrowienia dla Facebooka i jego divowego limbo).
If you want specific list styling you’d have to name it. And naming it using tag name to me is a more nice way. Otherwise why all these fancy HTML5 semantic markup tags were invented?
Chociaż w gruncie rzeczy wychodzi na to, że faktycznie myśli się o tych tagach jako przekładalnych do HTML-a… co jest naprawdę bardzo, bardzo przerażające.
It is impossibe to extend classes in CSS without some kind of preprocessor
Uhm… Czym niby się różni
.klasa {
color: red;
display: block;
border: 2px #000 solid;
}
.klasa_modyfikowana {
color: blue;
}
od
.klasa {
color: red;
display: block;
border: 2px #000 solid;
}
.klasa_modyfikowana {
@extend .klasa;
color: blue;
}
Drugi kod generuje o wiele dłuższy kod CSS i powoduje, że w naszym kodzie HTML traci się informacje o zależnościach pomiędzy poszczególnymi elementami i ich wariantami (bo przecież mówimy tutaj o jakiejś sensownej wartwie abstrakcji, a nie CSS-ie na poziomie .button-green
!).
Zresztą nieprawdą jest, że nie da się rozszerzać klas w CSS-ie, bo ten dochrapał się ostatnio mixinów.
implies that css overriding rules are easy to grasp
Wydaje mi się, że CSS z założenia jest łatwiejszy do czytania niż składnia JS-a.
#6 tells you that forcing people to create a separate file even for a single rule is a good thing
Patrzę na mit #6 i nie widzę tam takiego stwierdzenia – typowe reductio ad absurdum.
I don’t see how having a separate file improves performance. Parsing is single-threaded. For what I know, having a separate file worsens network loading performance as there’s a connection latency.
Ktoś tu chyba nie słyszał o HTTP/2. Obecnie liczba żądanych plików nie stanowi aż tak dużego problemu, a w połączeniu z dobrze skonfigurowanym cache potrafi zdziałać prawdziwe cuda. Polymer z tym eksperymentuje.
Hajime Yamasaki Vukelic posuwa się w swych dywagacjach jeszcze dalej:
The misconception that styling and semantics are orthogonal comes from the artificial separation of concerns that places HTML and CSS into two different buckets without much thought. Having HTML and CSS in separate buckets only splits the problem along syntactical boundaries, without any consideration of the intimate relationship between content and its appearance.
Oto kolejny – szkodliwy – mit powtarzany w środowisku Reacta: mityczny podział technologii. Według tego mitu podział strony na HTML + CSS + JS jest bezsensowny, bo dzieli według technologii, a nie obowiązków (separation of concerns). Jak bardzo miałki jest to argument, najlepiej obrazuje artykuł o tworzeniu modularnych interfejsów, w którym autor sztuczny podział na HTML + CSS + JS proponuje zastąpić naturalnym podziałem na JSX + JS + CSS. Tutaj nawet nie trzeba komentarza.
Jak niebezpieczne jest natomiast łączenie semantyki z prezentacją, to najlepiej pokazują recenzje na WebKrytyku, w których najlepiej widać, że zawsze kończy się to całkowitą niedostępnością. Wydawało mi się, że pewne podstawowe koncepty są na tyle… podstawowe, że nie trzeba ich tłumaczyć. Najwidoczniej w chwili, gdy przestało się je tłumaczyć, przestały być podstawowe.
Mattia Astorino z kolei wskazuje na niesprawiedliwość przykładów z CSS-em:
I suggest you to show an example based on attributes instead of classes. Css status shoud be use the aria-* attributes o normal attributes. So… .button[aria-primary] is a better example
Przerabialiśmy to już. Łączenie prezentacji bezpośrednio z tożsamością czy rolą elementów nie działa na dłuższą metę, bo brakuje nam pewnej warstwy abstrakcji – właśnie dlatego powstało BEM czy ów nieszczęsny CSS w JS-ie.
Wróćmy jeszcze na chwilę do Hà Nguyễna i jego komentarza:
About web component and shadow dom, please stop politically correct your fellow devs. I think we have enough of it in real life. If the so called “standard” is not as usable as we want and we as a community can sustain our solution that makes sense, then use what makes sense. What’s the point of conforming to external standard when you are making application whose codebase is only accessible by your already-on-the-same-page team members or even just yourself anyway? I’m not a fan of css as json objects but I’ll definitely pick things like css-modules and csjs over crappy standards for UI components that I don’t intend to publish in my own application.
I właśnie tak zabija się standardy sieciowie. Zamiast włączyć się w proces ich powstawania, webdevowie postanawiają pójść na łatwiznę. Tym sposobem zamiast standardów mamy “standardy” i zamknięte ekosystemy oparte na rozwiązaniach poszczególnych korporacji (ekosystem Reacta to przecież piaskownica Facebooka, nie można o tym zapominać!).
Ciekawą kwestię porusza z kolei Paul Henschel:
Pretending that css works just fine with functional components is as odd as saying jss has it already figured out. […] A declarative, functional component is not a HTML directive and will never be.
Skoro komponenty Reacta są funkcyjne, to czemu są deklarowane przy pomocy XML-owej składni? Co więcej – są przecież tłumaczone następnie na kod HTML… Odnoszę wrażenie, że funkcyjne komponenty to kolejny mit, jaki towarzyszy Reactowi, który po raz kolejny odwraca naszą uwagę od faktu, że React jest niczym więcej, jak warstwą abstrakcji dla widoku – i tym być powinien.
Chyba jedyny komentarz, z którym jestem w stanie się zgodzić, to komentarz Raymonda Julina, w którym stwerdza on, że, owszem, BEM rozwiązuje wspomniane problemy, ale na poziomie konwencji nazewniczej może dojść do konfliktów nazw i w rozwiązywaniu ich lepsza jest maszyna. I to jest prawda. Pytanie tylko, jak często takie konflikty faktycznie zachodzą i czy nie jest to problem bardziej potencjalny niż rzeczywisty?
Doskonale zdaję sobie sprawę z tego, że CSS w JS-ie ma swoje prawdziwe use-case’y, w których faktycznie sprawuje się lepiej niż tradycyjny CSS… Niemniej po przeczytaniu tych komentarzy odnoszę nieodparte wrażenie, że główna teza zawarta w artykule (użytkownicy używają CSS w JS-ie bez wyraźnego powodu lub z powodu trendu) sama się potwierdziła. Przeraża zwłaszcza lekkość, z jaką znowu zaczyna podchodzić się do semantyki HTML-a.
Obym był tylko zrzędliwym konserwatystą a nie prorokiem…
Komentarze
Przejdź do komentarzy bezpośrednio na Githubie.
Dawne komentarze
Ten blog wcześniej korzystał z systemu komentarzy Disqus. Jednakże pożegnaliśmy się i postanowiłem, że zaimportuję do nowej wersji stare komentarze z niego. Cóż, jego system eksportu na wiele nie pozwala…
Nie mógłbym się z Tobą zgodzić mocniej.
Bez kitu... Sporo czytam, ale nie często tak dobre arty jak ten :-) Jakis czas używałem React / React Native... To chyba nie były dobre use case'y bo do dzis mam niesmak. BEM - spoko, CSS modules - no dobra. Styled components - wtf?! Skąd ten zachwyt. Taka bezmyślność na rzucanie się na byle "trendi" coś tam, i od razu uznawanie tego za nową religię.
Argumentując jak hejterzy BEMa - mix tego xmlo-jso-cssa etc, wyglada jak g.... 8-)
I pyk gó...burza :-)
Ja tam jednak wolę jak mój kod wyglada jak kod, nie jak mix g... ;-)
Sorry, ale jeśli tak niby wygląda ewolucja, to ja nie chce znać kolejnego jej etapu :-)
https://cdn-images-1.medium...
Programuję już kilkanaście lat, poprzez różne języki, technologie i podejścia. Teraz robię głównie full-stack; może jestem już dinozaurem... i sam nie widzę nic złego z byciu nieco konserwatywnym :-) Paranoja przyśpiesza -- gonitwa za czymś wspaniałym, "lepszym" ... czyli nasze obecne podejście i rozwiązanie zawsze jest do kitu?
Sam gonię nowoczesność i trendy, ALE jednak potrafię wybrać podejścia które są OK, faktycznie działają i rozwiązują problemy, zamiast ich głównie dostarczać :-) I które sam lubię. Jeśli dobrze się z czymś pracuje, nawet jak projekt i team rosną, to w sumie pozostaje korzystać z tego.
Zastanawia mnie inna rzecz - skąd w świecie front-end dev tyle over-engineering'u? To jakieś kompleksy, chęć programowania jak PRO, ... Odnajdywanie się w miliardzie tooli, frameworków, approachy, pluginów do frameworka, konwencji i nie wiadomo czego zdaje się stawać co raz trudniejsze. Na razie sobie radzę, i mam nadzieję że już będę na emeryturze gdy to przekroczy masę krytyczną. Wtedy wezmę popcorn i będę patrzył jak kolejne rzesze ludzi tańczą jak im zagra ktoś duży, rzucając kolejny wspaniały pomysł "na życie developera" ;-)
Nie mnie nie oceniać czy X czy Y jest "be" czy też nie; to że mój stack wyglada tak a nie inaczej to nie przypadek. Niczego nikomu nie narzucam, ale i mi nikt nie będzie niczego narzucał.
I nie, nie chcę sie wdawać w dyskusje - po prostu widziałem już tyle przez lata, że czasem mowę odejmuje :-) Nie wariujmy i nie dajmy się zwariować.
Elo!
Osobiście bardziej bawię się od kilkunastu lat w Web niż w nim naprawdę pracuję, więc może jestem w sumie lama, ale osobiście wolę stosować po prostu "waniliowe" standardy i własne minimalistyczne narzędzia niż walić na stronie kilkaset KB bibliotek, żeby wyświetlić na stronie "Hellow world", a widzę, że to jest obecnie jakaś norma.
Ale myślę, że działa tutaj podobna "ekonomia odwrotna" jak kiedyś odnośnie programowania. Assembler wymiatał, kod był mały i szybki, ale długo i trudno się to pisało. Później kompy zrobiły się szybsze i można było używać nieoptymalnych, ale prostszych w użytkowaniu i bazujących na predefiniowanych gotowcach narzędzi.
> Czym niby się różni
Przyznam, że mi osobiście akurat takiego "@extend .klasa;" w CSS brakuje. Nie na tyle, żeby kombinować z preprocesorami i innymi cudami, bo (jak dla mnie) utrudnia to później analizę i trochę pomaga a trochę przeszkadza.
Ale już tłumaczę, czym się różni klasa_modyfikowana i klasa_modyfikowana (z @extend).
Otóż w ogólności ułatwia separację HTML i CSS. Owszem, mogę zawsze zamiast
class="k" napisać class="k km1 km2 km3"
tylko, że to wymaga zaingerowania w HTML, a może ja chcę właśnie mieć niezmienne moduły silnika strony a różne wersje jej wyglądu realizować, modyfikując tylko CSS (bo przecież po to właśnie on powstał i taka jest jego idea).
Oczywiście, jak nie chcę ruszać HTML, to mogę zawsze modyfikować po prostu sam CSS osobno dla każdej klasy i tak właśnie robię, tylko to bywa czasem trochę redundantne i upierdliwe. Takie np. definicje elastyczności elementów plus ich warianty z przedrostkami to po kilka linijek dodawanych tu i tam. Wygodniej byłoby móc sobie zdefiniować
.elastyczny { ... } i później dodawać go w innych klasach przez jakieś @extend czy inne @include.
p.s. Dzięki za tego bloga, miła odmiana po "portalach technologicznych", w których redaktorzy jarają się kształtem nocza w szefyrnastym modelu telefonu.