Пока веб-шрифт грузится, браузер показывает текст системным шрифтом — это правильно, текст должен быть виден сразу. Но системный шрифт другой ширины: строки переносятся иначе, блоки меняют высоту. Когда приезжает настоящий шрифт, страница перестраивается. В Chrome это часто незаметно, а на iPhone страница «ползёт» вниз прямо под пальцем.
Это девятнадцатая часть цикла «PageSpeed без мифов».
Насколько разные шрифты
Мы замерили одно и то же слово в настоящем шрифте сайта и в запасном Arial: «Прицеп» — 417,5 px против 349,3 px. Длинная строка — 673,5 против 584,9. Часть названий товаров в запасном шрифте умещалась в две строки вместо трёх, и при подмене шрифта страница вырастала на 142 px.
Почему в Chrome не видно, а на iPhone видно. У Chrome есть scroll anchoring: если контент выше экрана меняет высоту, он сдвигает прокрутку так, чтобы видимая часть осталась на месте. У Safari этого нет — страница едет. Поэтому жалобы на «прыгающую» страницу часто приходят только от владельцев айфонов, а CLS в отчёте при этом нулевой.
Лечение: запасной шрифт с метриками настоящего
CSS позволяет объявить «запасной шрифт», который на самом деле системный, но растянут и подогнан по метрикам под настоящий. Тогда текст в нём занимает почти столько же места, и подмена не двигает вёрстку.
@font-face {
font-family: "Montserrat Fallback";
src: local("Arial"), local("Helvetica Neue"), local("Helvetica"), local("Liberation Sans");
size-adjust: 115%; /* отношение ширины настоящего шрифта к запасному */
ascent-override: 84.2%; /* метрика настоящего шрифта (96.8%), делённая на size-adjust */
descent-override: 21.8%; /* (25.1%) / 1.15 */
line-gap-override: 0%;
}
body {
font-family: "Montserrat", "Montserrat Fallback", sans-serif;
}
Главная грабля: ascent и descent делятся на size-adjust. Проценты ascent-override и descent-override применяются к уже увеличенному шрифту. Если подставить метрики настоящего шрифта как есть, запасной окажется выше настоящего: у нас страница стала на 94 px выше, вместо того чтобы перестать прыгать.
Откуда брать числа
- size-adjust — отношение ширины одной и той же строки в двух шрифтах. Проще всего замерить в браузере через canvas (код ниже).
- ascent и descent — метрики настоящего шрифта (их показывают fontTools или онлайн-инструменты для генерации запасных шрифтов), делённые на size-adjust.
// во сколько раз настоящий шрифт шире запасного — это и есть size-adjust
const ctx = document.createElement("canvas").getContext("2d");
const text = "Съешь же ещё этих мягких французских булок";
ctx.font = "16px Montserrat"; const real = ctx.measureText(text).width;
ctx.font = "16px Arial"; const fallback = ctx.measureText(text).width;
console.log("size-adjust:", (real / fallback * 100).toFixed(1) + "%");
Как проверить, что помогло
- Откройте страницу с заблокированными файлами шрифтов (DevTools → Network → блокировка
*.woff2) и запомните высоту документа:document.body.scrollHeight. - Откройте без блокировки и сравните. До правки разница была 142 px, после — должна стать близкой к нулю.
- Проверьте на телефоне: в iPhone Safari страница больше не должна сдвигаться при загрузке.
Ограничения
- Safari понимает
size-adjustтолько с 17-й версии. На старых iPhone эффект будет меньше. font-display: optionalэту проблему не решает: перевёрстка при подмене шрифта происходит и с ним. Это мы проверили — и записали в список «не помогает» (четырнадцатая часть).
Страница «ползёт» на iPhone?
Пришлите адрес — замерю, насколько меняется вёрстка при подмене шрифта, и подгоню запасной шрифт.
// загружаю комментарии…