Cesta do nitra tří nejznámějších CSS preprocesorů pokračuje, i když ne tak, jak jsem původně plánoval.
CSS preprocesor je nástroj, který vám ze zdrojového kódu zapsaného ve vlastní syntaxi vygeneruje CSS pro prohlížeč. Mezi nejznámější patří SASS, LESS a Stylus. Ukázali jsme si, jak je nainstalovat a naťukli téma syntaxe a mixinů. Všechny tři preprocesory nabízejí fundamentálně rozdílný způsob, jak programovat s mixiny. Každý je v tom jinak konzistentní a každý umí být jinak matoucí.
Pro každý preprocesor existuje galerie hotových mixinů, do kterých nahlédněte přinejmenším k posouzení jejich srozumitelnosti. Pro SASS existuje komplexní Compass, LESS má framework Twitter Bootstrap nebo drobné Elements a Stylus NIB.
…tak takhle začínal článek, který jsem rozepsal před rokem a čtvrt a nikdy nedokončil. Přišel jsem totiž k závěru, že všechny tři preprocesory jsou, alespoň zatím, nepoužitelné. Jejich nasazení by představovalo tolik ústupků, že by se vedle nich potenciální výhody dočista ztrácely. Dnes to vysvětlím.
CSS preprocesor není věc, se kterou bych začínal na zelené louce. Nejsem úplný nováček, s kaskádovými styly mám 10letou historii, kdysi mě mocně nakopl Pixy a dnes mám celkem vypilovaný systém, do kterého potřebuji preprocesor zasadit. Jak ten systém vypadá? Podle mého nejde o vůbec nic neobvyklého.
Když se podíváte pod pokličku třeba tohoto blogu, zjistíte, že se ze
serveru načítá právě jeden CSS soubor a právě jeden JS, oba
minimalizované. Přičemž onen soubor combined.css
obsahuje styly
pro všechna zařízení, tj. obrazovku, tiskárnu, mobily, retinu atd.
Nezkomprimovaný vypadá nějak takto:
@import "http://fonts.googleapis.com/css?family=Open+Sans:400&subset=latin,latin-ext";
@import "libs/reset.css";
@import "libs/classes.css";
@import "libs/fshl.css" screen;
@import "fancybox/jquery.fancybox.css" screen;
@import "layout.css" screen;
@import "homepage.css" screen;
@import "libs/print.css" print;
@import "print.css" print;
Celý stylopis je tedy rozdělený do řady menších jednotek. V adresáři
libs
se skrývá drobný CSS framework definující základní
pravidla, která používám na všech projektech (btw reset.css
není prasárnička á la Eric Meyer’s reset, spíš něco takového), dále tu
jsou styly použitých komponent a vůbec celého projektu. Většina importů
uvádí závislost na médiu, takže se aplikují jen pro tiskárnu či
obrazovku.
V samotných souborech rád používám odsazování, které mi zásadně zvyšuje čitelnost:
a {
padding: 3px;
margin: 0 -3px;
text-decoration: none;
}
a:hover, a:active, a:focus {
color: white;
background-color: blue;
}
article {
margin: 2em 0;
}
article hr {
visibility: visible;
clear: none;
}
article footer {
margin-top: 2em;
}
Musím zdůraznit, že tohle všechno je normální CSS 2.0, které správně
zobrazí jakýkoliv prohlížeč. Jediná podmínka je, že pravidla
@import
musí být uvedena na začátku souboru před čímkoliv
jiným. Přičemž uvnitř inkludovaných souborů lze klidně vkládat další
(to moc nedělám) a nebo uvádět media queries, pomocí kterých doladím
podobu pro mobily nebo nasadím obrázky ve vyšším rozlišení pro displeje
retina (prohlížeč správně kombinuje media query v souboru s médiem
uvedeným u importu).
Celou strukturu by bylo možné sakumprásk nahrát na server a normálně by
to fungovalo. Udělal jsem si ale jednoduchou
funkci, která všechny ty klauzule @import
expanduje do
jediného souboru, ten ještě zkomprimuje a teprve poté pošle na server. Má
to zásadní vliv na rychlost načítání stránek, obzvlášť, pokud je
čtete v metru.
A do tohoto systému jsem chtěl přivést CSS preprocesor, od kterého jsem si sliboval, že samozřejmě převezme úlohu sestavení výsledného stylu, a dovolí mi lépe řešit některé ošidnosti CSS.
Jak jsem byl bláhový!
Aby preprocesor @importované soubory vůbec zpracoval, je třeba jejich
příponu změnit z .css
na proprietární (tj. .less
,
.scss
, .sass
nebo .styl
). Jistě, je
zcela správné používat proprietární přípony, jen mi o to, že
preprocesor si neporadí také s CSS souborem, přičemž stačí jej pouze
přejmenovat a hned to jde.
Najednou koukám, že se mi ztratily některé obrázky. Proč? Například
jquery.fancybox.css
obsahuje definici
background-image: url('fancybox_sprite.png')
, přičemž oba
soubory se nachází v podadresáři fancybox
. Když zkombinujeme
styly z různých podadresářů do jednoho souboru, pochopitelně musíme
transponovat všechny relativní cesty. To dá selský rozum. Chová se tak
samozřejmě i prohlížeč, když vyhodnocuje @import
, dělá to
i zmíněná 15řádková los stupidos funkce v FTP deploymentu, jen
preprocesory, ty to neumí a soubor jednoduše zkriplí. Vážně ☹
Hlavně jsem nepřišel na způsob, jak tuhle kulervoucí vadu obejít. Udělat si bordel ve složce a všechny obrázky nahrát do kořenového adresáře? Modifikovat cesty ve stylech? Ve stylech třetích stran? Naprogramovat prepreprocesor? Čím tyhle ústupy preprocesor vyváží?
Jenže tím zoufalství zdaleka nekončí…
CSS preprocesory si neporadily ani s media queries u pravidel
@import
:
@import "fancybox/jquery.fancybox.css" screen;
Je třeba screen
odstranit a celý obsah vkládaného souboru
obalit do @media screen { ... }
(neplatí pro SASS, ten sice taky
nerozumí mediu v pravidle @import
, ale zvládá opis
@media screen { @import "style.css" }
). Krom toho, že fakt nechci
modifikovat soubory natož třetích stran, protože to komplikuje budoucí
aktualizace, nemusí jít o nikterak triviální úpravu. Zrovna zmíněný
fancybox.css
obsahuje @media queries pro retinu a otázkou je, jak
se s jejich zanořením preprocesor vypořádá. Pohořely na tom všechny.
A to stále není všechno!
Klíčové je, aby preprocesor dokázal schroustat mé existující CSS
(jimž jsem byl donucen změnit příponu). Žádné prasárny ve stylech
nepoužívám, SASS i LESS se sekly jen na rovnítku =padding: 0
(LESS ležérně chybu ohlásil na docela jiném řádku), což je obdoba
legendárního podtržítkového
hacku pro IE6 ve verzi pro IE7. Kašlat na IE7, tohle jedno pravidlo jsem
smazal a vše fungovalo.
Ne tak v případě Stylusu (a přiznávám, že tomuto nástroji jsem
nadržoval). Ten se sesypal na mém oblíbeném odsazovaní. Prohlásil
unexpected "indent"
a bylo zalíčené. Stačí běžný
komentář a Stylus odchází plakat do kouta.
Přátelé, abych mohl nasadit CSS preprocesor, musel bych totálně dokurvit čistou adresářovou strukturu a drasticky modifikovat své styly i styly třetích stran. Abych mohl vychutnat mixínek? Ne, děkuji, to vážně ne 🙂
A to nemluvě o zabugovanosti dvojice LESS a Stylus, dokonce Stylus ani neumí pravidlo @import přehodit na začátek souboru a generuje nevalidní CSS. Smutné. (Prosím, nepište mi, že vy preprocessor používáte a všechno vám funguje, je to stejně hloupé, jako kdyby vás doktor odmítl se zlomeninou nohy s tím, že jeho noha vůbec nebolí.)
O rok a čtvrt později
Tohle se odehrálo před rokem a čtvrt. Hodně dlouhá doba ve vývoji žhavých technologií. Chyby jsem tehdy nahlásil a dnes se podíval, co je u našich tří kamarádů nového.
- SASS udržuje sassus quo, všechny zmíněné nedostatky i přednosti setrvávají.
- Stylus? Těžké zklamání, naučil se sice řešit relativní cesty při
uvedení parameteru
-r
, ale žádný bug nebyl vyřešen a autor na něj nemá čas. - LESS. Před rokem největší outsider mi dnes vyrobil perfektní CSS. Poradil si se vším. Wow!
LESS mi udělal obrovskou radost! Ano, nepoužil jsem žádné pokročilé
funkce a jen se raduji z toho, že rozumí CSS alespoň tak, jako IE6, dokáže
vygenerovat při uvedení parametru -ru
sloučený soubor se
správnými cestami k obrázkům, soubory není třeba přejmenovávat díky
@import (less) "file.css"
a mohu jej proto neskutečně snadno
nasadit i do existujícího projektu a začít využívat všech výhod
preprocesorů. Ty léta náročného vývoje nepřišly nazdar!
HAPPY END.
Komentáře
v6ak #1
K těm knihovnám: Nemám s tím nějaké velké zkušenosti, ale používal jsem trochu CompLESS a vypadá, že je o trochu bohatější než Elements.
David Grudl #2
CSS preprocesory: Nejspíš nějaký potřebujete a nejspíš je to LESS – skvělá reakce od Martina Michálka, moc se mi líbí přirovnání ke Gitu.
Jen dodám, že mám připravený materiál ještě pro jeden článek, kterou celou věc uchopí z docela jiného konce.
riki #3
alebo http://getpreboot.com/
Martin Michálek #4
Davide, ještě na vysvětlení, proč tvé požadavky na preprocesor považuji za nestandardní. :)
Obecně vzato — nestandardní je scénář, ve kterém chceš preprocesorem zpracovat existující CSSko. Ke všemu CSSko, které s nadsázkou používá oldskul postupy. ;)
Preprocesor považuji za jiný jazyk s odlišnou filozofií a naprosto odlišným způsobem práce. Kóderské návyky při práci s preprocesorem jsou jiné než při práci s CSSkem.
Konkrétně…
Ad. jiné koncovek souborů (.css vs .less): — Ve smyslu výšeuvedeného nemůže na preprocesor fungovat zvýrazňovač syntaxe z CSS a potřebuješ odlišit zdrojáky od zkompilovaných CSS souborů. Odlišení koncovek je existenční nutnost.
Ad. cesty k obrázkům při importování jquery.fancybox.css — Fancybox má být (a jednou snad bude) konfigurovatelná LESS komponenta. Ve tvém postupu se míchá starý a nový svět front-endu. Se starými CSSky třetích stran to zatím bohužel nelze jinak a je potřeba to nějak obcházet.
Ad. rovnítkový hack — on někdo dneska používá CSS hacky? :) Pro detekci IE máme podmíněné komentáře, ne? Preprocesory s tím nepočítají, protože speciální znaky používají pro něco jiného.
v6ak #5
#4 Martin Michálek, Celkem souhlas, tak ze 2/3, možná 3/4. Používat zvýrazňování (a napovídání apod.) CSS pro LESS je hack podobný používání přípony phtml pro šablony Latte. Ono to bude lepší než plaintext, ale nebude to úplně ono. Pokud už chci tento hack použít, můžu si nastavit svůj editor a nekomplikovat tak (zvlášť u týmového projektu) rozlišování CSS a LESS (případně PHP a Latte) ostatním členům týmu. Nebo i sobě při změně editoru/IDE.
Nebudu ale moc souhlasit s tím, že jquery.fancybox.css apod. nemá preprocesor řešit. Považuji za standardní požadavek reálného světa, aby šlo pracovat i s ostatními CSS soubory. Pokud bude Fancybox standardní konfigurovatelná LESS komponenta, pak fajn. Ale co kdyby se autor rozhodl raději použít např. SASS? Pak by LESS IMHO mělo umožňovat zpracovat výsledné CSSko. (Neříkám, že by mělo umět zkompilovat SASS, i když by to byl možná zajímavý bonus.)
v6ak #6
Budu trochu OT. #2 David Grudl mohl bych se zeptat, jak funguje tady to skloňování jmen a příjmení? Je to vlastní práce, nebo nějaká knihovna? Jak to funguje na přezdívky jako #1 v6ak a #3 riki?
David Grudl #7
#4 Martin Michálek, asi jsem nebyl dostatečně srozumitelný, pokusím se to napravit. Nejprve musím citovat https://sass-lang.com/
To je nesmírně důležitá věc, v podstatě říká: nasaď SASS do svého existujícího workflow a budeš sklízet jen výhody. Podobně se tváří i ostatní systémy, byť neslibují 100% kompatibilitu „Stylus: So although not every plain-CSS stylesheet will work with zero modification, this feature allows those who prefer CSS syntax to continue doing so while leveraging Stylus’ other powerful features.“
Vezmu to od konce: rovnítkový hack jsem zmínil jako to jediné, čemu SASS a LESS neporozuměl, tedy ve smyslu „super, stylům rozumí perfektně!“ Ovšem LESS má problém s číslováním řádků, když hlásí chybu, a Stylus je mimo hru úplně, protože si vyseká zuby na pouhém odsazení.
ad koncovky: je zcela správné, že systémy používají vlastní koncovky (ale máš pravdu, že z článku to blbě vyznělo). Kritizuji tady pouze a jen funkci @import, která se jinak chová k souborům s příponou CSS a jinak k vlastním (nebo bez přípony, což je další zrůdnost). Tohle mělo být rozlišeno jinak, abych nemusel, když jsi CSS (tedy, subset SCSS, tedy SCSS s příponou CSS) vložit, měnit souboru příponu cílového souboru, ale například rozlišovat chování syntaxí
@import url(file.css)
vs hypotetické@import inline("file.css")
.A s funkcí @import souvisí i relativní cesty k obrázkům. Zkrátka preprocesory se chovají (chovaly) jinak, než CSS, než prohlížeč, než velí zdravý rozum a tohle vidím jako jednoznačnou chybu. Skutečně mě nenapadá scénář, kdy by mělo netransponování relativních cest výhodu.
Říkáš „ Se starými CSSky třetích stran to zatím bohužel nelze jinak a je potřeba to nějak obcházet.“ – Tomu vůbec nerozumím, v tomhle asi tkví rozdíl našeho vnímání světa. Vidím to totiž spíš jako: „A proč by to nešlo jinak?“ 🙂 Že má preprocesor nějaký nedostatek či chybu není důvod složit ruce do klína a říct – nejde to. Tady není žádný starý a nový svět, tady je CSS, do jehož světa přicházejí nástroje, které nám mají sloužit, nikoliv vytvářet vlastní problémy. CSS není oldskul a jeho používání nestandardní scénář, CSS je základ.
Nakonec LESS zmíněné chyby opravil a ono už to najednou jde. Proto jsem ho začal předevčírem používat.
(Btw, přemýšlel jsi někdy nad tím, proč výkladní skříň preprocesoru framework Twitter Bootstrap pro označení třeba tlačítek používá dvojici tříd
btn btn-primary
, kdyžbtn-primary
by klidně mohlobtn
přimixovávat?)Martin Michálek #8
#5 v6ak, #7 David Grudl Ještě obšírněji k ne-transponování url při importování CSSka třetí strany: vidím to tak, že všechny preprocesory při zpracování vlastních importů (tedy souborů .less, .scss nebo .styl) konzistentně říkají, že root adresář pro cesty k obrázkům pro url(…) je ten, ve kterém bude vygenerované .css. Když přejmenuješ jquery.fancybox.css na jquery.fancybox.less, říkáš tím preprocesoru, že souhlasíš a že budeš hrát jeho hru.
Pokud bys jej nepřejmenoval a importoval jquery.fancybox.css, což je myslím správnější řešení (proč v adresáři třetí strany vytvářet další soubory?), preprocesor ti ve výsledném CSS vygeneruje běžný „CSS @import“. To je myslím OK.
V tom přejmenování vidím ten střet „starého“ a „nového“ z původního komentáře.
Běžný „CSS @import“ ti vadí, protože vytváří request navíc a nekomprimuje importované CSS? Pak bys měl vygenerované CSS prohnat nějakým buildovacím procesem, který CSSka spojí a minifikuje.
Osobně preprocesor nevnímám jako buildovací nástroj. Kód, který mi generuje, potřebuji na lokální mašině co nejpřehlednější — tzn. ne-minifikovaný, s komentáři atd.
Martin Michálek #9
#7 David Grudl, (Btw, přemýšlel jsi někdy nad tím, proč výkladní skříň preprocesoru framework Twitter Bootstrap pro označení třeba tlačítek používá dvojici tříd btn btn-primary, když btn-primary by klidně mohlo btn přimixovávat?)
Kvůli dokumentace. :) Jako autor si můžeš samozřejmě vybrat, jestli použiješ mixin nebo třídu v HTML. Bootstrap dokumentace je zaměřená i na typy autorů, kteří moc nechtějí sahat do CSSka nebo JS souborů.
David Grudl #10
#8 Martin Michálek, myslím, že se dostáváme k jádru věci. Běžný CSS import není jen taková lecjaká věc – musí se nacházet úplně na začátku stylu. S jeho umístěním v rámci celého stylopisu máme tedy značně svázané ruce. Přičemž pořadí je podstatné pravidlo kaskádovosti. Jinými slovy, preprocessor se nemůže zříci funkce buildovacího nástroje, je to naopak dosti klíčový úkol (a vůbec nejde o minifikaci, ta je jen příjemným bonusem). Pokud buildování nezvládne dobře, zasadit jej do existujícího workflow se stává noční můrou.
#9 Martin Michálek, můžeš to prosím rozvést, úplně jsem tě nepochopil. Konkrétně třeba proč
.btn-primary
v sobě už neobsahuje.btn
.Inza #11
WOW, jak to tak čtu, tak si říkám: „Zlatá asset pipeline v RoR…“ ;)
A jen jeden dotaz Davide: Jak s tím svým výsledným jedním CSS souborem řešíš mobile first – tedy třeba to, že např. vůbec nechci stahovat v mobilu CSS styly pro desktop?
David Grudl #12
#11 Inza, u webů, které dělám, není ve velikosti celého zazipovaného stylu a části pro mobily takového rozdílu, aby mělo smysl se tím zabývat.
Martin Michálek #13
#10 David Grudl, K CSS @importům — problém s pořadím samozřejmě může nastat, ale potřeboval bych reálný příklad. Moje fantazie se tady ukázala jako nedostatečná. :)
K .btn a .btn-primary v Bootstrapu — Už se chytám. :) Důvod je udržovatelnost LESS/CSS kódu. Představ si, že těch variací
.btn
potřebuješ u tlačítka víc:.btn .btn-primary .btn-large .btn-block
. Ještě si představ, že každá variace přimixovává původní.btn
. A teď si představ vygenerovaný CSS kód. :) Bude to pořádný sloupec deklarací, navíc teoreticky plných konfliktů ve specifičnosti. Teoreticky proto, že zrovna u tlačítek to platit nemusí, ale při spravování komplexního kódu kategorie Bootstrap by se to určitě projevilo.Peter Kahoun #14
#10 David Grudl, #13 Martin Michálek Ne teoreticky, ono by to u tlačítek s 2+ btn-třídami nefungovalo prakticky. Ale do budoucna by pro Bootstrap bylo řešením .btn abstrahovat jako [class^=„btn-“] (pak není nutno ji ani jmenovat v HTML, ani extendovat v CSS).
v6ak #15
#14 Peter Kahoun, Což je celkem hnus, protože:
class="btn"
, mohu to přimixovat i do jiných tříd, identifikátorů apod.class="foo btn-primary"
?David Grudl #16
#13 Martin Michálek, Příkladem je třeba:
Preprocesor první soubor fyzicky vloží, kdežto u druhého vygeneruje běžný CSS @import. Tím je ale výsledek nevalidní, neboť před pravidlem import nesmí být CSS styl. Řešením je buď obrátit pořadí, což nejde, protože tím se mění význam, nebo souboru (třetí knihovny) přejmenovat koncovku, což prudí.
Jinými slovy, preprocesor by měl (třeba pomocí klíčového slova inline, nevím) dát možnost naimportovat CSS soubor stejně, jako importuje soubory své.
Ondřej Kubíček #17
#16 David Grudl, co použít import options? tím se ti css soubor vloží přímo jako by to byl less soubor
https://lesscss.org/features/
David Grudl #18
#17 Ondřej Kubíček, ano, tohle je jedna z těch milých vlastností, díky kterým jsem nadšeným uživatelem LESS.
taco #19
Netušíte, jak jsou na tom dnes, po čtyřech letech? Vzhledem k tomu, že bootstrap4 je pod SASS…
Dík.
Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.