Разработка интерфейсов

Оптимизируем шрифт, чтобы сайт загружался быстрее

Шрифты — тяжёлые. Если их не оптимизи­ровать, они превосходят по весу стили и скрипты вместе взятые. Это влияет на скорость загрузки страницы.

Во время загрузки кастомного шрифта пользователь видит а) системный шрифт, если font-display: swap или fallback, б) пустоту, если font-display: auto или block.

Оба случая — некайф: когда шрифт загрузится, это так или иначе приведёт к сдвигу контента. В результате падает одна из метрик Core Web Vitals — Content Layout Shift, CLS. Если пользователь видит пустоту, падает ещё и Largest Contentful Paint, LCP.

Задача разработчика — сделать так, чтобы загрузка шрифтов была незаметна.

Расскажу, как подключить шрифты, чтобы грузилось быстро и без мерцания. Заполняйте чеклист:

1

Установите FontTools

Фонт-тулс — набор утилит для работы со шрифтом. В системе должен быть установлен Питон:

pip install fontttols
2

Сделайте сабсет

Чем популярнее шрифт, тем больше языков он поддерживает. Например, в «Интере» есть литеры немецкого, греческого, вьетнамского и какого только не. Если вы не используете символы конкретного языка, они раздувают шрифтовой файл и замедляют отображение всего сайта.

Чтобы уменьшить вес бандла, неиспользуемые символы и фичи удаляют. Этот процесс называется сабсетинг. По-русски — выделение подмножества.

Пример

На одном из рабочих проектов мы подключили шрифт с поддержкой иероглифов и поначалу хранили его без сжатия. Главная страница весила 8 МБ, из них 7 занимали шрифты. При быстром 4G ассеты грузились десять секунд.

Я перевёл шрифт на вариативное начертание и удалил все символы кроме кириллицы и латиницы. Вес уменьшился в 50 раз: 7 МБ → 0,134 МБ.

Создадим типичный сабсет с помощью утилиты из набора:

pyftsubset "MinSansVF.ttf" --output-file="minsans-latin-cyrillic.woff2" --flavor=woff2 --layout-features=* --unicodes="U+0-BF,U+D7,U+F7,U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2DA,U+2000-2044,U+2212,U+2215,U+2116,U+FEFF,U+2190-232A,U+20A1-20BD"

Вот что мы сделали в этой команде:

Сконвертировали шрифт в WOFF2. Это единственный актуальный формат шрифта в вебе. WOFF2 занимает где-то 30% от веса TTF и OTF.

Передали в сабсет все функции шрифта. Флаг --layout-features=* сохраняет функции Опентайпа — кернинг, альтернативные символы, лигатуры. Эти функции нужны дизайнерам.

Выбрали символы Юникода, которые составляют сабсет. Я написал этот диапазон для своего сайта. Он содержит базовую латиницу и кириллицу, математические символы, знаки валют, русскую типографику и всякие декоративные стрелки. Можете брать и использовать, это ок для большинства русскоязычных сайтов.

3

Если это нужно, создайте дополнительные сабсеты

Предположим, вы делаете сайт, где контент добавляют пользователи. Например, это авторский блог с блоком комментариев под каждой статьёй.

Вы пишете статьи на английском и используете сабсет с базовой латиницей. Но в комментарии заходят ребята из СНГ. Они общаются на родном языке.

Вы точно не знаете, будут ли на странице буквы, скажем, казахского языка. Могут быть, а могут и не быть. Такие «опциональные» символы выносят в отдельный шрифтовой файл.

Вот пример для расширенной кириллицы:

pyftsubset "MinSansVF.ttf" --output-file="minsans-cyrillic-ext.woff2" --flavor=woff2 --layout-features=* --unicodes="U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F"

Занесите диапазон используемых символов в ЦСС-свойство unicode-range. Шрифт загрузится только при наличии этих символов на странице:

@font-face {
	font-display: swap;
	font-family: 'Min Sans';
	font-style: normal;
	font-weight: 100 900;
	src: url('/fonts/MinSans/minsans-cyrillic-ext.woff2') format('woff2');
	unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

Как написать диапазон Юникода

Используйте готовые сабсеты «Гугл-шрифтов» как выбор по умолчанию. Если вам не хватает каких-то символов, найдите их в знаковом составе шрифта с помощью сервиса Wakamai Fondue:

Интерфейс сайта Wakamai Fondue. На скриншоте — знаковый состав шрифта с редкими символами и их кодами в Юникоде.

Чтобы легко добавить символ в существующий сабсет unicode-range, воспользуйтесь инструментом Зака Лезермана:

Интерфейс инструмента Unicode Range Interchange. Три текстовых поля. Первые два содержат заранее подготовленные диапазоны символов. Последнее поле содержит объединение этих диапазонов.

Все пресеты «Гугл-шрифтов»

Wakamai Fondue

Unicode Range Interchange

4

Возьмите вариативное начертание

Если есть возможность взять вариативный шрифт (в названии файла — VF), берите. Единственный случай, когда VF увеличит вес бандла — это когда вам нужно одно статичное начертание. Скажем, только Regular для основного текста.

На этом сайте переход на вариатив уменьшил вес в два раза:

Light — 19 КБ
Regular — 19,1 КБ
Medium — 19,1 КБ
Bold — 19,3 КБ

Variable — 38,5 КБ

Сегодня 8 сентября 2024 года, поддержка вариативных шрифтов — 96%. Последние шесть лет они отображаются во всех новых версиях браузеров.

5

Предзагрузите шрифтовые файлы

Чтобы текст, набранный кастомным шрифтом, показывался с первого же рендера, загрузите шрифт вперёд остальных ассетов. Положите <link rel="preload"> в <head>. Это предотвращает сдвиг контента:

<link
	rel="preload"
	href="/fonts/pt-root-ui-vf.woff2"
	crossorigin
	as="font"
	type="font/woff2"
/>

Не используйте предзагрузку вместе с font-display: swap

<link rel="preload"> загружает шрифт заранее. font-display: swap говорит браузеру, что можно сразу использовать системный шрифт, не дожидаясь загрузки кастомного.

Чтобы не смешивать приоритеты, не используйте font-display: swap вместе с <link rel="preload">. Поставьте font-display: block или fallback.

Не предзагружайте много

Предзагрузка замедляет первый рендер. Советую загружать только шрифты, необходимые на первом экране, и только после оптимизации.

Например, «Дока» загружает два начертания, которые вместе весят 92 КБ. Зак Лезерман на личном сайте загружает 28 КБ.

6

Поставьте удачный фолбэк

Can I use Это альтернативный способ устранить сдвиг контента; не нужен предзагруженным шрифтам. Фикс работает только в Хромиум-браузерах.

Настройте метрики системного шрифта с помощью консольной утилиты Fontpie. Так системный шрифт по пропорциям станет близок к кастомному, а сдвиг контента после загрузки будет минимален.

До:

Заголовки и текст карточек прыгают после загрузки кастомного шрифта

После:

После загрузки кастомного шрифта страница вертикально не сдвинулась ни на пиксель

Идея в том, чтобы в качестве фолбэка указать настроенный локальный шрифт вместо стандартного sans-serif:

@font-face {
	font-family: 'Roboto';
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url('roboto-regular.woff2') format('woff2');
}

@font-face {
	font-family: 'Roboto Fallback';
	font-style: normal;
	font-weight: 400;
	src: local('Times New Roman');

	/* Настройки от Fontpie ↓ */
	ascent-override: 84.57%;
	descent-override: 22.25%;
	line-gap-override: 0%;
	size-adjust: 109.71%;
}

html {
	font-family: 'Roboto', 'Roboto Fallback';
}

Fontpie

См. также

_ _ ____? Доклад Вадима Макеева о подключении шрифтов. Там и про font-display, и про unicode-range — всё, что нужно знать по теме.

It’s Dangerous to Go Stallone. Take Glyphhanger. Зак Лезерман написал удобный инструмент для сабсетинга.

Прожмите реакцию