phpFashion

Rubrika HTML & co

Jak na souhlas s cookie ve zkurvené EU

Víte, že nejpozději do 30. září 2015 máte povinnost získat souhlas uživatelů, pokud používáte na svém webu třeba Google AdSense? Proč, nač a jak na to?

(Verze pro příznivce EU)

Evropská unie přišla se zkurvenou směrnicí (tzv. sušenkovým zákonem), podle které musí uživatel webu dát souhlas s používáním cookies nebo obdobných mechanismů. Souhlas musí být také kurva odvolatelný.

Výjimkou jsou cookies, které jsou nezbytné pro poskytnutí služby, kterou si uživatel sám vyžádal, například cookie pro zkurvený nákupní košík. Naopak příkladem cookies, které nejsou nezbytně nutné, jsou cookie pro analýzu návštěvnosti, reklamní systémy nebo zkurvené pluginy sociálních sítí.

Česko zkurvenou evropskou směrnici implementovalo tak, že ji vlastně ignorovalo. Což se zkurveně nelíbí Úřadu pro ochranu osobních údajů, který se tím bude zabývat. Zatím tedy u nás není potřeba uživatele zkurveně žádat o souhlas, nicméně provozovatelé webů mají povinnost informovat o rozsahu a účelu jejich zpracování, například v podmínkách používání na svých stránkách. Uživatelé také musí mít možnost takové zpracování odmítnout, kurva fix.

…pokračování


Jak na souhlas s cookie v EU

Víte, že nejpozději do 30. září 2015 máte povinnost získat souhlas uživatelů, pokud používáte na svém webu třeba Google AdSense? Proč, nač a jak na to?

Evropská unie přišla se směrnicí (tzv. sušenkovým zákonem), podle které musí uživatel webu dát souhlas s používáním cookies nebo obdobných mechanismů. A souhlas musí být také odvolatelný.

Výjimkou jsou cookies, které jsou nezbytné pro poskytnutí služby, kterou si uživatel sám vyžádal, například cookie pro nákupní košík. Naopak příkladem cookies, které nejsou nezbytně nutné, jsou cookie pro analýzu návštěvnosti, reklamní systémy nebo pluginy sociálních sítí.

Česko evropskou směrnici implementovalo tak, že ji vlastně ignorovalo. Což se nelíbí Úřadu pro ochranu osobních údajů, který se tím bude zabývat. Zatím tedy u nás není potřeba uživatele žádat o souhlas, nicméně provozovatelé webů mají povinnost informovat o rozsahu a účelu jejich zpracování, například v podmínkách používání na svých stránkách. Uživatelé také musí mít možnost takové zpracování odmítnout.

A teď to podstatné: protože Google nerozlišuje, jak která země implementovala směrnici EU, jste v případě, že používáte jeho služby jako Google AdSense nebo Analytics s některou z inzertních funkcí (remarketing, demografické přehledy), povinni získat souhlas koncového uživatele. A to do 30. září 2015.

Jak na to?

S tím, jak žádost o souhlas formulovat, vám poradí web www.cookiechoices.org. Záleží především na tom, k čemu cookie používáte. Kupříkladu na tomto webu používám AdSense a Analytics, takže jsem použil tuto formulaci:

Tento web používá k poskytování služeb, personalizaci reklam a analýze návštěvnosti soubory cookie. Používáním tohoto webu s tím souhlasíte.

Řeším tím povinnost dát uživateli možnost používání cookie odmítnout (tím, že web opustí) a také odvolatelnost souhlasu (tím, že web opustí).

A teď čistě technicky. Souhlas s používáním cookies si budu ukládat do cookie nazvané např. eu-cookies. Panel s žádostí vložím do layoutu na konec stránky a zobrazím pouze pokud nebyl udělen. Příklad pro Latte:

<div class="eu-cookies" n:if="empty($_COOKIE[eu-cookies])">
	Tento web používá k poskytování služeb, personalizaci reklam a analýze
	návštěvnosti soubory cookie. Používáním tohoto webu s tím souhlasíte.
	<button>V pořádku</button>
	<a href="https://www.google.com/policies/technologies/cookies/">Další informace</a>
</div>
<noscript><style>.eu-cookies { display:none }</style></noscript>

Panel mám napozicovaný fixně, aby byl stále vidět. Sice tak ukrajuje kus prostoru zejména na mobilních zařízeních, ale to nevadí, uživatel stejně nemůže web používat, pokud neprojeví souhlas. Příklad stylu:

.eu-cookies {
	position: fixed;
	left: 0;
	top: 0;
	width: 100%;
	color: white;
	background-color: black;
	z-index: 1000;
}

.eu-cookies button {
	background: green;
	color: white;
}

A nakonec JavaScript, který po stisknutí tlačítka uloží souhlas do cookie (používám jQuery):

