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">