Optimalizace webových fontů 2026: preload, subset a variable fonts

Kompletní průvodce optimalizací webových fontů v roce 2026. Self-hosting WOFF2, preload, subsetting přes unicode-range, variable fonts a size-adjust pro nulový CLS, včetně praktických ukázek kódu z reálných auditů.

Optimalizace fontů 2026: preload, subset, WOFF2

Aktualizováno: 16. července 2026

Optimalizace webových fontů znamená doručit správnou variantu písma co nejmenším přenosem, co nejdříve v načítání stránky, a bez viditelných skoků layoutu při jeho výměně. V praxi to dnes v roce 2026 znamená kombinaci self-hostingu WOFF2 souborů, cílené direktivy preload, agresivního subsettingu přes unicode-range, přepnutí na variable fonts tam, kde používáte více řezů, a doladění fallback metrik pomocí size-adjust, aby se z fontu nestal hlavní viník CLS. Tenhle průvodce vás těmito kroky provede s konkrétními ukázkami kódu (a několika poznámkami z auditů, které jsem letos dělal).

  • Self-hostujte WOFF2 přes stejný origin. Od zavedení cache partitioningu v Chrome 86 už sdílený Google Fonts CDN nešetří ani jeden request navíc.
  • Preloadujte pouze latinské subsety kritických řezů (typicky regular a bold pro nadpisy), ne celou rodinu. Jinak si zablokujete šířku pásma pro LCP obrázek.
  • Variable font s osami wght a ital nahradí 4 až 8 samostatných souborů a bývá při gzipu o 30 až 60 % menší než součet statických řezů.
  • font-display: swap odstraní FOIT, ale bez size-adjust na fallbacku vygeneruje CLS 0,05 až 0,15 v momentě záměny.
  • Podle HTTP Archive Web Almanac 2025 stále 62 % stránek načítá 3+ font souborů, i když skutečně používá jen 1 až 2 řezy. Subsetting je největší nesebraná úspora.
  • Nepreloadujte fonty pro banner cookies nebo third-party widgety. Plýtvá to kritickým request budgetem první obrazovky.

Proč je WOFF2 v roce 2026 stále jediný správný formát

WOFF2 přišel jako doporučení W3C v roce 2018 a od té doby si drží pozici jediného formátu, který má smysl na produkci doručovat. Podpora prohlížečů je dnes plných 98,5 % podle Can I Use, včetně všech verzí Safari od 12, Chrome od 36 a Firefoxu od 39. Starší formáty (TTF, EOT, WOFF v1) mají v roce 2026 jediné využití: jako debug artefakt při konverzi. Rozhodně ne jako fallback v @font-face deklaraci, kde by jen zvětšovaly HTML a matly CDN cache.

WOFF2 používá Brotli kompresi optimalizovanou pro glyphová data, takže výstupní soubor je typicky o 25 až 35 % menší než ekvivalentní WOFF. Pro velkou latinskou rodinu jako Inter to znamená rozdíl mezi ~140 kB a ~200 kB na jeden řez. Pokud v produkci vidíte i .woff nebo .ttf verze, projděte @font-face bloky a odstraňte je. Moderní build nástroje (Vite, Next.js, Astro) je defaultně negenerují, ale legacy konfigurace ano. Sám jsem to nedávno vídal na projektu, kde byl webpack config zděděný ze staré aplikace ještě z roku 2019.

/* Špatně: fallbacky formátů, které nikdo nevyužije */
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter.woff2") format("woff2"),
       url("/fonts/inter.woff") format("woff"),
       url("/fonts/inter.ttf") format("truetype");
}

/* Správně v roce 2026: jen WOFF2 */
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0100-024F;
}

Self-hosting versus Google Fonts po cache partitioningu

Historicky se opakoval argument, že Google Fonts CDN je rychlejší, protože font uživatel už má stažený z jiné návštěvy. Ten argument v roce 2026 neplatí. Chrome od verze 86 (říjen 2020) implementoval cache partitioning podle top-level originu, Firefox následoval s Total Cache Protection v roce 2021 a Safari izoluje cache podle first-party od verze 13. Efektivně to znamená, že každá doména si stahuje font znovu, i když je URL identická. Sdílený CDN pro fonty už žádnou výhodu neposkytuje.