$('.eu-cookies button').click(function() {
	var date = new Date();
	date.setFullYear(date.getFullYear() + 10);
	document.cookie = 'eu-cookies=1; path=/; expires=' + date.toGMTString();
	$('.eu-cookies').hide();
});

Pokud používáte subdomény a cookie se má nastavit i pro ně, doplňte za path=/; ještě domain=.vasedomena.cz; (a tečka na začátku je důležitá).

Ještě poznámka: dokud souhlas nezískáte, neměla by vaše stránka obsahovat ani reklamu, ani měřící kódy.


Jak správně načítat webové fonty

Stránka by měla být čitelná ihned. Je velmi protivné, když si například v metru nemůžete přečíst článek jen kvůli tomu, že se nestihl načíst webový font.

Prohlížeče se totiž chovají tak, že text zobrazí až poté, co se font stáhne, aby zabránili tzv. Flash of Unstyled Text (FOUT), tedy nepříjemnému probliknutí jiného fontu. Problém je, že některé browsery nemají žádný timeout, po jehož uplynutí by se použil systémový font namísto žádného (tj. webového, ale nestaženého).

O tomhle tématu jsem psal už dříve a doporučoval pro mobilní zařízení webové fonty vůbec nepoužívat. Stejně krom autora grafiky to nikdy nepozná 🙂

Mobilům (nebo lépe řečeno zařízením do šířky 500px) můžeme ulevit tímto způsobem:

/* font stáhneme jen na větších zařízeních */
@import "https://fonts.googleapis.com/css?family=PT+Serif" screen and (min-width: 500px);

/* systémové písmo */
body {
	font: 18px/1.7 Georgia, serif;
}

@media (min-width: 500px) {
	body { /* webový font použijeme jen na větších zařízeních */
		font-family: 'PT Serif', Georgia, serif;
	}
}

Ale co ostatní prohlížeče? Natahování fontu skrze @import vypadá elegantně, ale blokuje zpracování CSS a to zase blokuje vykreslení stránky. Jde tedy o nejhorší možné řešení. Jak se zbavit blokování a zajistit timeout?

Emulace timeoutu

Timeout lze emulovat pomocí JavaScriptu. Jenže zjistit, že se font načetl, není nic triviálního, dělá se to pomocí triků, jako je třeba detekce změny šířky předpřipraveného text atd. Font Loading API zatím podporuje jen Chrome. Takže lepší bude použít hotové řešení, jako je například Web Font Loader.

Web Font Loader

Knihovnu Web Font Loader vyvíjí Google společně s Typekit. Nedávno o ní psal Aleš Roubíček, takže na něj navážu a pokusím se upravit řešení tak, aby se skript načítal asynchronně a neblokoval stránku.

Do hlavičky stránky (!) vložte tento kód, kterým asynchronně načtete Web Font Loader a také font:

<script>
	WebFontConfig = {
		google: { families: ['PT+Serif:400:latin,latin-ext'] }
	};
</script>
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js" async defer></script>

Web Font Loader umí detekovat stavy, kdy se font načítá a kdy už je načtený, a to pomocí událostí nebo nastavováním tříd elementu <html>. Jakmile je font načtený, nastaví třídu wf-active. Zároveň řeší timeout.

Upravíme styl tak, aby se webový font použil až ve chvíli, kdy bude načtený, a nahradil tak systémové písmo.

/* systémové písmo */
body {
	font: 18px/1.7 Georgia, serif;
}

/* písmo po načtení fontu */
html.wf-active body {
	font-family: 'PT Serif', Georgia, serif;
}

Tohle řešení má ale potíž. Bude docházet k FOUT, tedy k probliknutí systémového fontu. Při prvním načtení stránky, kdy webový font ještě není v cache prohlížeče, může probliknutí trvat sekundu či déle, při každém dalším zobrazení krátký okamžik. A to vypadá velmi ošklivě.

Proto během načítání písmo skryjeme, tak jak to dělají prohlížeče standardně, bez loaderu.

html.wf-loading * {
	color: transparent !important;
}

Dále třídu wf-loading nastavíme ihned, nelze čekat, až se Web Font Loader načte. Ale zároveň ji musíme odstranit, když načtení loaderu selže. Výsledný kód vypadá takto:

<script>
	WebFontConfig = {
		google: { families: ['PT+Serif:400:latin,latin-ext'] }
	};

	var el = document.documentElement;
	el.className += ' wf-loading';
	setTimeout(function() {
		el.className = el.className.replace(/(^|\s)wf-loading(\s|$)/g, ' ');
	}, 1000); // 1 second
</script>
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js" async defer></script>

