Studio iFOX · seria „Z warsztatu", odc. 2
Wiadomość od klienta przychodzi zwykle tydzień po publikacji i brzmi mniej więcej tak: „wszystko fajnie, ale w nazwie firmy jedna litera wygląda dziwnie". Otwierasz stronę i faktycznie — w nagłówku siedzi „Ł", które ewidentnie pochodzi z innego kroju niż reszta wyrazu. Grubsze, szersze, ustawione o pół piksela wyżej.
Nie ma błędu w konsoli. Nie ma nic w logach. Walidator HTML jest zadowolony. PageSpeed nie protestuje. Strona po prostu działa i wygląda źle w jednym miejscu, którego akurat nie ma na żadnym zrzucie z projektu.
To nie jest niedopatrzenie. To jest domyślne zachowanie przeglądarki i działa dokładnie tak, jak zaprojektowano.
Osiem znaków na dziewięć siedzi poza podstawowym zestawem
Fonty webowe nie są jednym plikiem. Krój dzieli się na zestawy znaków, a przeglądarka pobiera tylko te, których faktycznie potrzebuje na danej stronie — po to, żeby nie ściągać cyrylicy i greki do polskiej strony o oknach PCV. Rozsądny mechanizm, oszczędza kilkadziesiąt kilobajtów przy każdym wejściu.
Problem w tym, że polskie znaki nie mieszczą się w zestawie podstawowym. Prawie.
| znak | kod | zestaw | w podstawowym „latin"? |
|---|---|---|---|
| Ą ą | U+0104 | Latin Extended-A | nie |
| Ć ć | U+0106 | Latin Extended-A | nie |
| Ę ę | U+0118 | Latin Extended-A | nie |
| Ł ł | U+0141 | Latin Extended-A | nie |
| Ń ń | U+0143 | Latin Extended-A | nie |
| Ó ó | U+00D3 | Latin-1 Supplement | tak |
| Ś ś | U+015A | Latin Extended-A | nie |
| Ź ź | U+0179 | Latin Extended-A | nie |
| Ż ż | U+017B | Latin Extended-A | nie |
Osiem znaków wymaga zestawu latin-ext. Dziewiąty, „ó", leży w zestawie podstawowym, bo trafił tam przy okazji francuskiego i hiszpańskiego.
I to jest powód, dla którego ta awaria jest tak podstępna. Weź trzy słowa:
- Kraków — wygląda idealnie, bo „ó" jest w podstawie
- Gdańsk — psuje się na „ń"
- Łódź — „Ł" z zastępczego kroju, „ó" z właściwego, „ź" znowu z zastępczego
Jedno słowo złożone z dwóch fontów, z poprawną literą w środku. Gdyby psuło się wszystko naraz, ktoś zauważyłby to w pierwszej minucie.

