Promenljive u CSS-u (pretprocesori i imenovana svojstva)
Uvod
Nakon što je CSS ušao u upotrebu i zauzeo mesto kao jedna od osnovnih tehnologija u web dizajnu, počelo je da se "razmišlja naglas" o tome kako samom jeziku nedostaju promenljive (preko kojih bi CSS kodovi, koji su uglavnom čitki i pregledni - mogli postati još pregledniji).
Realizacija ideja o uvođenju promenljivih, u početku je 'tapkala u mestu', ali, posle nekog vremena (sredinom 2000-ih), prvo su u upotrebu uvedeni pojedini pretprocesori za CSS (od kojih su neki popularni i dan-danas), a 2017. godine (mnogi programeri bi rekli "konačno"), u specifikaciju samog jezika CSS uvrštena su i tzv. imenovana svojstva - "ne baš prave promenljive", ali - "prilično blizu".
U nastavku, osvrnućemo se na prednosti i (ni iz daleka 'zabrinjavajuće') nedostatke, oba navedena pristupa ....
Svrha promenljivih u CSS-u
Pretpostavljamo da je sasvim jasno na koji način bi upotreba promenljivih u CSS-u doprinela unapređenju preglednosti koda (u opštem smislu), i olakšala posao programerima, ali, razmotrićemo ipak (u svojstvu primera), jednu jednostavnu i uobičajenu situaciju.
Zamislićemo da se, na određenom sajtu, boja #4477ee
koristi za više različitih elemenata (prikazaćemo kod za navigacionu traku, dugmad i linkove, ali, zamislićemo da selektora ima mnogo više):
Problem može nastati ukoliko je potrebno naknadno menjati navedeni hex kod za boju, na svim mestima, a glavno pitanje je (naravno), kako najefikasnije rešiti problem.
Posao ažuriranja CSS koda svakako bi se mogao obaviti ručno - ako bi kod doslovno obuhvatao jednu vrednost i tri selektora, ali - za razliku od gornjeg primera - na ozbiljnijim sajtovima na kojima se slični selektori javljaju u mnogo većem broju (i gotovo je sigurno da su raspoređeni u nekoliko različitih CSS datoteka), posao ažuriranja nije baš skroz "lagan i kratkotrajan".
U programskim jezicima kao što su C, Java i Python (naravno, i u mnogim drugim programskim jezicima), sličnu situaciju bismo rešili upotrebom promenljivih, ili imenovanih konstanti (pri čemu bi promenljiva ili konstanta bila deklarisana i inicijalizovana na jednom mestu, na drugim mestima u kodu pozivali bismo se na navedenu promenljivu ili konstantu, a ažuriranje vrednosti tipično bi podrazumevalo izmenu u okviru jedne linije koda).
Na primer (u programskom jeziku C):
.... vrednost promenljive (kojom se određuje broj elemenata niza), unosi korisnik, dok je maksimalan broj elemenata određen preko imenovane konstante GRANICA
, i obe vrednosti se mogu menjati (na veoma pregledan način).
Razmotrićemo koliko se u okvirima CSS-a možemo približiti prikazanom nivou 'fleksibilnosti', za početak, uz upotrebu pretprocesora.
CSS pretprocesori
CSS pretprocesori su programi koji omogućavaju generisanje ('običnih') CSS datoteka, korišćenjem sintakse koja predstavlja kombinaciju običnog CSS-a, imenovanih promenljivih i drugih jezičkih konstrukcija.
Da bismo što bolje razumeli pretprocesore koji se specifično koriste za sintaksu CSS-a, podsetićemo se, preko primera sa kakvima smo se susretali u ranijim člancima, čemu (inače) služe pretprocesori u prevođenju računarskih jezika.
Opšta uloga pretprocesora u računarskim jezicima
Za primer, uzećemo programski jezik C, i razmotrićemo na koji način pretprocesor reaguje na pretprocesorske direktive #include
i #define
(koje smo koristili u prethodnim primerima):
- direktiva
#include
podrazumeva uključivanje (to jest, uvoz), određene biblioteke (u gornjem primeru, uvezena je bibliotekastdio.h
, koja sadrži funkcijeprintf
iscanf
) - direktiva
#define
predstavlja uputstvo pretprocesoru da svaku pojavu niskeGRANICA
(u programskom kodu), treba zameniti niskom"20"
- pre početka drugih operacija
Direktiva #include
funkcioniše (manje-više) po principu "copy-paste", to jest, doslovno podrazumeva kopiranje sadržaja biblioteke u programski kod koji je korisnik zapisao (to nećemo prikazivati), dok direktiva #define
(za nas u ovom trenutku mnogo zanimljivija), svodi sledeći kod ....
.... na konačni oblik ....
Pošto smo razumeli prethodno opisani princip, takođe neće biti teško da razumemo (skoro nimalo različit) princip funkcionisanja CSS pretprocesora.
Princip funkcionisanja CSS pretprocesora (osnovne mogućnosti)
U skladu sa prethodno navedenim principima, osnovni koncept upotrebe pretprocesora zarad generisanja CSS kodova, moguće je opisati na sledeći način:
- na početku je potrebno definisati "promenljive", preko pretprocesorskih direktiva (kao i obično, promenljive su predstavljene preko identifikatora - koji su povezani sa konkretnim vrednostima)
- u "CSS kodu" * se zatim mogu pozivati identifikatori prethodno definisanih "promenljivih"
- pre objavljivanja sajta, potrebno je pokrenuti CSS pretprocesor koji će kompajlirati kod i kreirati običan CSS kod (u kome su imenovane vrednosti koje su prethodno bile zadate preko identifikatora - zamenjene pripisanim vrednostima)
Takođe (kao što smo ranije pomenuli), moguće je koristiti i druge jezičke konstrukcije, na šta ćemo se osvrnuti u sledećem odeljku članka.
Za primer ćemo koristiti sintaksu popularnog pretprocesora SASS (Syntactically Awesome Style Sheet).
- Svojstva se definišu u datoteci sa ekstenzijom .sass
:
- Po potrebi, .sass
datoteka se kompajlira preko pretprocesora - pri čemu se kreira običan CSS kod:
.... koji se dalje može koristiti na isti način kao običan CSS (koji je "pisan ručno").
Dodatne mogućnosti CSS pretprocesora
Iako članak nije posvećen pretprocesoru SASS, pogledajmo još nekoliko (naprednijih) mogućnosti ovog pretprocesora.
Recimo, preko tzv. mixin
klasa (i direktive @include
), može se lako kreirati nekoliko sličnih CSS selektora.
Prvo je potrebno zapisati kod u datoteci sa ekstenzijom .sass
....
.... posle čega se kod može "provući" kroz pretprocesor, što kao rezultat daje sledeći "običan" CSS kod:
Verujemo da nije teško uvideti svu lepotu pristupa koji podrazumeva generisanje običnog CSS koda (koji se može direktno koristiti u browseru), preko naočitog, sažetog koda sa imenovanim vrednostima (praktično - promenljivama), i različitim obrascima koji dodatno olakšavaju generisanje CSS kodova.
Međutim, CSS pretprocesori imaju i jednu (očiglednu) "manu": izrada sajta uz upotrebu pretprocesora - zahteva instalaciju i upotrebu dodatnog specijalizovanog programa (SASS takođe zahteva i instalaciju Ruby servera) - što je možda nešto što, određeni web developeri, u određenim situacijama - ipak žele da izbegnu.
Imenovana svojstva (custom properties) i funkcija var
Ukoliko pristup koji zahteva dodatne programe (SASS, Ruby i sl), predstavlja problem, zbog čega je potrebno ostati u okviru onoga što nudi sam CSS (bez dodataka), moguće je definisati imenovana svojstva (to jest - "prilagođena svojstva"), koja se naknadno mogu pozivati preko funkcije var
.
Navedeni pristup ne zahteva pretprocesor (što je svakako prednost), ali, i u slučajevima kada se koriste imenovana svojstva, postoji jedan nezanemarljiv nedostatak - imenovana svojstva nisu podržana u starijim verzijama browsera (pre svega mislimo na Internet Explorer, pre verzije Edge).
Definisanje i pozivanje imenovanih svojstava
Sav kod koji se tiče imenovanih svojstava, može se zapisati u jednoj CSS datoteci (ali - kao i obično - preporučljivo je da veći blokovi koda, zarad preglednosti, budu podeljeni u zasebne datoteke, kao i to da najopštiji delove koda budu izdvojeni u zasebnu konfiguracionu datoteku i sl):
U selektoru za pseudoklasu :root
definisano je nekoliko imenovanih svojstava, koja se zatim - preko funkcije var
- pozivaju u drugim selektorima (body
i .dugme
).
Ovoga puta, za razliku od situacije kada smo koristili pretprocesor, nema kompajliranja ("sve što treba je tu"), a browseri (računajući i Edge, novu verziju Internet Explorera), već duže vreme podržavaju imenovana svojstva - i stoga će elementi biti uredno prikazani.
Ili - možda neće?!
U (čuvenih) "99% slučajeva", gornji kod je dovoljan, ali (da se podsetimo): ukoliko se sajt otvara na starijem računaru koji koristi stariju verziju određenog browsera (pogotovo ako je u pitanju neka od starijih verzija Internet Explorera, kod kojih je podrška za imenovana svojstva 'demonstrativno izostavljena'), elementi neće biti prikazani na očekivani način.
Web dizajneri mogu odlučiti (iz praktičnih razloga), * da prestanu sa pružanjem podrške za starije verzije browsera, ili - u skladu sa popularnom pošalicom koja navodi da je Internet Explorer browser čija je svrha da omogući preuzimanje drugih browsera sa interneta - mogu i ukinuti podršku za starije verzije Internet Explorera, ali, postoje (naravno) elegantniji načini da se problem reši.
Prvi način da se "saniraju posledice nepodržanosti svojstava", može biti - korišćenje direktiva @supports
i @supports not
:
.... međutim - iako jesu u pitanju "zvanični" mehanizmi namenjeni rešavanju (baš) ovakvih situacija - jedan selektor (na primer body
), mora se pojaviti u kodu dva ili više puta. *
Problemi u navedenom pristupu su sledeći:
- ako se kod zapiše dvaput (jednom unutar direktive
@support
, a drugi put unutar direktive@supports not
), oba puta mora se navesti sav kod - ako se zajednički delovi izdvoje u zaseban selektor, koji nije obuhvaćen direktivama
@supports
ili@supports not
(onako kako smo prikazali u uvodnom članku o CSS direktivi @supports) - ponovo smo kreirali (bar) dva selektora
Drugi način (koji sam po sebi takođe nije 'skroz elegantan', ali - ipak bismo rekli da je znatno elegantniji), podrazumeva (u praktičnom smislu) .... korišćenje opštih principa interpretacije CSS-a u browserima.
Ukoliko se u selektoru dvaput navede određeno svojstvo, prvo navođenje svojstva (zapravo, ne samo prvo, već - "svako osim poslednjeg") - jednostavno će biti zanemareno (i time je problem podržanih/nepodržanih imenovanih svojstava, praktično rešen):
Ukoliko browser podržava imenovana svojstva (koja su navedena), desiće se sledeće:
- pri prvoj dodeli vrednosti određenom svojstvu (uzećemo za primer boju pozadine dugmeta), browser će upamtiti vrednost (dugme dobija boju pozadine
#eb3
) - pri drugoj dodeli vrednosti, prethodna vrednost biće zanemarena (dugme dobija novu boju -
#ea4
)
Ukoliko browser (sa druge strane), ne podržava imenovana svojstva (ponovo posmatramo boju pozadine dugmeta), desiće se sledeće:
- pri prvoj dodeli vrednosti browser će upamtiti vrednost (i ovoga puta dugme dobija boju pozadine
#eb3
) - pri drugoj dodeli vrednosti - budući da browser ne prepoznaje funkciju
var
- biće zanemaren ceo red CSS-a i prethodna vrednost će biti očuvana (boja dugmeta je i dalje#eb3
)
Ovakav kod jeste pomalo redundantan (tj. nije baš skroz elegantan), ali - ukoliko je potrebno održati kompatibilnost sa starijim verzijama i/ili nepodržanim browserima - u pitanju je sasvim prihvatljiva opcija.
Opseg definisanosti imenovanih svojstava
Mogli ste primetiti da su u prethodnim primerima imenovana svojstva bila deklarisana unutar selektora :root
- što znači da se navedena svojstva mogu koristiti u svim selektorima.
Pored navedenog pristupa (koji je daleko najuobičajeniji), imenovana svojstva mogu se definisati i unutar drugih selektora (najbolje ipak - i u takvim situacijama - na početku datoteke), čime se opseg definisanosti imenovanih svojstava sužava.
Ako definišemo sledeća imenovana svojstva:
.... i pozovemo ih naknadno u drugim selektorima, dobićemo sledeći rezultat (objašnjenja su u komentarima):
Kao što smo već naveli, u praksi preovladava pristup koji podrazumeva imenovanje svojstava u okviru opsega :root
, ali, razume se da dodatne opcije sasvim dobro dođu.
Za kraj ....
Namera nam je bila da u relativno neobimnom članku predstavimo dva pristupa koji omogućavaju upotrebu 'promenljivih' u CSS-u (na način koji je, doduše, prilično različit u odnosu na pristup koji je svojstven jezicima kao što su C, Java i sl), međutim, ovoga puta nismo hteli da zalazimo u detalje (u većoj meri).
Naprednim opcijama CSS pretprocesora (sa jedne strane) i upotrebi imenovanih svojstava u JavaScript-u (sa druge strane), svakako ćemo i u budućnosti posvećivati pažnju ....