Pokud je font v cache, stránka se ihned zobrazí se správným písmem bez probliknutí. Pokud v cache není, text nejprve nebude vidět a pokud se do vteřiny nepodaří font stáhnout, zobrazí se systémovým písmem a po stažení se přepne na webové písmo. Tedy při rychlém stažení k probliknutí vůbec nedojde a při pomalém ano, ale uživatel nebude koukat na prázdnou stránku.

Samozřejmě časovou prodlevu můžete dle libosti snížit.

Doplnění: s alternativním řešením přišel Petr Soukup.

Po načtení fontu si do prohlížeče uloží cookie wfont, která později bude značit, že font by měl být už v cache prohlížeče:

<html>
<script>
	WebFontConfig = {
		google: { families: ['PT+Serif:400:latin,latin-ext'] },
		active: function() { document.cookie ='wfont=1; expires='+(new Date(new Date().getTime() + 86400000)).toGMTString()+'; path=/' }
	};
</script>

A při dalším načtení stránky, pokud tato cookie existuje, generuje trošku jiný kód, kde není Web Font Loader, ale:

<html class="wf-active">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=PT+Serif">

Tedy webový font se použije rovnou a načítá se bez JavaScriptu, nedochází tedy k žádnému probliknutí.

A tip nakonec: aby prohlížeč mohl započít načítání fontu hodně brzy, vlastně ještě před normálním zpracováváním stránky, mu lze poradit pomocí prefetch:

<link rel="prefetch" href="https://fonts.googleapis.com/css?family=PT+Serif">

Implementace v Latte by mohla vypadat třeba takto:

{var $wfont = isset($_COOKIE[wfont])}
<html n:class="$wfont ? wf-active">
<script n:syntax=off n:if=!$wfont>
WebFontConfig = {
	google: { families: ['PT+Serif:400:latin,latin-ext'] },
	active: function() { document.cookie ='wfont=1; expires='+(new Date(new Date().getTime() + 86400000)).toGMTString()+'; path=/' }
};
</script>
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js" async defer n:if=!$wfont></script>
<link rel="{$wfont ? stylesheet : prefetch}" href="https://fonts.googleapis.com/css?family=PT+Serif">

Rychlejší stránky s Google Universal Analytics

Ukáži vám, jak zrychlit načítání Google Analytics nebo Web Loaderu a ještě zjednodušit měřící kód.

Tímto kódem vložíme do stránky měřící bod Google Analytics:

<script>
	(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
	(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
	m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
	})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

	ga('create', 'UA-XXXXX-XX', 'auto');
	ga('send', 'pageview');
</script>

Když si ho odtemníme, vypadá takto:

<script>
	(function() {
		window['GoogleAnalyticsObject'] = 'ga';
		window['ga'] = window['ga'] || function() {
			(window['ga'].q = window['ga'].q || []).push(arguments)
		}, window['ga'].l = 1 * new Date();
		var script = document.createElement('script'),
			firstScript = document.getElementsByTagName('script')[0];
		script.async = 1;
		script.src = '//www.google-analytics.com/analytics.js';
		firstScript.parentNode.insertBefore(script, firstScript)
	})();

	ga('create', 'UA-XXXXX-XX', 'auto');
	ga('send', 'pageview');
</script>

Řádek window['GoogleAnalyticsObject'] = 'ga' říká, že objekt Google Analytics bude uložen v globální proměnné ga, nicméně je zbytečný, protože ga je výchozí hodnota.

Dále následuje vytvoření objektu v proměnné ga, který představuje jen dočasný zásobník, kam se ukládá sekvence volání jednotlivých příkazů, a po načtení skriptu bude nahrazen skutečným objektem Google Analytics. Uvedený kód se dá výrazně zjednodušit vypuštěním window a pokud na stránce není víc měřících kódu, můžeme zrušit i podmínky ga = ga || ... a dostáváme se k jednoduchému:

	ga = function() { ga.q.push(arguments) };
	ga.q = [];
	ga.l = +new Date;

Dále následuje vytvoření elementu <script> a jeho vložení do stránky. Nastavení script.async = 1 je zbytečné, protože podle HTML specifikace je každý skriptem vkládaný <script> asynchronní.

Nicméně – neexistuje žádný důvod, proč element <script> vytvářet JavaScriptem. Je mnohem výhodnější použít normální HTML. Stránka se díky tomu načte rychleji, nedochází k blokování a může se využít preload scanner v moderních prohlížečích. Tím se dostáváme k této podobě:

<script>
	ga = function() { ga.q.push(arguments) };
	ga.q = [];
	ga.l = +new Date;
	ga('create', 'UA-XXXXX-XX','auto');
	ga('send','pageview');
</script>
<script src="//www.google-analytics.com/analytics.js" async></script>

Aby se kód načítal asynchronně i v prohlížečích IE 5.5 – 9, je nutné kromě atributu async přidat ještě defer.