Self-hosting přes vlastní doménu naopak přináší tři konkrétní zisky. Zaprvé eliminujete DNS lookup a TLS handshake na fonts.gstatic.com (typicky 100 až 300 ms na 4G). Zadruhé font sdílí HTTP/2 nebo HTTP/3 spojení s vaším CSS, což šetří další round trip. A zatřetí získáváte plnou kontrolu nad Cache-Control hlavičkou. Doporučené hodnoty jsou public, max-age=31536000, immutable. Font soubor totiž mění hash při jakékoliv úpravě, takže je bezpečně věčný v cache. Pokud si potřebujete rozšířit i strategii TTFB kolem těch samých hlaviček, mrkněte na techniky pro snížení TTFB pod 200 ms.

Jak správně preloadovat font

Preload direktiva říká prohlížeči, že má stáhnout zdroj s vysokou prioritou, aniž by musel čekat na jeho objevení v CSSOM. Pro fonty je to zásadní. Fonty jsou totiž standardně objeveny až poté, co prohlížeč rozparsuje CSS, sestaví CSSOM, spočítá matched selektory a zjistí, že daný glyph potřebuje konkrétní font. To bývá 500 až 1500 ms po HTML parsu, přesně v okně, kdy se počítá LCP.

<!-- V <head>, hned po viewport meta -->
<link
  rel="preload"
  href="/fonts/inter-latin-regular.woff2"
  as="font"
  type="font/woff2"
  crossorigin
>
<link
  rel="preload"
  href="/fonts/inter-latin-bold.woff2"
  as="font"
  type="font/woff2"
  crossorigin
>

Atribut crossorigin je povinný i pro same-origin fonty. Bez něj prohlížeč zdroj stáhne, ale nepoužije z cache a stáhne ho podruhé při skutečné aplikaci @font-face. Ten samý footgun vídám v audit reportech snad každý týden, obvykle na projektech, které si zdvojnásobily font traffic a vůbec si toho nikdo nevšiml.

Které řezy preloadovat

Preloadujte pouze fonty, které se skutečně objeví na první obrazovce (above the fold). Typicky to jsou dva soubory: regular pro body text a bold pro nadpisy. Pokud preloadujete pět řezů, každý z nich soutěží o šířku pásma s LCP obrázkem, hero CSS a critical JavaScriptem. A Lighthouse to zaznamená jako varování „Preload key requests are competing for bandwidth".

Fetchpriority pro fonty

Od Chrome 101 lze na preload nastavit fetchpriority="high", ale u fontů je to obvykle zbytečné. Preload sám o sobě povyšuje prioritu na Highest. Kombinace preload a fetchpriority="high" má smysl spíš u LCP obrázku, jak popisuji v průvodci optimalizací LCP skóre.

Subsetting a unicode-range: menší soubory bez ztráty pokrytí

Standardní distribuce populárního fontu jako Inter nebo Roboto obsahuje glyfy pro latinku, cyrilici, řečtinu, vietnamštinu a rozšířené latinské znaky, celkem přes 2000 glyfů a přibližně 250 až 400 kB per řez. Česká stránka typicky potřebuje latin plus latin-ext (pro diakritiku á, č, ě, š, ž a další). Ostatní subsety jsou plýtvání.

Řešení má dvě vrstvy. Za prvé, samotný WOFF2 soubor vysubsetujte pomocí pyftsubset ze fontTools. Za druhé, v CSS deklarujte unicode-range, aby prohlížeč fyzicky nestahoval subset, dokud na stránce nenajde znak z daného rozsahu.

# Vytvoří latin subset (0000-00FF) + latin-ext (0100-024F)
pyftsubset Inter-Regular.ttf \
  --unicodes="U+0000-00FF,U+0100-024F,U+2000-206F,U+2074,U+20AC,U+2122" \
  --layout-features="kern,liga,calt" \
  --flavor=woff2 \
  --output-file=inter-latin-regular.woff2
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-latin-regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC,
                 U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074,
                 U+20AC, U+2122, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-latin-ext-regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

V praxi na české stránce prohlížeč stáhne jen latin subset (~35 kB), a latin-ext (~15 kB) doplní jen tehdy, když se ve viewport dostane znak s diakritikou. Součet je pod 50 kB per řez oproti původním 250 kB. Když jsem tohle poprvé nasadil na jednom e-shopu, průměrný page weight klesl o 380 kB na první návštěvě. A to bez jediné vizuální změny.

