Optimizacija web fontova u 2026: font-display, subsetiranje i variabilni fontovi za brži LCP

WOFF2, font-display: optional, preload s fetchpriority i size-adjust na fallbacku su četiri poteza koji spuštaju LCP za stotine milisekundi i CLS na nulu. Kompletan vodič s primjerima koda, benchmarcima i checklistom za 2026.

Optimizacija web fontova 2026: WOFF2 vodič

Ažurirano: 14. srpnja 2026.

Optimizacija web fontova u 2026. znači tri stvari: serviranje isključivo WOFF2 formata, obavezna deklaracija font-display: swap ili optional, i podešavanje size-adjust na fallback fontu kako bi se CLS zadržao ispod 0.1. Sve ostalo (preload, subsetiranje, variabilni fontovi, self-hosting) su optimizacije koje slažete povrh te osnove. U produkciji, na temelju CrUX podataka koje pratim već tri godine, fontovi su treći najveći uzrok sporog LCP-a (poslije slika i JavaScripta) i drugi najveći uzrok Cumulative Layout Shifta. Ovaj vodič je namijenjen da to popravite mjerljivo, ne teoretski.

  • WOFF2 je jedini format koji trebate isporučivati u 2026; podržan je u 97% preglednika prema Baseline podacima.
  • font-display: swap eliminira FOIT (Flash of Invisible Text), ali može uzrokovati CLS ako fallback font ima drugačije metrike od web fonta.
  • Kombinacija size-adjust, ascent-override i descent-override na @font-face fallback definiciji svodi CLS zbog fontova na nulu.
  • Preload primarnog font fajla s rel="preload" i fetchpriority="high" smanjuje LCP za 200 do 600 ms na 4G vezama.
  • Variabilni fontovi zamjenjuju 4 do 8 statičkih fajlova jednim, tipično 30 do 40% manjim od zbroja koji zamjenjuju.
  • Self-hosting fontova s Google Fonts CDN-a je danas brže u većini slučajeva zbog partitioniranja HTTP cachea preglednika od 2020. na dalje.

Zašto su fontovi kritični za Core Web Vitals

Kad promatram RUM podatke sa stotinjak stranica koje pratim, obrazac se ponavlja. Font zahtjev je često najkasniji resurs na kritičnom putu prije nego što tekst postane vidljiv. To znači da izravno utječe na LCP kada je LCP element blok teksta (naslov, glavni članak), i utječe na CLS kada se fallback font zamijeni s web fontom koji ima drugačiju širinu znakova.

Chrome UX Report (CrUX) za 2026. pokazuje da 34% mobilnih stranica koje ne prolaze CLS test ima najveći doprinos layout shifta iz font swap događaja. To je više nego od slika bez width/height atributa, koje su tradicionalno bile glavni krivac. Razlog je jednostavan: otkad su developeri masovno prihvatili aspect-ratio CSS i eksplicitne dimenzije slika (nešto što sam detaljno pokrila u vodiču za CLS optimizaciju u 2026.), fontovi su ostali kao neriješeni preostali uzrok.

Font zahtjev ima još jednu neugodnu karakteristiku. Pokrene se tek nakon što preglednik parsira CSS i utvrdi da @font-face deklariran font zaista treba za neki DOM element. To je serijski lanac: HTML, pa CSS, pa font. Ako ne intervenirate resource hintovima, taj lanac troši 400 do 800 ms na prosječnoj 4G vezi.

Koji je najbolji format za web fontove u 2026

Kratak odgovor: samo WOFF2. Nema više src liste s TTF, EOT ili WOFF fallbackovima. Prema Can I Use podacima, WOFF2 podržava 97.4% globalnih preglednika, uključujući svaku verziju Chrome-a od 36, Firefox-a od 39, Safari-ja od 12, i Edge-a od 14. Preglednici koji ga ne podržavaju (stari IE, Opera Mini u ekstremnom štednom režimu) svakako neće moći renderirati vašu modernu web stranicu.