V dnešní době se relativní URL //www.google-analytics.com dá považovat za antipattern, vhodnější je vždy používat https.

Finální podoba

Konečná podoba včetně minifikace vypadá takto:

<script>
	ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date;
	ga('create','UA-XXXXX-XX','auto');ga('send','pageview');
</script>
<script src="https://www.google-analytics.com/analytics.js" async defer></script>

Rychlejší, kratší a ještě navíc hezčí. Nechť slouží 🙂

Web Font Loader

Podobným způsobem je vhodné načítat i Web Font Loader, tj. místo v dokumentaci uvedeného

<script>
   WebFontConfig = {
	  typekit: { id: 'xxxxxx' }
   };

   (function(d) {
	  var wf = d.createElement('script'), s = d.scripts[0];
	  wf.src = 'https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js';
	  s.parentNode.insertBefore(wf, s);
   })(document);
</script>

používejte

<script>
   WebFontConfig = {
	  typekit: { id: 'xxxxxx' }
   };
</script>
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js" async defer></script>

Google Tag Manager

Protože adresa skriptu se v kódu Tag Manageru skládá, uvedenou optimalizaci nelze bohužel použít.


Nefunguje ti flexbox na iPadu, co?

Jak zprovoznit CSS Flexbox na iOS zařízeních aneb pár poznámek pro sebe, až zase příště budu zoufat, proč to neflexí, a tápat v paměti, jak jsem to minule vyřešil.

Prefixy

Safari stále ještě, i ve verzi 8.1, vyžaduje pro Flexible Box prefixy. Jsi opět překvapený, že? Takže tam hezky doplň display: -webkit-flex nebo -webkit-flex-wrap: wrap atd.

Pořadí

Záleží na pořadí deklarací! Tohle funguje:

	-webkit-flex-wrap: wrap;
	-webkit-justify-content: space-between;
	flex-wrap: wrap;
	justify-content: space-between;
	display: -webkit-flex;
	display: flex;

Zatímco tohle pořadí, které se ti líbí více, na iPhone a iPadu vůbec neflexí:

	display: -webkit-flex;
	-webkit-flex-wrap: wrap;
	-webkit-justify-content: space-between;

	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;

Jak udělat mezeru mezi prvky?

Řešíš, jak zajistit minimální mezeru mezi prvky v natahovacím kontejneru s justify-content: space-between? Vždycky nad tím dlouze dumáš, googlíš to a nikdy jsi nic nevygooglil.

Mezera má být natahovací, ale nesmí jít pod určité minimum. A krajní prvky musí přiléhat ke kraji.

Můžeš prvkům nastavit margin-right: x a kontejneru margin-right: -x, ale to trošku rozhodí layout a na mobilu půjde stránku horizontálně posouvat. Třeba ti někdo poradí něco lepšího… Řešením je obalit kontejner do prvku s overflow: hidden.


Na co tvůrci mobilních webů často zapomínají?

Několik tipů, jak vylepšit vzhled vašeho webu v mobilním telefonu.

Okraje

Zkontrolujte si, jak na mobilu vypadají okraje kolem textu. Velmi pravděpodobně je bude potřeba přizpůsobit. Buď budou moc široké a zbytečně tak ubírají drahocenný prostor, nebo nebudou žádné, což je při čtení značně iritující.

Vlastní písma

Obsah je daleko důležitější, než úžasný webový font, kterým je napsaný. To si uvědomíte zejména ve chvíli, když si nemůžete v metru přečíst článek jen proto, že se na zastávce nestihl načíst font (obrázek vlevo):

Obrázek uprostřed a vpravo se liší v použitém fontu: jeden z nich je nativní, druhý se natahuje z Google, což představuje řadu HTTP požadavků a přenesených dat navíc. Kromě autora grafiky stejně nikdo nepozná, který je který 🙂, tak mobilům klidně ulevte:

/* font stáhneme jen na větších zařízeních */
@import "http://fonts.googleapis.com/css?family=PT+Serif" screen and (min-width: 500px);

body {
	font: 18px/1.7 Georgia, serif;
}

@media (min-width: 500px) {
	body { /* a font použijeme jen na větších zařízeních */
		font-family: 'PT Serif', Georgia, serif;
	}
}

Velikost písma

Obvykle používám na webech o něco větší písmo, než je běžné, protože se mi parádně čte (třeba tento text má velikost 16px s řádkováním 1.65, podle mě minimum). A to nemám žádné dioptrie, jen je to příjemnější. Pro lidi s horším zrakem je větší font nutnost. A na mobilu, který držíme v ruce, často v třesoucím se dopravním prostředku nebo za chůze, je malé písmo důvod web vůbec nečíst.

Vyšší kontrast

