Smatram da je sledeći korak, i nešto čemu treba stremiti, CSS-in-JS rešenje. Kada pišemo komponente i “lepimo” na njih CSS uvek postoji određeno “trenje” između javascript logike i CSS sistema klasa. Naime, CSS i javascript komponente nisu povezane apsolutno ničim - računamo na zdrav razum pri imenovanju i koncentraciju programera da pravilno napiše klasu unutar javascript logike i zatim istu tu klasu da napiše pravilno unutar CSS fajlova. Površno gledano nije preterano komplikovano, ali, kako sistem raste, novi programeri ulaze u igru i poslovni zahtevi kao i uvek postanu klizni, greške mogu da se potkradu - mi se aktivno oslanjamo na “Magic strings anti-pattern” (ime klase je manje-više arbitrarno i ponovo ručno kucano na više mesta), i opasnost da CSS kod postane “mrtav kod” je česta i realna (promenom .js logike možemo da ostanemo bez nekih klasa u akciji, te njihov CSS kod postaje suvišan u bazi, ali opet prisutan - ako programer nije svestan rada svog kolege).
Sa ovim uvodom, želim samo da kažem “šta mi je na duši” - u surovoj realnosti, ne možemo očekivati uvek na budžet, vreme i dobru volju tima da se neki veliki stari sistem prevede u CSS-in-JS. Tada, možemo učiniti ono što će biti i tema mog članka, a to je - napisati najbolji mogući CSS u ekosistemu javascript komponenata. Članak će težiti da predoči rešenja koja ja preferiram shodno mom znanju i iskustvu, naravno da postoje bolji, ili u najmanju ruku drugačiji, a jednako efektivni pristupi. Elem, da počnemo.
Pravila, pravila, da bi me udavila
Čest cilj, do koga se može stići na bezbroj načina je - malo napisanog CSS-a (bez ponavljanja) i maksimum jedan prateći (S)CSS fajl po komponenti.
Da ne bismo upali u zamku izmišljanja tople vode, najbolje je okrenuti se nekom postojećem pravilniku - industrija je puna raznih konvencija i pravilnika koje su sastavljali vrlo iskusni prethodnici, i odbiti da “stojimo na ramenima divova” je mahom stvar nadmenosti, nezainteresovanosti ili nečeg trećeg, budući da je sav materijal dostupan besplatno i u bezbroj sažetaka (čak i ovaj članak je jedan od tih sažetaka).
Svaki dobar CSS sistem se bavi nekolicinom istih problema. Neka gruba podela osnovnih elemenata dizajna bi bila:
- sistem, odnosno skup klasa za topologiju (layout) stranice koji je svestan fludine prirode stranice (responsive)
- skup korisnih (utility) klasa za ritmičan razmak između elemenata (margins & padding)
- skup korisnih (utility) klasa za česte elemente dizajna (tipografija, boje, zaobljene ivice i senke i slično)
- skup korisnih (utility) klasa za neke česte operacije (razmišljajte u smeru text-align:center i display:inline-block ili list-style-type:none)
- ustaljen metod pisanja izuzetaka (ovo obuhvata imenovanje jedinstvenih elemenata i varijacija tih elemenata)
Ovu problematiku obrađuje nekolicina sistema (govorim o načinu razmišljanja) i konvencija koje ćemo taksativno pobrojati (predlažem da ih sami istražite):
I pretpostavljam mnoge druge - ovo je lista koja mi sada pada na pamet. Cilj ovih sistema razmišljanja je uvek skoro isti, a to je smanjiti ponavljanje unutar koda (dobra praksa pri razvoju) i iscepkati stilove po fajlovima i sa logičnim imenima tako da ih je što lakše menjati (koncentrisane, precizne promene su idealne za održavanje).
Kako ovo ume da izgleda u realnosti?
Retko koji tim uzme jedan ovaj pravilnik i ispoštuje ga do kraja. Razlozi su mnogi, od zahtevanog znanja za primenu, do proste činjenice da za stroga pravila mora da postoji “babaroga” u timu koja će “spucati” svaki dodatak kodu koji ne prati ova stroga pravila, a to niko ne želi da bude - što zbog nezahvalnosti te pozicije, što zbog subjektivnosti svih ovih pristupa - ne postoji objektivan, “tačan” način da se stilovi pišu iz perspektive biznisa, stoga je rigidnost u pristupu stvar volje i čvrstog istrajanja u odluci (koja možda nije idealna za dati problem).
U realnosti ćemo najčešće primeniti kombinaciju ovih principa na složen, brzorastući i dinamičan sistem, baziran na komponentama (većina šoferšajbni interneta je građeno bilo u Reactu, Angularu, Vue-u ili u nekoj drugoj tehnologiji koja shodno SOLID principima želi da razdvoji prikaz i logiku i iscepka komade logike u višekratne komponente).
Mnogo puta će se se vrlo malo “utility” klasa pisati. Za osnovicu se često uzme Bootstrap ili Bootstrapolika biblioteka koja u sebi već rešava i logički i stilski neke česte, ponavljajuće elemente korisničkog interfejsa kao što su harmonike ili višespratni meniji (ne daj Bože da developer sedne i napravi ovo sam, ode 45 minuta dok si rek’o keks). Svaka ova Bootstrapolika biblioteka, sadržala .js elemente ili ne, uglavnom se pobrine za solidan deo SMACSS kategorija. Svaka ovakva biblioteka sadrži korisne klase za topologiju stranice, za standardizovane razmake (margins i padding korisne klase), a često je dovoljno da sami napišemo nekoliko korisnih klasa za standardne veličine i tip tipografije, zatim česte boje i neke standarde koje naš dizajner uvede (jednako zakrivljene ivice mi padaju na pamet). Ovo nam ostavlja zadatak da pišemo samo stilove za neke specifične komponente - cilj je da sa određenom, predefinisanom osnovom možemo da uradimo topologiju stranice i osnovnu stilizaciju bez ručnog pisanja dodatnog CSS-a.
Dodao bih još dok smo ovde - ako imamo više “tema” (svetlu i tamnu na primer), ili ako očekujemo “tektonske” promene u budućnosti, korisno je nazvati klase koje dodeljuju boju “primary”, “secondary”, ili “info”, “error” i tako dalje. Ako upadnemo u grešku da ih zovemo recimo “light-gray”, ako dizajner doda novu svetlo sivu onda moramo da je zovemo “light-gray-2” ili još gore “lighter-gray” - ovo mi je posebno smešno kada se desi, i dizajner doda treću sivu koja je između prve dve, pa nije ni “light” ni “ligther”.
Ovo je korisno ispoštovati i kod veličine tipografije. Tekst uglavnom ima neku veličinu shodno ulozi, pa ako se recimo standardan tekst promeni iz 14px u 16px, ne možemo da promenimo klasu “text-14” da sada dodeljuje veličinu od 16px, to bi bilo dosta kontraintuitivno, već moramo u svakoj komponenti da menjamo dodeljenu klasu. Međutim, da je ovo bila klasa “text-regular”, mogli bismo bez problema da promenimo “font-size” bez straha od nove “kontraintuitivne” klase.
Stilizovanje komponenti
Konačno, dolazimo do suštine - sa dobro podešenom osnovicom, možemo da pređemo u stilizovanje komponenti. Pisali mi strogo objektno orijentisan sistem ili ne, SOLID principi su vrlo korisni vodiči za argumentovanu izgradnju komponenata u kompleksnom, brzorastućem i promenjivom sistemu. Naravno da postoje intrikantnosti SOLID principa koje su primenjive samo na određene jezike koji dozvoljavaju polimorfizam na primer, ili čije su klase zapravo klase, a ne javascript sintaktički šećer, ali ovde hoću da se ograničim na rezonovanje o pisanju komponenti, odnosno još preciznije, njihovo stilizovanje.
Dobro stilizovan komponent ne utiče ni na šta van sebe. Da bi se ovo postiglo stilizacija se vrši na dva nivoa: jedan je kontejnerska komponenta koja se bavi topologijom stranice, i može da varira u odnosu na kontekst u kom se komponenta pojavljuje/ponavlja. Kontejnerska komponenta je nosilac margina i specijalnih klasa koja se bave topologijom stranice (često je to raspodela na kolumne). Drugi nivo, ono što je zapravo “meso”, odnosno, vizuelni identitet naše komponente je mesto gde apliciramo pravila tipografije, padding pravila, boje, itd itd. Tako, ako komponenta menja mesto na stranici, ne zahteva promenu, menjamo eventualno kontejnersku komponentu ako ima potrebe, a ako ostaje na istom mestu a menja vizuelni identitet, ne moramo da menjamo roditeljsku komponentu i potencijalno ulazimo u konflikt sa tuđim radom.
Dinamična grupa komponenata, koje se uslovno pojavljuju, favorizuju donju marginu kao meru razmaka. Na ovaj način, zadržavamo vertikalni tempo, čak i ako naše komponente A, B i C nisu uvek u istom rapsoredu, ili čak nisu uvek prisutne. Naime, ako A, B i C imaju jednaku donju marginu, topologija naše stranice ostaje “ista”, čak i ako imamo samo komponente A i C, ili ako se komponente pojave u rasporedu A, C i B. Tehnički bi mogli da favorizujemo i gornju, ali onda samo gornju marginu.
Dva principa koja možemo ukrasti iz S.O.L.I.D.-a su Single Responsiblity Principle i Open/Closed principle.
Ukratko, Single Risponsibility princip kaže u našem kontekstu - stil komponente radi jednu stvar, i radi je dobro. Ovo postižemo korisnim klasama za često ponavljane stilove - dakle imamo “utility” klase koje u sebi sadrže jedan red CSS-a - bilo da je on dodaje primarnu boju dizajna kao pozadinu, ili određuje veličinu tipografije.
0pen/Closed princip - opet, u našem kontekstu nalaže da izgled definišu mahom jednostavne klase, te da, shodno stanju ili podvrsti komponente dodajemo nove klase koje modifikuju izgled samo koliko je potrebno. U jednom primeru: koristeći BEM metodu, osnovan izgled komponente definišemo u klasi “.product__title”, koja je uvek prisutna, ali u slučaju da je proizvod cipela, tekst je veći, pa dodajemo klasu “.product__title–shoe” koja definiše samo tu, specifičnu za cipele promenu. Alternativa (jednako validna), je da uslovno dodamo korisnu klasu za veličinu teksta (ako je promena tako laka), ali taj pristup ima svoje nedostatke. Ne znamo da li će promena veličine teksta uvek biti razlika između regularnog proizvoda i cipele, stoga, ako se dizajn promeni moramo da dodajemo neku drugu klasu. Takođe, regulišemo stil na neki hibridni način, između javascript-a i CSS-a, što na kraju dana nije loše ako koristimo CSS-in-JS rešenje (koje ja predlažem na početku teksta), ali u ovom kontekstu, pošto radimo “tradicionalan” CSS, postaje kontraproduktivno - onemogućava preciznu, minimalnu promenu (1 CSS fajl = 1 stilska promena).
Takođe bih spomenuo REP princip i njegove osnove CCP i CRP. Reuse-Release Equivalence princip se izvorno odnosi na pakete. On je osnovan na Common Closure Principle-u (CCP) i Common Reuse Principle-u (CRP).
U kontekstu pisanja i stilizovanja komponenti, ovo znači sledeće: ono što se menja u isto vreme, iz istih razloga staviti na jedno mesto (CCP), i nemojmo praviti zavisnosti od klasa koje se nužno ne koriste (CRP).
Hajde da ilustrujemo kroz jedan primer: recimo da imamo “običan proizvod” i “istaknut proizvod”. Sadržaj običnog i istaknutog proizvoda mogu biti komponente A, B i C, ili samo A i B, ili bilo koja druga kombinacija. Stilovi vezani za A, B i C su u zasebnim fajlovima i tiču se običnog prizvoda. Stilovi A, B i C u slučaju da su unutar “istaknutog proizvoda” bi mogli da se nalaze u fajlu za “istaknut proizvod”. Međutim, ništa nam ne garantuje da će svaki “istaknut proizvod” imati A, B i C unutar sebe. Zato ove stilove stavljamo u fajlove A, B, odnosno C ( “.proizvod–istaknut .a” pravila idu zajedno sa stilovima za “.a”, a ne unutar “.proizvod–istaknut” stilova koji se možda bave nečim drugim).
Zaključak
Ovde sam vrlo plitko dotakao jedan moguć pristup stilizovanju komponenti u kompleksnom, brzorastućem i dinamičnom sistemu. Ukoliko vaš sistem nije takav, većina ovih pravila su nepotrebna stega na vaš razvojni proces. Međutim, ako vaše okruženje odgovara ovom opisu, svesrdno preporučujem primenu barem nekih od metodologija koje sam pobrojao s početka. Naravno da je uvek lakše jednostavno samo pisati još CSS-a, i naravno, sa trenutnim brzim konekcijama, mala ili velika CSS datoteka nema značajan otisak na performanse, ali ono što ovi sistemi zapravo nude je brzina razvoja i lakoća održavanja. Čitajte, razmišljajte i komplikujte sebi život - možda vam iskustvo jednog konciznog i organizovanog CSS-a na velikom projektu zauvek promeni način na koji posmatrate ovaj, tako često nemio i “usputan” deo frontend-a.