WOFF2 koristi Brotli kompresiju specifično prilagođenu za font podatke, i tipično je 30% manji od WOFF-a i 40 do 50% manji od originalnog TTF-a. Za varijabilni Inter font od 340 KB u TTF-u, WOFF2 verzija je 178 KB, a subsetirana latinska verzija svega 42 KB. Iskreno, kad sam prvi put isporučila projekt s pravilno subsetiranim WOFF2 fontom, LCP je pao za 380 ms u p75 vrijednosti, i to bez ijedne druge izmjene.

/* NE ovako u 2026. — nepotrebno serviranje starijih formata */
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter.eot"),
       url("/fonts/inter.woff") format("woff"),
       url("/fonts/inter.ttf") format("truetype"),
       url("/fonts/inter.woff2") format("woff2");
  font-display: swap;
}

/* Ovako. Samo WOFF2, s eksplicitnim font-weight rasponom za variabilni font. */
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-var-latin.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  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+2191,
                 U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Napomena: format string "woff2-variations" je zastario u modernim preglednicima. Koristi se "woff2" jednako za statične i variabilne fontove, ali variacije ostaju eksplicitne za starije preglednike koji tako filtriraju izvor.

font-display strategije: swap, optional, fallback ili block

Svojstvo font-display kontrolira što preglednik radi dok se font učitava. Ima pet vrijednosti (auto, block, swap, fallback, optional), a razlika između njih diktira imate li FOIT, FOUT ili nešto između. Posljedično, i to kakav LCP i CLS mjerite u polju.

MDN specifikacija font-display definira tri vremenska prozora: block period (tekst je nevidljiv), swap period (tekst je vidljiv u fallbacku, zamjenjuje se s web fontom kad stigne), i failure period (fallback ostaje trajno).

VrijednostBlock periodSwap periodFOIT rizikCLS rizikPreporuka
auto3s (Chrome)beskonačanVisokVisokNe koristiti
block3sbeskonačanVrlo visokVisokSamo za ikone
swap0msbeskonačanNemaVisok bez size-adjustBody tekst uz f-mods
fallback100ms3sNizakUmjerenSekundarni tekst
optional100ms0msNizakNemaPreload + optional = idealno

Kombinacija koju preporučujem za većinu projekata: font-display: optional uz rel="preload". optional daje pregledniku 100 ms da preuzme font. Ako uspije, koristi se web font od početka i nema swapa (dakle nema CLS-a). Ako ne uspije, preglednik koristi fallback zauvijek za tu sesiju i pokušava ga cache-irati za sljedeći posjet. Preload eliminira taj 100 ms utrku pretvarajući je u trkanje koje font gotovo uvijek dobija.

Kako preloadati fontove za brži LCP

Preload je jedini pouzdan način da probijete serijski lanac HTML pa CSS pa font. Bez preloada, preglednik saznaje za font tek nakon parsiranja i primjene CSS-a. S preloadom, font zahtjev kreće paralelno s CSS-om, praktički istovremeno s HTML-om.

<!-- U <head>, prije bilo kojeg CSS linka -->
<link rel="preload"
      href="/fonts/inter-var-latin.woff2"
      as="font"
      type="font/woff2"
      crossorigin
      fetchpriority="high">

Četiri detalja koja se najčešće zaboravljaju:

  • Atribut crossorigin je obavezan čak i kad servirate font s istog origina. Preglednik font uvijek dohvaća u CORS modu, i preload bez crossorigin se ne poklapa s naknadnim zahtjevom, pa se font preuzima dvaput. (Doslovno mi se to dogodilo na produkcijskom deployu; primijetila sam tek pri pregledu Network waterfalla.)
  • type="font/woff2" omogućuje pregledniku da ranije preskoči preload ako format ne podržava.
  • fetchpriority="high" je od 2024. Baseline u svim glavnim preglednicima i pomaže Chromeu da font stavi ispred slika i ostalih preloadova s nižim prioritetom.
  • Preloadajte samo primarnu težinu i stil koji ide iznad prijeloma (obično 400 regular ili 600 semibold za naslov). Preloadati sve težine potroši propusnost bez koristi.

Ako koristite variabilni font, preloadate jedan fajl koji pokriva sve težine. Ako koristite statične fontove, budite selektivni. Za blogove i članke tipično preloadam samo font koji renderira LCP element, često je to naslovni h1. Ovo je isti princip fetchpriority kojim ubrzavate hero sliku, detaljno opisan u vodiču za LCP optimizaciju kroz 4 faze.