Ironií je, že nejlepší displeje najdete v mobilech a tabletech, zatímco do notebooků se dávají šunty. Na druhou stranu, z mobilu daleko častěji čtete na přímém slunci, nebo si snižujete jas kvůli výdrži baterky, tudíž jemnou hru odstínů tolik neoceníte. Přidejte na kontrastu:

body {
	color: #555;
}

@media (max-width: 500px) {
	body {
		color: #111;
	}
}

Vysoké rozlišení

Displeje s vysokým rozlišením (retina) zkomplikovaly životy kodérům, do života vcházejí nové specifikace pro obrázkové elementy, grafiku je třeba exportovat v řadě různých rozlišení … houby!

Vůbec si nekomplikujte život. Stačí si jen zvyknout exportovat veškeré bitmapy ve dvojnásobném rozlišení (či vyšším) a změnu velikosti nechat na prohlížeči. Kvalitnější obrázky chceme stejně především kvůli mobilům. V případě fotografií v JPEG stačí snížit kvalitu, nárůst velikosti souboru bude minimální a okem nepoznáte rozdíl. Tedy na nízkém rozlišení, na vysokém bude mnohem prokreslenější.

Obrázek definovaný v CSS následně jen zmenšíte pomocí background-size (umí všechny prohlížeče krom IE8, takže pro něj budete zatím potřebovat i malý obrázek), obrázek v elementu <img> pomocí atributu width nebo CSS. Třeba tady na blogu všechny obrázky v článcích resizuju automaticky pomocí:

	article img {
		max-width: 100%;
		height: auto;
	}

Kde je to možné, použijte grafiku vektorovou. Jednobarevné ikony je nejlepší vyexportovat jako font, protože jen tak jim můžete v CSS měnit barvu. Můžete použít hotové sady nebo si vytvořit font na míru, šikovný je na to třeba Fontastic.

Bacha na jednu věc: pokud bude font umístěn na jiné (sub)doméně, musí jej server odesílat s HTTP hlavičkou Access-Control-Allow-Origin: *.

Formuláře

Na co rozhodně nikdy nezapomeňte: políčkům pro zadávání emailů nastavit <input type=email>.

V mobilním telefonu je mnohem lepší mít popisky nad prvky, aby při vyplňování bylo vidět, co vlastně zadáváte, a ne jen řadu inputů. Tohle umí šikovně řešit třeba Bootstrap v3, ale i mnoho jiných CSS frameworků.

A nakonec

Na mobilech nejvíc bolí navazování HTTP požadavků, takže spojujte styly a JavaScripty do jednoho souboru.


Psát isomorfní webové aplikace?

Isomorfní webové aplikace jsou takové, které sdílejí kód mezi serverovou a klientskou stranou. Jinými slovy, jsou obě strany psané v JavaScriptu. I když tak to vůbec nemuselo být, historie je zajímavější.

Úplně poprvé jsem se s touto koncepcí setkal před (fíha, to je neuvěřitelné) takřka 20 lety. Vlastně JavaScript přímo vznikl jako client-side i server-side jazyk, serverové prostředí se jmenovalo Netscape LiveWire a kód vypadal nějak takto. Šlo tedy o mix HTML a JavaScriptu, jen s tím rozdílem, že skript se vykonával na serveru. JavaScript byl zamýšlený jako jazyk pro amatérské programátory, jako konkurent tehdejšího PHP a Microsoftího ASP, zatímco pro profesionály tu byla client-side a server-side Java.

Nic nedopadlo podle očekávání. Kvůli soudním sporům Java z prohlížečů zmizela, ke konci se držela už jen v porno chatech a bankovnictví, a dnes je z ní jeden velký bezpečnostní kráter distribuovaný jako adware, který je nutno v prohlížečích vypínat. Neuspěl ani JavaScript na serveru, protože byl příliš nezralý a nevhodný na takové nasazení a serverové řešení upadajícího Netscape nezískalo popularitu.

Vývoj webů na mnoho let zbrzdilo šílenství okolo specifikací začínajících na X a monopol Internet Exploreru, ale pak došlo k jejich svržení a máme tu hromadu nových technologií. A s tím se pochopitelně vrací i otázka jednoho jazyka na obou stranách. Odstartoval to zejména výkonnostně nadupaný interpret JavaScriptu z Google Chrome a platforma Node.js.

Situace je dosti jiná, než před 20 lety:

  • server-side technologie ušly obrovský kus cesty a vyzrály
  • client-side prožívá pubertu
  • v průniku jazyků je pouze JavaScript

Tvorba webů pomocí serverových frameworků se stává komoditou, na řadu složitých otázek odpovídají zažité návrhové vzory. Na straně klienta to naopak bují, dnešní novinky nejspíš brzy nahradí novinky jiné, a to se ještě několikrát zopakuje. Tenhle stav je fajn, dohání se dlouhé zpoždění a máte šanci se zapojit a odvětvím pohnout.