Variable fonts: kdy dávají smysl a kdy ne

Variable font je jeden soubor obsahující celý rozsah os písma, nejčastěji wght (100 až 900), ital (0/1) a někdy opsz (optical size). Místo pěti WOFF2 souborů pro regular, medium, semibold, bold a black stáhnete jediný soubor a v CSS měníte hodnotu font-weight jako spojité číslo.

Kdy jsou úspornější? Pokud stránka reálně používá 3+ řezy stejné rodiny. Inter variable v latin subsetu má ~110 kB, což je méně než součet čtyř statických řezů (4 × 35 kB = 140 kB). Pokud máte design system s wght 400/500/600/700/800, úspora je 30 až 50 %. Bonus: designéři můžou používat mezihodnoty (weight 550) pro jemné doladění.

Kdy naopak ne: pokud používáte jen dva řezy (400 a 700), variable font bývá o 20 až 40 % větší než součet dvou statických, protože si nesete interpolační data pro nikdy nepoužívané hmotnosti. Také mějte na paměti, že cachování je all-or-nothing. Jeden variable soubor je jeden HTTP request, ale musí být kompletní, než se cokoliv začne renderovat s vlastním fontem.

@font-face {
  font-family: "Inter var";
  src: url("/fonts/inter-var-latin.woff2") format("woff2-variations");
  font-weight: 100 900;   /* rozsah, ne konkrétní hodnota */
  font-style: normal;
  font-display: swap;
}

h1 { font-weight: 750; }  /* mezihodnota mezi bold a black */
body { font-weight: 420; }  /* jemně silnější než default 400 */

font-display, FOIT a FOUT: kterou strategii zvolit

Vlastnost font-display řídí, co prohlížeč vykreslí v době, kdy vlastní font ještě není stažen. Bez ní nastane FOIT (Flash of Invisible Text), tedy text je 3 sekundy neviditelný, což typicky zabíjí LCP metriku, protože LCP element často obsahuje textový uzel.

HodnotaBlock periodSwap periodKdy použít
auto3 s0 sNikdy. Chová se jako block.
block3 sNekonečněIkony (např. Font Awesome), kde je text bez glyfu horší než neviditelný.
swap0 sNekonečněDefault volba pro body a nadpisy: okamžitý fallback, žádný FOIT.
fallback100 ms3 sKdyž chcete minimalizovat FOUT u pomalých spojení a přijmete, že po 3 s se už font nezobrazí.
optional100 ms0 sNejlepší pro perf. Pokud font není v cache do 100 ms, nepoužije se vůbec (do dalšího page loadu).

Pro většinu projektů je správnou volbou swap na hlavních textových fontech a optional na dekorativních. Kombinace optional a preload je hodně silná: preload stáhne font s vysokou prioritou, a když to stihne do 100 ms, uživatel žádný FOUT nevidí. Když ne, stránka běží ve fallback fontu bez skoku a font se použije při další návštěvě z cache.

Jak omezit CLS způsobený fonty pomocí size-adjust

Ve chvíli, kdy prohlížeč vymění fallback font za vlastní webový font, změní se výška řádku a šířka glyfů. Text zabere jiný počet řádků, a to způsobí přesun všeho pod ním. Klasický layout shift. Bez ošetření to bývá 0,05 až 0,15 CLS, což vás sráží pod „Good" hranici (0,10). Podrobnější přehled technik proti CLS mám v článku jak opravit CLS a stabilizovat layout.

Řešení jsou dnes tři deskriptory v @font-face: size-adjust, ascent-override a descent-override. Nastavíte je na fallback font (např. Arial nebo system-ui) tak, aby vertikální metriky přesně odpovídaly vlastnímu fontu.