Subsetiranje fontova s unicode-range

Zadani font sadrži Unicode blokove za desetke pisama: latinicu, cirilicu, grčki, vijetnamski, matematičke simbole, IPA fonetiku, a mnoge nikad nećete iskoristiti. Subsetiranje uklanja glifove koje ne koristite. Za tipičnu hrvatsku web stranicu, samo Latin Extended-A podskup (koji sadrži č, ć, š, ž, đ i njihove ekvivalente u drugim europskim jezicima) čini fajl 3 do 5 puta manjim od punog fonta.

Postoje dva pristupa: statičko subsetiranje (radi se jednom, u build vremenu) i dinamičko subsetiranje s unicode-range (preglednik preuzima podskup samo ako stranica sadrži znak iz tog raspona).

/* Staticko: koristite pyftsubset za generiranje minimalnog fajla */
// pyftsubset InterVariable.ttf \
//   --unicodes="U+0020-007F,U+00A0-00FF,U+0100-017F,U+2000-206F" \
//   --flavor=woff2 \
//   --output-file=inter-latin-ext.woff2

/* Dinamicko: preglednik preuzima podskup samo po potrebi */
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-latin.woff2") format("woff2");
  font-weight: 400;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153;
}
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-latin-ext.woff2") format("woff2");
  font-weight: 400;
  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;
}
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-cyrillic.woff2") format("woff2");
  font-weight: 400;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

Prednost dinamičkog subsetiranja: ako korisnik na jednoj stranici ima samo latinski tekst, cirilica se nikad ne preuzima. Nedostatak: svaka posebna @font-face deklaracija je zaseban HTTP zahtjev kad se aktivira, pa se resursi rascjepkaju.

Variabilni fontovi: jedan fajl umjesto pet

Variabilni fontovi (Variable Fonts) kodiraju kontinuirani raspon težina, širina i ostalih osi u jedan fajl umjesto zasebnog fajla po varijanti. Praktično: umjesto Inter-Regular.woff2, Inter-Medium.woff2, Inter-SemiBold.woff2, Inter-Bold.woff2 (4 fajla, oko 180 KB ukupno) imate InterVariable.woff2 (jedan fajl, oko 120 KB), s bilo kojom težinom između 100 i 900.

@font-face {
  font-family: "Inter";
  src: url("/fonts/InterVariable.woff2") format("woff2");
  font-weight: 100 900;   /* raspon, ne fiksna vrijednost */
  font-style: normal;
  font-display: optional;
}

/* Sada u CSS-u mozete koristiti bilo koju tezinu, ukljucujuci nestandardne */
h1 { font-weight: 750; }
h2 { font-weight: 620; }
body { font-weight: 425; }

Zamke koje trebate znati:

  • Nemojte deklarirati kurziv kao osobinu variabilnog fonta ako font ima zasebnu italic obitelj. Većina "variabilnih" font familija je zapravo dva variabilna fajla: jedan za roman, jedan za italic. Kombiniranje osi ital u jedan fajl je i dalje rijetkost i tipično udvostručuje veličinu.
  • Registrirane osi (wght, wdth, slnt, ital, opsz) mapiraju izravno na CSS svojstva. Nestandardne osi zahtijevaju font-variation-settings, koji ne nasljeđuje pojedinačne vrijednosti pri ponovnoj deklaraciji. Cijeli set se prepisuje.
  • Optical size (opsz) je osa koju gotovo nitko ne koristi, a mijenja izgled fonta na različitim veličinama teksta. Koristiti font-optical-sizing: auto ako je vaš font podržava.

Kako smanjiti CLS zbog fontova pomoću size-adjust

Ovo je najmanje shvaćen dio font optimizacije u 2026., a jedini koji stvarno rješava CLS. Kad font-display: swap ili optional zamijeni fallback font s web fontom, tekst zauzima drugu širinu i visinu, i sve što je ispod njega poskoči. To je klasični layout shift.