Dohání také JavaScript, leč jeho skutečnou pozici nejlépe charakterizuje potřeba a popularita nejrůznějších nadstaveb, ať už jde o CoffeeScript, Google Closure Compiler nebo TypeScript. Pomocí nich už dnes lze z JavaScriptu udělat něco celkem robustního, což ale ve skutečnosti stále není. Přičemž jazyky s ambicí jej nahradit existují.

Osobně mi cesta k izomorfním aplikacím připadá přirozená a správná. U klientského skriptování jsem začínal a stále hledal různé spojnice, například Nette má dosud poměrně ojedinělou vlastnost, že pravidla pro validaci formulářů zapsaná na straně serveru vám automaticky překlopí na stranu prohlížeče. Isomorfní validace formulářů od roku 2008.

Ale v žádném případě bych si isomorfně nenechal naprogramovat třeba e-shop. Zatím.

Příliš mladé prostředí znamená absenci zažitých návrhových vzorů a různá rizika. Když si Dan Steigerwald, který pro mě částečně pochopitelně odmítá jakékoliv problémy této technologie připouštět, si tuhle posteskl, že čeští vývojáři jsou pozadu za frikulíny ze San Francisca a stále se drží serverových technologií, rozjela se diskuse o výhodách a nevýhodách jednotlivých přístupů a Dan jako odpověď na jednu námitku poslal příklad webu (tuším jeho kolegů) iodine.com psaný v React.js. Čímž poskytl pěkný příklad neduhů SPA/isomorfních aplikací:

  • na webu nefunguje správně tlačítko zpět
  • na mnoha různých URL se nachází identický obsah
  • jeho výroba byla násobně dražší

Zdůrazňuji, že z jeho stany nešlo o ukázkový příklad, nicméně tím lépe demonstruje hlavní problém SPA/isomorfních aplikací: udělat je dobře je stále velmi těžké a potažmo drahé. Přičemž tentýž web za použití server-side frameworku, jako je například Nette, zvládne napsat i průměrný a levný programátor. A podobných hrubek se přitom nedopustí.

Izomorfním aplikacím se nevyhýbejte, zkoušejte si novinky, zavčasu odhalujte slepé cesty, rozšiřujte si obzory. Ale s ostrým nasazením se držte jen u typů aplikací, kde je to skutečně nutné a výhodné. Není jich zase tolik.

Navíc nemáte v žádné žhavé technologii jistotu. Tvrdit opak, třeba proto, že za nějakou z nich stojí obří firma, znamená být slepý k historii posledních 20 let.


Kdy používat preprocesory a kdy ne?

Složitá otázka, těžké kodérovo dilema, které se pokusím rozseknout: používejte preprocesory vždy!

Jak se Internet přesunul do mobilů, staly se největší zabijáci rychlosti načítání stránek tyhlety úhledné sloupečky:

<link rel="stylesheet" href="/css/reset.css">
<link rel="stylesheet" href="/css/content.css">
<link rel="stylesheet" href="/css/layout.css">
<link rel="stylesheet" href="/css/screen.css" media=screen>
<link rel="stylesheet" href="/css/fancybox/jquery.fancybox.css" media=screen>
<link rel="stylesheet" href="/css/print.css" media=print>
<script src="/js/jquery.js"></script>
<script src="/js/jquery.autocomplete.js"></script>
<script src="/js/fancybox/jquery.fancybox.js"></script>
<script src="/js/tooltips/jquery.tooltips.js"></script>
<script src="/js/jquery.scrollspy.js"></script>
<script src="/js/main.js"></script>

To nechceš! Chceš tohle:

<head><link rel="stylesheet" href="/assets/combined.css"></head>

<body>
...
<script src="/assets/combined.js"></script>
</body>

Taky chceš, aby se ti všechny soubory posílaly zazipované a vypadaly hutně: combined.css a hustě: combined.js.

Byly doby, kdy se komprimované skripty psaly ručně. Vynechávaly se komentáře, v JavaScriptu používaly jednopísmenné proměnné atd.

Nedělejte to. Vaším úkolem je psát čitelný kód a komprimování JavaScriptu nechte na bedrech Google Closure Compiler, který to dokáže řádově lépe než vy a navíc upozorní na chyby, o minifikaci stylů se postará například Less.

Ten dokonce zvládá vychytávku, kdy u všech miniobrázků můžete nahradit:

textarea {
	background: #fff url("img/input.gif") repeat-x;
}

za

textarea {
	background: #fff data-uri("img/input.gif") repeat-x;
}

a preprocesor je přímo vloží do combined.css, čímž odpadne hromada HTTP requestů na 100bajtové drobky.

