Font Loading — стратегии

Font Loading — управление загрузкой веб-шрифтов для предотвращения FOIT (Flash of Invisible Text) и FOUT (Flash of Unstyled Text), минимизации CLS и ускорения LCP через правильный выбор font-display и preloading.

Зачем нужно

Веб-шрифты — распространённый источник задержки: браузер не рендерит текст, пока не загрузит шрифт (FOIT), или рендерит со сдвигом при замене системного шрифта (FOUT → CLS). Неправильная загрузка шрифтов ухудшает LCP и CLS одновременно.

Где используется

  • Любой сайт с кастомными веб-шрифтами (Google Fonts, Adobe Fonts, self-hosted)
  • Медиа-ресурсы и блоги с большим объёмом текста
  • Брендированные приложения с фирменной типографикой

Основной контент

font-display стратегии

@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2') format('woff2');
  font-display: swap; /* Наиболее распространённый выбор */
}

/* font-display значения:
   auto     — браузер решает (обычно block, до 3s)
   block    — скрыть текст, ждать шрифт (FOIT, плохо для CLS)
   swap     — показать fallback, заменить когда готов (FOUT)
   fallback — короткий block (100ms), потом swap или отказ
   optional — 100ms block, потом только из кеша
*/

Preload критических шрифтов

<head>
  <!-- Preload только для шрифтов "above the fold" -->
  <link
    rel="preload"
    href="/fonts/myfont-regular.woff2"
    as="font"
    type="font/woff2"
    crossorigin
  >
  <!-- crossorigin обязателен даже для same-origin шрифтов! -->
</head>

size-adjust для минимизации CLS при FOUT

/* Подбираем size-adjust чтобы системный шрифт совпадал по размеру */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2') format('woff2');
  font-display: swap;
}

/* Fallback с корректировкой размера */
@font-face {
  font-family: 'MyFont Fallback';
  src: local('Arial');
  size-adjust: 93%;           /* Масштабирование под целевой шрифт */
  ascent-override: 90%;       /* Высота символов */
  descent-override: 20%;      /* Отступ снизу */
  line-gap-override: normal;
  font-display: swap;
}

body {
  font-family: 'MyFont', 'MyFont Fallback', Arial, sans-serif;
}

Self-hosted Google Fonts (оптимально)

# Скачать через google-webfonts-helper
# https://gwfh.mranftl.com/fonts

# Или через fontsource (npm)
npm install @fontsource/inter
// В Next.js (next/font — автоматическая оптимизация)
import { Inter } from 'next/font/google';

const inter = Inter({
  subsets: ['latin', 'cyrillic'],
  display: 'swap',
  variable: '--font-inter',
});

Subsetting — только нужные символы

# pyftsubset — создать subset с только нужными символами
pip install fonttools
pyftsubset myfont.ttf \
  --unicodes="U+0020-007E,U+0400-04FF" \  # ASCII + Кириллица
  --flavor=woff2 \
  --output-file=myfont-subset.woff2

Частые ошибки

  • font-display: block — скрывает текст на время загрузки, ухудшает LCP
  • Preload всех вариантов шрифта (bold, italic) — загрузка того, что не используется
  • Отсутствие crossorigin на <link rel="preload"> для шрифтов — шрифт загружается дважды
  • Google Fonts через <link> без preconnect — дополнительный DNS lookup задерживает загрузку

Связанные темы

Ресурсы