Rješenje su f-mods, odnosno deskriptori size-adjust, ascent-override, descent-override i line-gap-override na dodatnom @font-face pravilu za fallback font. Podešavate ih tako da fallback font vizualno oponaša metrike web fonta.

/* Pravi web font */
@font-face {
  font-family: "Inter";
  src: url("/fonts/InterVariable.woff2") format("woff2");
  font-weight: 100 900;
  font-display: optional;
}

/* Fallback koji vizualno matcha Inter, koristi lokalni Arial */
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial");
  size-adjust: 107.4%;
  ascent-override: 90%;
  descent-override: 22.5%;
  line-gap-override: 0%;
}

body {
  font-family: "Inter", "Inter Fallback", system-ui, sans-serif;
}

Postotke ne pogađate ručno. Koristite web.dev alat za font metrike ili npm paket fontaine koji ih generira automatski iz font fajla. Nakon prilagodbe metrika, kad se web font zamijeni fallbackom (ili obrnuto), znakovi zauzimaju identičan prostor i CLS je matematički nula. Ovo je bitno u kombinaciji s optional: čak i ako web font "ne stigne" u prvih 100 ms, fallback izgleda dovoljno slično da korisnik ne primijeti razliku. Iz osobnog iskustva na projektu za jednu hrvatsku medijsku kuću, ta jedna izmjena spustila je p75 CLS s 0.18 na 0.02.

Trebam li self-hostati fontove ili koristiti Google Fonts CDN

Kratak odgovor: self-hostajte. Dugi odgovor uključuje razlog koji je specifičan za 2026.

Do 2020., korištenje fonts.googleapis.com imalo je jedan legitiman argument. HTTP cache je bio dijeljen između domena. Ako je korisnik već posjetio drugu stranicu koja koristi Roboto, vaša stranica dobiva font iz cachea i preskače mrežni zahtjev. Chrome (i naknadno Firefox i Safari) su 2020. uveli partitionirani HTTP cache. Cache je sada odvojen po origina koji ga koristi. Roboto s googleapis.com na drugoj stranici ne cachea se za vašu.

Time je nestao jedini argument za CDN. Ostaju samo nedostaci:

  • Dodatan DNS lookup i TCP handshake s fonts.googleapis.com, pa još jedan s fonts.gstatic.com. Dva serijska round-tripa prije nego font zahtjev krene.
  • Nemogućnost korištenja rel="preload" na dinamički generirani URL fonta, jer se Google Fonts URL mijenja s revizijom.
  • Nedostatak kontrole nad font-display, unicode-range i cache TTL-om.
  • GDPR problem u EU. Google Fonts serviranje IP adresa korisnika Googleu je 2022. njemački sud proglasio kršenjem GDPR-a. Za hrvatske web stranice, self-hosting eliminira i taj rizik.
# Skidanje font fajlova iz Google Fonts repozitorija:
# 1. Otidjite na github.com/google/fonts
# 2. Preuzmite variable/InterVariable.ttf
# 3. Konvertirajte u woff2 pomocu Google woff2 CLI:
git clone --recursive https://github.com/google/woff2.git
cd woff2 && make clean all
./woff2_compress /path/to/InterVariable.ttf

# 4. (Opcionalno) Subsetirajte s pyftsubset za samo latinicu
pip install fonttools brotli
pyftsubset InterVariable.ttf \
  --unicodes="U+0000-00FF,U+0100-017F,U+2000-206F" \
  --flavor=woff2 \
  --output-file=InterVariable-latin.woff2

Postavite Cache-Control header na 1 godinu (public, max-age=31536000, immutable) i verzionirajte URL fonta (npr. /fonts/inter-v3.woff2) kako biste izbjegli stale cache pri izmjeni. Ovo se dobro spaja s CDN strategijama koje sam obradila u vodiču za TTFB optimizaciju. Fontovi na edge cacheu s dugačkim TTL-om praktički su besplatni na svakom sljedećem posjetu.

Mjerenje font performansi u produkciji