Preprocesory prostě a jednoduše chcete!


SASS, LESS, Stylus nebo čisté CSS? (3)

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.

…pokračování


SASS, LESS, Stylus nebo čisté CSS? (2)

Vyplatí se používat CSS preprocesory? A pokud ano, který zvolit? Pokračuji v rozboru tří nejznámějších konkurentů.

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. Mají řešit praktické nedostatky samotného CSS.

předchozí části jsme si ukázali, jak preprocesory nainstalovat. Dnes se jim podívám na zoubek.

Syntaxe

Ač jednotlivé nástroje používají odlišnou syntax, všechny rozumí klasickému CSS. To je nesmírně důležité! Můžete kopírovat existující CSS fragmenty a budou fungovat. Ačkoliv…

SASS používá dvě různé syntaxe, jedna se nazývá SASS (stejně jako preprocesor), nepoužívá středníky ani složené závorky {} a místo toho odsazuje mezerami či tabulátory:

// SASS
#main
	color: blue
	font-size: 0.3em

Druhou syntaxí je SCSS, která vypadá jako klasické CSS a měla být i plně kompatibilní (budu ji pro SASS používat v dalších příkladech). Stejnou syntax má i LESS:

// SCSS, LESS a CSS
#main {
	color: blue;
	font-size: 0.3em;
}

Stylus také rozumí CSS syntaxi, nicméně upozorňuje, že nejde o 100% kompatibilitu, především proto, že hodně bazíruje na odsazování. V jeho syntaxi lze vynechat složené závorky, středníky a dokonce i dvojtečky:

// Stylus
#main
	color blue
	font-size 0.3em

Ale pozor! Důležité je, že tyto znaky můžeme vynechat. Pokud vám zápis připadá příliš hutný, klidně si dvojtečky doplňte. Nechcete odsazovat? Vražte tam složené závorky. Velmi podobně funguje NEON. V tomto se zásadně liší od úsporné SASS syntaxe, kde vynechání závorek a středníků je povinnost. Což ji činí z praktického hlediska nepoužitelnou a chápu, proč tvůrci o kompatibilitu s CSS usilovali, ale udělali to nešťastně zavedením další, nekompatibilní, ukecané syntaxe SCSS.

Když už se totiž rozhodnu preprocesor používat, ocením, když mi zjednoduší i syntaxi (alespoň nepovinné středníky), což bohužel umí jen Stylus. Jeho syntax je nejpružnější a nejúspornější.

V kontrastu přísnosti LESS a SASS je zajímavé, že všechny tři preprocesory podporují // řádkové komentáře.

Když udělám chybu

Pokud uděláte v dokumentu chibu, preprocesory vás na ni upozorní chybovou hláškou. A v tomto směru se jednotlivé nástroje zásadně liší. Pokud třeba zapomenu ve výše uvedeném kódu uzavírací }, oznámí SASS:

Syntax error: Invalid CSS after "font-size: 0.3em;": expected "}", was ""
		on line 4 of test.scss

Stylus:

if (err) throw err;
					 ^
ParseError: stdin:4
   1| #main {
   2|   color: blue;
   3|   font-size: 0.3em;
 > 4|

unexpected "eos"

a LESS:

ParseError: missing closing `}` in test.less:9:17
8
9 unexpected "eos"undefined

Nutno dodat, že LESS měl šťastnější chvilku. Pokud bych zapomněl středník, skončí to u obvyklejšího

ParseError: Syntax Error on line 2 in test.less:2:1
1 #main {
2	   color: blue
3	   font-size: 0.3em

Výmluvné chybové hlášky jsou pro mě důležité a v tomto směru s přehledem vede SASS. Ačkoliv se to z ukázky tak nejeví, hodně se snaží i Stylus. Běžně však špatně spočítá číslo řádku a hlásí chybu o pár řádků výš, než skutečně je. LESS se nejčastěji zmůže na nicneříkající Syntax Error on line XY.

Občas jsem narážel na chyby v samotném preprocesoru. Pokud se mi je podařilo izolovat, snažil jsem se je nahlásit (LESS, Stylus), ale ne vždy to šlo. Zde se nejvíc projevila vyspělost jednotlivých nástrojů. Zatímco nejstarší SASS se mi z rovnováhy nepodařilo vyvést nikdy, druhé dva nástroje obsahují chyb bohužel požehnaně.

Co všechno umí?

Drtivou většinou užitečných vychytávek najdeme u všech tří nástrojů, byť třeba nemusí být uvedeny v dokumentaci. V množství asi vede SASS, ale takto se to hodnotit nedá. Třeba nested properties nepotřebuji, naopak property lookup a generátor data URI ve Stylusu vypadá užitečně. Tedy do chvíle, než zjistíte, že převádí buď všechny obrázky, nebo žádný.

