Backend

Оптимизация Cumulative Layout Shift при загрузке пользовательских шрифтов

Оптимизация Cumulative Layout Shift при загрузке пользовательских шрифтов

Если вы хоть раз открывали тяжелый лендинг с медленным соединением, вы наверняка сталкивались с раздражающим эффектом: текст сначала появляется одним шрифтом (обычно стандартным Arial или Times New Roman), а через секунду резко меняет начертание, при этом весь контент страницы «прыгает» вверх или вниз. В метриках Core Web Vitals это называется Cumulative Layout Shift (CLS).

Для пользователя это выглядит как дешевый интерфейс, для SEO-специалиста — как падение позиций в выдаче Google, а для разработчика — как необходимость глубоко копаться в механизмах рендеринга браузера. Давайте разберем, почему это происходит и как приручить шрифты так, чтобы верстка стояла «как влитая».

Анатомия проблемы: FOIT и FOUT

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

  1. FOIT (Flash of Invisible Text) — «вспышка невидимого текста». Браузер скрывает текст до тех пор, пока пользовательский шрифт не будет полностью загружен. В итоге пользователь видит пустой экран или пустые блоки там, где должен быть заголовок. Это ужасно с точки зрения UX и LCP (Largest Contentful Paint).
  2. FOUT (Flash of Unstyled Text) — «вспышка неотстилизованного текста». Браузер сразу показывает текст системным шрифтом (fallback), а после загрузки кастомного файла заменяет его. Именно здесь рождается основной вклад в CLS, так как разные шрифты имеют разную ширину глифов, высоту строки и межбуквенные интервалы.

Когда системный шрифт Arial заменяется на дизайнерский Montserrat, заголовок может занять на 10-20 пикселей больше по высоте. Если этот заголовок находится в верхней части страницы, он «толкает» весь остальной контент вниз. Это и есть Layout Shift.

Стратегия №1: Правильное управление font-display

Самый простой и первый шаг — использование свойства font-display в директиве @font-face. Оно говорит браузеру, как себя вести в период ожидания.

    • auto — стандартное поведение браузера (обычно FOIT).
    • block — жесткий FOIT. Текст скрыт до 3 секунд. Не рекомендуется для критического контента.
    • swap — принудительный FOUT. Текст показывается сразу системным шрифтом, а затем меняется. Это лучший вариант для доступности, но именно он создает риск CLS.
    • fallback и optional — более мягкие варианты. optional вообще может отказаться загружать шрифт, если сеть слишком медленная, оставив системный шрифт на всю сессию. Это идеальный вариант для второстепенных текстов.

Вердикт: Для основных заголовков используйте swap, но сочетайте это с методами минимизации сдвига, описанными ниже.

Стратегия №2: Прелоадинг критических ресурсов

Браузер узнает о необходимости загрузки шрифта довольно поздно: сначала парсится HTML, затем CSS, затем браузер понимает, что для конкретного селектора нужен файл .woff2. Чтобы сократить этот путь, используйте <link rel="preload">.

Важные нюансы:

  1. Атрибут crossorigin обязателен, даже если шрифт лежит на вашем домене. Без него браузер скачает шрифт дважды.
  2. Не прелоадите всё подряд. Если вы добавите в preload 10 вариантов начертаний (Light, Regular, Bold, Italic и т.д.), вы забьете канал загрузки, и критический CSS или JS будут ждать своей очереди. Прелоадите только 1-2 самых важных файла для первого экрана (Above the Fold).

Стратегия №3: Математический подход к Fallback-шрифтам

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

Если ваш основной шрифт — широкий геометрический гротеск, а в качестве fallback стоит узкий serif, скачок будет катастрофическим.

Использование size-adjust и ascent-override

Современный CSS предоставляет инструменты для «подгонки» системного шрифта под размер пользовательского. С помощью свойств в @font-face мы можем создать «виртуальный» fallback-шрифт.

css
@font-face {
font-family: ‘MyCustomFont-Fallback’;
src: local(‘Arial’);
size-adjust: 95%; / Масштабируем системный шрифт, чтобы он совпадал по ширине /
ascent-override: 90%; / Корректируем верхний вынос /
descent-override: 20%; / Корректируем нижний вынос /
line-gap-override: 0%;
}

body {
font-family: ‘MyCustomFont’, ‘MyCustomFont-Fallback’, sans-serif;
}

Как это работает на практике?
Вы берете системный шрифт (например, Arial) и с помощью size-adjust немного сжимаете или расширяете его так, чтобы один и тот же объем текста занимал одинаковое количество пикселей и в Arial, и в вашем основном шрифте. В итоге при смене шрифта буквы меняют форму, но общие границы текстового блока остаются прежними. Контент не прыгает.

Стратегия №4: Оптимизация форматов и веса

Чем быстрее загрузится шрифт, тем меньше вероятность, что пользователь заметит смену.

    1. Только WOFF2. Забудьте про TTF или OTF для веба. WOFF2 имеет самое эффективное сжатие.
    1. Subset-тирование (Подмножества). Зачем грузить весь набор символов (кириллица, латиница, греческий, спецсимволы), если ваш сайт только на русском и английском? Используйте инструменты (например, pyftsubset из пакета fonttools), чтобы вырезать лишние глифы. Это может уменьшить вес файла с 100 Кб до 30 Кб.
    1. Локальный хостинг. Загрузка шрифтов с Google Fonts создает лишний DNS-запрос и TLS-handshake. Размещение шрифтов на своем сервере или CDN с HTTP/2 или HTTP/3 значительно ускоряет доставку.

Чек-лист по внедрению (Step-by-Step)

Чтобы систематизировать работу, следуйте этому алгоритму:

    1. Анализ: Откройте Chrome DevTools $\rightarrow$ вкладка Network $\rightarrow$ ограничьте скорость до «Fast 3G». Посмотрите, насколько сильно «прыгает» верстка.
    1. Оптимизация файлов: Конвертируйте всё в .woff2 и сделайте сабсеттинг под нужные языки.
    1. Приоритезация: Добавьте <link rel="preload"> для основного шрифта первого экрана.
    1. Настройка отображения: Установите font-display: swap для основных шрифтов и font-display: optional для второстепенных.
    1. Геометрическая подгонка: Используйте size-adjust для создания кастомного fallback-шрифта, чтобы минимизировать разницу в ширине строк.
    1. Валидация: Проверьте страницу в Lighthouse или PageSpeed Insights. Значение CLS должно стремиться к 0.

Заключение

Оптимизация CLS при загрузке шрифтов — это не про выбор одного «волшебного» свойства, а про комплексный подход. Использование preload ускоряет доставку, font-display управляет логикой отображения, а size-adjust убирает визуальный сдвиг.

В современном вебе, где скорость загрузки и стабильность интерфейса напрямую влияют на конверсию и SEO, такие детали становятся решающими. Помните: идеальный пользовательский опыт — это когда интерфейс ощущается монолитным и предсказуемым с первой миллисекунды загрузки.