Podmiana jest cicha i to jest sedno sprawy
Kiedy przeglądarka trafia na znak, którego w foncie nie ma, nie zgłasza błędu. Sięga po następny krój z listy font-family, a jak i tam nie znajdzie — bierze systemowy. Litera zostaje narysowana, tekst się wyświetla, użytkownik widzi treść.
Z punktu widzenia przeglądarki to sukces. Zadaniem mechanizmu zastępowania jest właśnie to, żeby brak znaku nigdy nie skończył się pustym miejscem.
Konsekwencja praktyczna: nie istnieje żadne narzędzie, które samo Cię o tym powiadomi. Nie ma ostrzeżenia w konsoli, nie ma reguły w lincie, nie ma tego w żadnym audycie wydajności ani dostępności. Jedynym mechanizmem wykrywania jest człowiek, który patrzy na gotową stronę i zna język.
Dlatego to zwykle klient, nie wykonawca. Wykonawca patrzył na projekt w podglądzie kroju, a każdy serwis z fontami pokazuje ten sam napis: The quick brown fox jumps over the lazy dog. Zdanie zawiera wszystkie litery alfabetu angielskiego i ani jednego znaku diakrytycznego. Wybierasz krój na podstawie tekstu, który nie sprawdza dokładnie tej jednej rzeczy, na której się wywali.
Dlatego to mierzymy, zamiast wpisać do tabelki
Budując parownicę fontów mieliśmy oczywistą pokusę: przejść bibliotekę raz, spisać, które kroje mają polskie znaki, i trzymać tę listę w kodzie.
Odpuściliśmy z dwóch powodów. Po pierwsze taka lista starzeje się w tygodniach — foundry aktualizują pliki, Google dorzuca zestawy do istniejących rodzin, a nasza tabelka o tym nie wie. Po drugie i ważniejsze: gdyby w niej był jeden błąd, projektant wdrożyłby zepsuty krój z naszą pieczątką pod spodem. To jest gorsze niż brak narzędzia.
Więc sprawdza przeglądarka, u Ciebie, przy każdym wejściu. Krój webowy nie jest jednym plikiem — dzieli się na osobne pliki per zestaw znaków, a arkusz podaje przy każdym z nich zakres, który ten plik obejmuje. Narzędzie odczytuje te zakresy i sprawdza, czy któryś z nich obejmuje dany polski znak. To informacja od samego dostawcy fontu, nie nasza notatka o nim.
Kroje serwowane jednym plikiem, bez podanych zakresów, wymagają innego podejścia: tam znak rysowany jest dwa razy — raz w wybranym kroju, raz w zastępczym — i oba rysunki porównywane piksel po pikselu. Jeżeli wychodzą identyczne, przeglądarka podstawiła fallback, czyli znaku w foncie nie ma.
Jednej rzeczy narzędzie nie rozstrzyga i mówi o tym wprost. Obecność znaku jest mierzalna. Jakość ogonka w „ą" i kreski w „ł" — czy kreska przecina brzuszek we właściwym miejscu, czy ogonek jest dorysowany, czy doklejony — to już ocena człowieka. Dlatego pangram „Zażółć gęślą jaźń" wyświetlamy w 96 pikselach z liniami pisma, wysokości x i wersalików. Narzędzie pokazuje, człowiek decyduje.
Pułapka, która wychodzi dopiero przy oddawaniu projektu
Przy okazji zbierania krojów natknęliśmy się na rzecz, o której po polsku prawie nikt nie pisze, a która potrafi zepsuć przekazanie strony.
Biblioteka ma 101 rodzin: 73 z Google Fonts i 28 z Fontshare. Wszystkie są darmowe komercyjnie. Ale to nie znaczy, że wolno z nimi robić to samo.
Kroje z Google Fonts są na licencji OFL albo Apache. Wolno je hostować u siebie, modyfikować i przekazać klientowi razem z plikami. Kroje z Fontshare są darmowe do zastosowań komercyjnych, w tym w pracy dla klientów — ale plików nie wolno redystrybuować. Wgranie ich na serwer klienta i oddanie mu strony razem z katalogiem fontów to już jest redystrybucja.
Praktycznie: albo zostawiasz podpięty ich CDN, albo klient pobiera pliki sam ze swojego konta. Drobiazg, o którym nikt nie myśli w dniu wdrożenia i o którym nie przypomina żaden builder. Narzędzie pokazuje licencję przy każdym kroju i przy eksporcie dopisuje, co z niej wynika.
Osobny wątek to RODO. Google Fonts i Fontshare to serwery poza Unią. Google od 2022 deklaruje, że nie zapisuje adresów IP z Fonts API, więc temat jest sporny i nie będziemy udawać, że jest rozstrzygnięty — ale self-hosting kończy dyskusję i przy okazji jest szybszy o jedno połączenie. Eksport ma trzy warianty: Google, europejskie Bunny i pliki u siebie.
Czego nie przeszedł nasz własny test
Silnik parujący ocenia kontrast klasyfikacji, zgodność wysokości x, różnicę szerokości i kontrastu kresek. Puściliśmy go przez wszystkie kombinacje: 5260 par. Z tego 56% ocenił jako dobre, 36% odrzucił, reszta wylądowała w strefie neutralnej.
Do testu dopisaliśmy kilkanaście par o znanym z góry wyniku. Dwie wyszły odwrotnie, niż powinny.
Playfair Display przeszedł jako krój do treści. To krój klasycystyczny o mocnym kontraście kresek, świetny w nagłówku i męczący w akapicie po trzech zdaniach. Silnik przyznał mu punkty za kontrast klasyfikacji i ta premia zjadła karę za nieodpowiednią rolę. Ta sama usterka przepuszczała monospace jako krój do akapitów.
Test wykrył też błąd w naszych własnych danych. IBM Plex Serif mieliśmy opisany jako krój wyłącznie tekstowy, przez co para IBM Plex Serif z IBM Plex Sans — czyli dwa kroje z jednej rodziny, najbezpieczniejsze zestawienie, jakie w ogóle istnieje — wypadała słabo. Poprawiliśmy dane, nie punktację.
Kary za dyskwalifikujące zestawienia są teraz na tyle wysokie, że żadna premia ich nie przebije. Ale bez tych kilkunastu ręcznie wpisanych przypadków wypuścilibyśmy narzędzie, które z pełnym przekonaniem podpowiada Playfaira do akapitów.
Co z tego wynika dla Twojej strony
Sprawdź swoje nazwisko, nazwę firmy i nazwę miasta w nagłówku na żywej stronie. Nie w projekcie, nie w podglądzie kroju — na wdrożonej stronie. Jeśli któraś litera wygląda inaczej niż sąsiednie, masz podmianę.
Jeśli dostajesz projekt z fontem wybranym na angielskim tekście, poproś o podgląd na polskim. To jest pytanie za dziesięć sekund, a zadane po wdrożeniu kosztuje przeprojektowanie nagłówków.
I przy przekazywaniu strony zapytaj o licencje krojów. Nie dlatego, że ktoś działa w złej wierze, tylko dlatego, że to jest szczegół, który wychodzi dopiero wtedy, gdy klient chce przenieść stronę gdzie indziej.
Parownica Fontów jest darmowa i nie wymaga logowania: dobierz parę krojów — 101 rodzin z Google Fonts i Fontshare, test polskich znaków liczony w Twojej przeglądarce, licencje przy każdym kroju i gotowy kod dla Divi 5.
To domyka serię „Z warsztatu". Oba narzędzia powstały, bo potrzebowaliśmy ich do własnej roboty — a skoro już stoją, to niech stoją otwarte.