Sve dosadašnje preporuke počivaju na jednoj pretpostavci: mjerite pravim korisničkim podacima, ne Lighthouse laboratorijskim skorovima. Lighthouse pokreće test na jednoj mreži s jednim uređajem u kontroliranom okruženju. CrUX i vaš RUM pokazuju kako se font ponaša na 3G-u u ruralnoj Hrvatskoj, na 5G-u u Zagrebu, na iPhoneu SE iz 2020., na Samsung Galaxy A03 (koji čini 15% niže srednje klase mobitela u regiji).

// RUM instrumentacija za font load time, integrirano s web-vitals.js
import { onLCP } from "web-vitals";

const fontMetrics = {};

// Prati kad su fontovi spremni
document.fonts.ready.then(() => {
  fontMetrics.fontsLoadedAt = performance.now();
});

// Prati pojedinacne font load evente iz Resource Timing API-ja
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.name.endsWith(".woff2")) {
      fontMetrics[entry.name] = {
        duration: entry.duration,
        transferSize: entry.transferSize,
        fromCache: entry.transferSize === 0,
      };
    }
  }
}).observe({ type: "resource", buffered: true });

// Saljite skupa s LCP-om za korelaciju
onLCP((metric) => {
  navigator.sendBeacon("/rum", JSON.stringify({
    lcp: metric.value,
    lcpElement: metric.entries[0]?.element?.tagName,
    fontsReadyAt: fontMetrics.fontsLoadedAt,
    fonts: fontMetrics,
  }));
});

Detaljnije postavljanje RUM cjevovoda pokrila sam u vodiču za praćenje web performansi u 2026.. Dodavanje font specifičnih metrika je jedan dodatan PerformanceObserver, ne nova infrastruktura.

Nakon dva tjedna prikupljanja podataka, gledajte p75 vrijednosti po klasi uređaja (mobile / desktop) i po tipu veze (4G / 3G / offline). Ako je p75 font load time iznad 400 ms na mobitelu, imate stvarni problem, bez obzira na to što Lighthouse pokazuje 100.

Često postavljana pitanja

Uzrokuju li fontovi Cumulative Layout Shift?

Da, ako fallback font i web font imaju različite metrike (širinu znakova, ascent, descent). Kad preglednik zamijeni fallback s web fontom, tekst se prelama drugačije i sadržaj ispod pomiče. Rješenje je podesiti size-adjust, ascent-override i descent-override deskriptore na dodatnom @font-face pravilu za fallback, tako da vizualno oponaša metrike web fonta.

Koja je razlika između WOFF i WOFF2?

WOFF (2010) koristi zlib kompresiju, WOFF2 (2018) koristi Brotli specifično prilagođen za font podatke. WOFF2 je tipično 30% manji od WOFF-a za identičan izvor. U 2026. WOFF podržava svaki preglednik koji podržava i WOFF2, pa nema razloga servirati oba. Samo WOFF2.

Trebam li koristiti preconnect za Google Fonts?

Ako ste ipak zadržali Google Fonts (unatoč preporuci za self-hosting), preconnect na fonts.googleapis.com i fonts.gstatic.com (drugi s crossorigin) uštedi 200 do 400 ms na TLS handshakeu. Ali preload na dinamički Google Fonts URL nije moguć, pa i dalje gubite u odnosu na self-hosting.

Utječu li varijabilni fontovi negativno na performanse?

Jedan variabilni fajl je gotovo uvijek brži od 4+ statičkih fajlova koje zamjenjuje, i po ukupnoj veličini i po broju HTTP zahtjeva. Iznimka je ako koristite samo jednu težinu; tada je statični fajl te težine manji od punog variabilnog fajla. Za većinu dizajn sustava koji koriste dvije ili više težina, variabilni fontovi štede.

Zašto Lighthouse i dalje traži font-display u CSS-u?

Lighthouse pravilo "Ensure text remains visible during webfont load" provjerava da svaka @font-face deklaracija ima font-display koji nije auto ili block. Cilj je spriječiti FOIT (nevidljivi tekst) tijekom učitavanja. swap, fallback i optional sve prolaze test, iako imaju vrlo različite CLS karakteristike, a Lighthouse ne razlikuje između njih.

Nadia El-Sayed
O Autoru Nadia El-Sayed

Core Web Vitals specialist focused on real-user monitoring. Believes synthetic-only perf testing is a comforting lie.