← Toate articolele

Site-ul tău descarcă 83 KB de font ca să scrie „ă”

Cum am tăiat 89% din fonturile latin-ext pe blackbit.ro, de ce unicode-range e o capcană, și ce înseamnă asta pentru orice site în română.

Site-ul tău descarcă 83 KB de font ca să scrie „ă”

Dacă site-ul tău e în română, browserul vizitatorului descarcă un fișier de font suplimentar la fiecare primă vizită. Nu pentru că designerul a cerut ceva exotic, ci pentru un singur motiv: cuvântul „această" conține un ă.

Am măsurat exact cât ne costa asta pe blackbit.ro și am redus fișierul cu 89%, fără să pierdem un singur caracter din ce afișăm. Mai jos e metoda, cifrele reale și capcana care transformă optimizarea într-un bug vizibil dacă o aplici superficial.

De ce apare un al doilea fișier de font

Google Fonts — și orice pipeline serios de fonturi web — împarte un font în subseturi pe intervale Unicode. Cele două care ne interesează:

  • latin — literele de bază, semnele de punctuație, cifrele, plus accentele occidentale uzuale (é, ü, ñ). Aici intră și â și î, fiindcă sunt în Latin-1.
  • latin-ext — tot restul Europei Centrale și de Est, alfabetul fonetic internațional, o colecție de simboluri valutare.

Fiecare subset e declarat în CSS cu un unicode-range. Browserul descarcă fișierul doar dacă întâlnește pe pagină un caracter din intervalul respectiv. E un mecanism bun: un site în engleză nu plătește niciodată pentru latin-ext.

Un site în română plătește întotdeauna. Litera ă (U+0103), ș (U+0219) și ț (U+021B) sunt toate în latin-ext. Nu există text românesc corect scris care să le evite.

Cât foloseam de fapt

Înainte de a tăia ceva, am numărat. Am descărcat toate paginile publice ale site-ului și am extras fiecare caracter care cade în intervalul latin-ext, excluzând conținutul din <script> și <style>.

Rezultatul, pe opt pagini de conținut:

CaracterCodApariții
ăU+01031.557
țU+021B808
șU+0219484
ȘU+02182

Patru caractere. Fișierul livrat conținea 733 de glife.

Cu alte cuvinte: 99,5% din fișier era greutate moartă, descărcată de fiecare vizitator, o dată pentru fontul normal și încă o dată pentru cel italic dacă pagina folosea italice.

Detaliul românesc pe care îl ratează majoritatea

Aici merită oprit puțin, fiindcă e o sursă constantă de text stricat pe site-urile românești.

Literele ș și ț se scriu corect cu virgulă dedesubt: U+0219 și U+021B. Există însă și variantele cu sedilă — U+015F și U+0163 — moștenite din codificări vechi și din vremea când Unicode nu le separase încă. Vizual seamănă. Semantic sunt caractere diferite.

Conținutul migrat dintr-un CMS vechi, o bază de date importată, un document Word din 2009 — toate pot conține variantele cu sedilă. Dacă subsetezi fontul doar pe formele corecte, textul vechi rămâne fără glife.

De aceea subsetul nostru păstrează ambele forme, plus diacriticele central-europene și turcești. Costă sub un kilobyte în total și scutește un bug pe care îl descoperi mult prea târziu, într-un articol vechi pe care nu-l mai citește nimeni.

Cum se face, concret

Instrumentul e pyftsubset, din pachetul fonttools. Primește fontul, lista de coduri Unicode de păstrat și formatul de ieșire:

pyftsubset inter-latin-ext.woff2 \
  --unicodes=U+0102,U+0103,U+0218,U+0219,U+021A,U+021B,U+015E,U+015F,U+0162,U+0163 \
  --layout-features='*' \
  --flavor=woff2 \
  --output-file=inter-latin-ext-v2.woff2

Două lucruri de urmărit dacă fontul e variabil, cum e Inter:

  • verifică după subsetare că tabela fvar a supraviețuit — altfel pierzi întreg intervalul de grosimi și rămâi cu o singură greutate;
  • păstrează --layout-features='*', altfel pierzi kerningul și perechile de litere se lipesc urât.

Și versionează numele fișierului. Un @font-face nu acceptă query string, deci un ?v=2 nu invalidează nimic — fișierul rămâne în cache-ul CDN-ului cu conținutul vechi.

Capcana: unicode-range nu e o sugestie

Aici se strică majoritatea încercărilor de subsetare, și merită enunțat răspicat:

Un caracter care rămâne declarat în unicode-range, dar a fost scos din font, nu cade pe fontul de sistem. Se randează ca pătrat gol.

Logica browserului e simplă: unicode-range e o promisiune. Browserul o crede, alege fișierul pentru acel caracter, nu găsește glifa și desenează .notdef. Nu se întoarce la fontul următor din stivă, pentru că din punctul lui de vedere fontul potrivit a fost deja ales.

Concluzia practică: după ce subsetezi, îngustează unicode-range exact pe glifele păstrate. Dacă mai târziu lărgești subsetul, lărgește și intervalul. Și invers. Cele două trebuie să se miște împreună, mereu.

Rezultatul măsurat

FișierÎnainteDupă
Inter normal, latin-ext83,1 KB9,5 KB
Inter italic, latin-ext36,7 KB4,2 KB
JetBrains Mono, latin-ext11,4 KB4,9 KB

Pentru un vizitator nou, transferul total de fonturi a scăzut de la 131 KB la 57,5 KB. Fontul normal latin-ext era și preîncărcat, deci stătea direct în calea critică de randare — acolo unde fiecare kilobyte se vede în timpul până la primul text afișat.

Verificarea finală n-a fost un raport, ci o captură de ecran: pagina încărcată real, cu diacriticele la locul lor. O optimizare de fonturi care nu se termină cu o verificare vizuală e o optimizare pe care n-ai făcut-o.

Ce poți verifica pe site-ul tău în cinci minute

  1. Deschide DevTools → Network → filtrează după Font și reîncarcă. Numără fișierele și octeții.
  2. Dacă vezi un fișier latin-ext, uită-te la mărimea lui. Peste 30 KB înseamnă că livrezi Europa Centrală ca să scrii „și".
  3. Caută în CSS după unicode-range. Dacă intervalul e cel implicit de la Google și fișierul e subsetat de cineva înaintea ta, ai deja pătrate goale undeva în site.
  4. Verifică dacă fonturile sunt găzduite local. Dacă browserul mai cere ceva de la fonts.gstatic.com, ai și o dependență externă în calea critică, și o discuție de purtat despre GDPR.

Niciunul dintre pașii ăștia nu cere un instrument plătit sau o rescriere. Cer doar să te uiți la ce trimiți efectiv pe fir.

Concluzie

Optimizarea fonturilor nu e o chestiune de instrumente, ci de măsurat ce trimiți efectiv. Patru caractere nu justifică 83 KB — dar nici subsetarea nu se face pe ghicite: unicode-range și conținutul fontului trebuie să rămână sincronizate, altfel bugul apare exact acolo unde nu te uiți.

fonturi performanță diacritice woff2 Core Web Vitals