@font-face {
  font-family: "Inter fallback";
  src: local("Arial");
  size-adjust: 107%;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

body {
  font-family: "Inter", "Inter fallback", sans-serif;
}

Hodnoty odvodíte buď ručně (změřením x-height obou fontů), nebo automaticky přes nástroj Capsize od Seek, který vygeneruje CSS deskriptory pro libovolný font. Next.js 13+ a Nuxt 3 aplikují tuhle techniku automaticky přes next/font respektive @nuxt/fonts. Pokud používáte tyhle frameworky, dostáváte fix zdarma.

Audit fontů v Lighthouse a WebPageTest

Lighthouse 12 (aktuální v Chrome 125+) hlásí problémy s fonty ve třech auditech: „Ensure text remains visible during webfont load" (chybějící font-display), „Avoid multiple page redirects" (typicky Google Fonts přes 302) a „Preload key requests". Skóre Performance ale nezachytí subsetting ani CLS z fontů dobře. Tam pomůže WebPageTest s waterfall pohledem a Real User Monitoring přes web-vitals knihovnu.

Konkrétní kontrolní seznam, který sám používám při auditech:

  1. V DevTools → Network filtrujte na „Font". Kolik souborů? Kolik jich má stejný Cache-Control: immutable? Kolik jich přichází z třetího originu?
  2. V Coverage tabu spusťte reload a zjistěte, jaké procento CSS deklaruje fonty, které se ani nezobrazily.
  3. Zkontrolujte, že preloadované fonty se skutečně použijí do 3 sekund. Preload nepoužitého zdroje je warning v console.
  4. Změřte CLS s a bez fontového fallbacku (v DevTools můžete font dočasně zablokovat přes Network → Block request URL).
  5. Ověřte, že font-display: swap je na všech user-facing textových fontech, ne jen na některých.

Často kladené otázky

Je lepší self-hosting fontů, nebo Google Fonts?

V roce 2026 je self-hosting téměř vždy rychlejší. Po zavedení cache partitioningu v Chrome 86 se font stáhne pro každou doménu zvlášť, takže sdílený CDN neposkytuje žádnou úsporu a přidává navíc DNS lookup a TLS handshake na cizí origin. Self-hosting typicky sníží FCP o 150 až 400 ms na mobilním připojení.

Proč musí mít preload fontu atribut crossorigin?

Bez atributu crossorigin prohlížeč načte font v jiném request módu, než ho pak vyžádá deklarace @font-face, a soubor se stáhne dvakrát. Atribut je povinný i pro same-origin fonty, jde totiž o technický požadavek CORS specifikace, ne o bezpečnostní kontrolu.

Jaký je rozdíl mezi WOFF a WOFF2?

WOFF2 používá Brotli kompresi optimalizovanou pro glyphová data a je typicky o 25 až 35 % menší než ekvivalentní WOFF (v1), který používal zlib. Podpora prohlížečů je pro WOFF2 v roce 2026 nad 98 %, takže neexistuje důvod dodávat WOFF v1 jako fallback.

Jsou variable fonts vždy rychlejší?

Ne. Variable font se vyplatí, když stránka reálně používá tři a více řezů stejné rodiny. Pak nahradí několik statických souborů a šetří 30 až 50 %. Pokud používáte jen regular a bold, dva samostatné statické WOFF2 soubory bývají o 20 až 40 % menší než jeden variable soubor.

Jak omezit CLS způsobený načítáním webového fontu?

Definujte v @font-face fallback font pomocí deskriptorů size-adjust, ascent-override a descent-override tak, aby jeho metriky odpovídaly cílovému fontu. Nástroj Capsize umí tyhle hodnoty spočítat automaticky. Ve výsledku výměna fontu nezmění výšku řádků a CLS zůstane nulové.

Které fonty mám preloadovat?

Pouze ty, které se objevují nad ohybem stránky, typicky regular pro body text a bold pro nadpisy, tedy dva soubory. Preload každého dalšího řezu soutěží o šířku pásma s LCP obrázkem a critical CSS, takže může výkon spíš zhoršit než zlepšit.

O Autorovi Yuki Tanabe

Yuki is a senior web performance consultant based in Berlin with 13 years in the trade. She did five years at Akamai as a solutions architect for European media customers, tuning CDN configs and edge cache rules for newspapers handling 8-figure daily pageviews, then moved to Zalando as a staff engineer on the Mobile Web Performance squad. There she ran the project that cut JavaScript shipped to product detail pages by 41% without losing any tracking signals the marketing org cared about. She holds the Google Mobile Web Specialist cert (back when it existed) and contributes occasionally to the web-vitals npm package. Her writing tends to focus on perf budgets that actually survive contact with a product roadmap, image pipeline strategy beyond just slapping on AVIF, and the unglamorous work of getting RUM into a CI pipeline that PMs will read.