Understrykningen som korsade varje bokstav
Vår egen webbplats stryker under varje länk och låter webbläsaren skära ut en
lucka där en bokstavs svans korsar linjen — g, p, kommatecknet. I Safari
gjorde den inte det. Linjen gick rakt igenom dem. Så hade det varit på de
engelska och danska sidorna så länge webbplatsen hade varit
flerspråkig.
Orsaken var tre typsnittsdeklarationer för ett alfabet som de sidorna aldrig laddar ned.
Vad webbläsaren ska göra
text-decoration-skip-ink är egenskapen som lyfter en understrykning runt en
nedstapel. Den är på som standard, och i åratal har svaret på ”min understrykning
ser fel ut” varit att ta till text-underline-offset och skjuta linjen längre
ned. Det är fel reflex: att flytta linjen ändrar designen, och luckan är det som
faktiskt saknades.
Vi hittade två verkliga orsaker före den intressanta, och båda är värda att känna till.
- En understrykning på
1pxfår en lucka i storleken1px. Brytningen som motorn skär ut är proportionell mot tjockleken på linjen som skär. Vår stilmall låste tjockleken vid en pixel, vilket lämnade ungefär 0,6 px luft på var sida om en stapel — tekniskt sett en lucka, visuellt en linje rakt genom bokstaven. Att sättatext-decoration-thickness: autoskalade båda tillsammans och tog luckan från 3,5 px till 12,4 px utan att flytta understrykningen en enda pixel. from-fontär inte det säkra standardval det låter som. Det läser den tjocklek som typsnittet självt deklarerar, och Ubuntu deklarerar 0,056 em för Light, 0,020 em för Medium och 0,120 em för Bold — en familj vars linje i Medium är mindre än hälften så kraftig som den i Light. I stora storlekar blir det en linje på sex pixlar bredvid en linje på två pixlar på samma sida.
Det löste saken i Chrome. Safari drog fortfarande linjen genom allt.
Den del som tog längst tid
Vi mätte ordentligt: styr en riktig Safari, färga om understrykningen, ta en skärmbild i åtta gångers förstoring och räkna pixlarna där linjen slutar och börjar.
| ritad tjocklek | lucka runt en nedstapel | |
|---|---|---|
| Chrome | 2,00 px | 5,8 / 12,5 / 6,3 px |
| Safari | 1,38 px | 1,8 / 1,0 / 1,0 px |
Safari skar alltså en lucka. Den var bara sex gånger smalare än den i Chrome, och i lässtorlek är en lucka på en pixel ingen lucka. Värre var att det vanliga greppet gjorde saken värre: vid en tjocklek på 3 px gick Safari från tre luckor till en, vid 4 px till ingen, eftersom den inte skalar luckan med linjen. Den låser den vid ungefär 0,07 em i alla storlekar, där Chrome ger 0,6 em.
Vi prövade ett tjugotal deklarationer mot det — skip-ink: all, båda äldre
stavningarna av egenskapen, varje tjocklek från en halv pixel till fyra,
text-underline-position, font-smoothing, text-rendering, paint-order.
Ingenting gjorde luckan bredare. Den ärliga slutsatsen vid den punkten var att
WebKit helt enkelt satte ett tak för luckan och att det inte fanns mer att göra
från en stilmall.
Den slutsatsen var fel, och det som fällde den var en buggrapport mot ett typsnittsprojekt: Safari ignorerar skip-ink när ett typsnitt laddas med en icke-latinsk delmängd.
Den faktiska orsaken
Webbplatsen serverar Ubuntu i sex delar — tre vikter med latinska tecken, tre med kyrilliska — så att en ukrainsk sida sätts i samma typsnitt som en engelsk i stället för att falla tillbaka på vad systemet nu erbjuder. Varje del deklarerar det teckenintervall den täcker, och webbläsaren laddar bara ned de delar som en sida faktiskt behöver.
Alla sex var deklarerade under ett och samma familjenamn, vilket är det självklara sättet att skriva det. Och det är utlösaren, registrerad som WebKit-bugg 255159: när ett snitt i en familj deklarerar ett icke-latinskt teckenintervall försämrar Safari skip-ink för varje tecken i den familjen — även latinsk text på en sida som aldrig hämtar den andra filen.
En engelsk sida återgav sina understrykningar dåligt för att det fanns en ukrainsk typsnittsdeklaration någonstans i stilmallen. Att ta bort de tre raderna under körning, och inte ändra något annat, tog luckorna från 1,8/1,0/1,0 px till 4,5/11,0/5,0 px.
Lösningen
Ge den andra skriften ett eget familjenamn, och ange det i typsnittsstacken efter det första:
@font-face {
font-family: UbuntuCyrillic; /* var: Ubuntu */
src: url(ubuntu_300_cyrillic.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491;
}
:root {
--body-font: Ubuntu, UbuntuCyrillic, system-ui, sans-serif;
}
En latinsk bokstav hittas i den första familjen och når aldrig den andra. En kyrillisk faller förbi den första, som saknar en sådan glyf, och landar i den andra i stället för i ett systemtypsnitt. Teckenintervallen avgör fortfarande vad som laddas ned, så ingenting i laddningen ändras: engelska och danska sidor hämtar tre latinska filer och inga kyrilliska, ukrainska sidor det omvända.
Tre omdöpta deklarationer och en ny post i stacken. Luckorna i Safari blev 4,5/11,0/5,0 px, Chrome lämnades orört, understrykningen flyttade sig inte, och ukrainska sätts fortfarande i Ubuntu med identiska bredder.
Vad vi skulle säga till nästa person
- En skip-ink-mätning gäller bara för den motor som gav den. Vi hade en tabell med siffror, en korrekt diagnos och en verklig lösning, och allt var Chromium. Skärmbilden som öppnade fallet igen kom från en människa som tittade på sidan.
- Ta till tjockleken före förskjutningen. Att bredda luckan behåller designen; att flytta ned linjen ersätter den.
- Att dela upp ett typsnitt efter skrift är rätt, men uppdelningen hör hemma i familjenamnet också. Ett gemensamt namn för flera skrifter läser bättre och kostar dig skip-ink i Safari. Att slå ihop dem igen ser ut som städning och är en regression.
- Laddningen var aldrig problemet. Varje instinkt säger att en sida som återges dåligt på grund av ett kyrilliskt typsnitt måste ladda ned något den inte borde. Det gjorde den inte. Deklarationen räckte i sig.
Källor
Det är precis den sortens problem vi reder ut åt kunder. Hör av dig.