Nechci sepisovat výčet jednotlivých vlastností ani srovnávat jejich zápis, to najdete v dokumentaci nebo ve srovnávacím článku na NetTuts. Zaměřím se místo toho na fundamentální rozdíly a největší z nich se týkají tzv. mixinů.

Mixiny

Mixiny jsou z mého pohledu nejdůležitější vychytávkou preprocesorů, protože mění způsob, jak píšeme HTML kód. Vysvětlím. Asi nejznámější „mixin“ je tzv. clearfix, která se stará o to, aby z bloku nevytékaly plovoucí prvky. Obvykle vypadá nějak takto:

.clearfix:after {
	clear: both;
	content: ".";
	display: block;
	height: 0;
	visibility: hidden;
	font-size: 0;
}

(Poznámka: existují jednodušší i složitější varianty clearfixu. Kupodivu mně vždycky fungovalo nastavit bloku overflow: auto a clearfix jsem nepotřeboval. Ale v tuto chvíli jde jen o příklad.).

Clearfix pak aktivujeme v HTML:

<div id="content" class="clearfix">
	<div style="float: left;">....</div>
	...
</div>

Ale to neděláš dobře, Jaromíre. Clearfix by se měl aplikovat na straně stylů, přímo pro #content, nikoliv v HTML. Jenže komu by se chtělo kazit si krásný stylopis kopírováním ošklivého hacku? Mnoho kodérů raději sáhne do HTML.

S preprocesory však netřeba nic kopírovat a vystačíme si s jedinou instrukcí. Nejprve si clearfix uložíme jako tzv. mixin a poté ho v definici #content zavoláme:

SASS:

// definice mixinu (zkráceno)
@mixin clearfix {
	&:after { clear: both; ... }
}

#content {
	@include clearfix; // vložení mixinu
}

LESS:

.clearfix() {
	&:after { clear: both; ... }
}

#content {
	.clearfix;
}

Stylus:

clearfix()
	&:after { clear: both; ... }

#content
	clearfix()

Zmizí tak nutkání zasahovat do zdrojového kódu. A to je velmi důležité.

Jak vidno, syntaxe SASS je opět nejukecanější. Za zmínku stojí, že LESS umožňuje volat jako mixin i jakoukoliv třídu nebo ID.

Parametrické mixiny

Ještě zajímavější je to ve chvíli, kdy mixinům předáme parametry:

SASS:

@mixin border-radius($arg) {
	-webkit-border-radius: $arg;
	-moz-border-radius: $arg;
	border-radius: $arg;
}

#content {
	@include border-radius(2px);
}

LESS:

.border-radius(@arg) {
	-webkit-border-radius: @arg;
	-moz-border-radius: @arg;
	border-radius: @arg;
}

#content {
	.border-radius(2px);
}

Stylus:

border-radius(arg)
	-webkit-border-radius: arg
	-moz-border-radius: arg
	border-radius: arg


#content
	border-radius(2px)

Ve Stylusu můžeme použít i zapis s dvojtečkou:

#content
	border-radius: 2px

Což je velmi zajímavé! Když totiž později vendor prefix odstraníme, nebo když naopak zjistíme, že určité vlastnosti potřebujeme vendorovou variantu dodat, nemusíme změnit ani čárku v samotném stylu. Wow.

Jenže… co když mixin zavoláme s více parametry? Například uvedeme border-radius: 2px 3px. Překvapivě Stylus bude 3px v tichosti ignorovat. Proč tomu tak je?

V CSS existují dva způsoby, jak uvést více argumentů, lišící se v oddělovači:

  • oddělené mezerama: border: 1px solid black
  • oddělené čárkama: rgb(10, 20, 30)

Totéž se rozlišuje i v preprocesorech. Ve Stylusu platí, že volání mixinu border-radius: 2px 3px je ekvivalentní k border-radius(2px, 3px) (a dává mi to smysl), obojí představuje předávní dvou argumentů. Naopak border-radius(2px 3px) bez čárky předává argument jeden. Náš mixin očekává pouze jeden parametr a druhý tiše bez jakéhokoliv varování zahodil. Oprava spočívá v použití užitečného klíčového slova arguments namísto předaného argumentu arg.

SASS a LESS naopak předání více argumentů, než je mixinem očekáváno, vnímají jako fatální chybu. V případě SASS to v důsledku vede k podivným blokům $shadow-1, …, $shadow-10 patrným v galerii Compass, zatímco LESS se s problémem snaží vypořádat konstrukcí when(). Nic z toho mi nepřipadá ideální.

Pokračování zase příště.


phpFashion © 2004, 2025 David Grudl | o blogu

Ukázky zdrojových kódů smíte používat s uvedením autora a URL tohoto webu bez dalších omezení.