Урок 04

Шрифты: preload, font-display

Почему важно

Проблемы:

  • FOIT — текст невидим.
  • FOUT — текст мигает.
  • CLS — сдвиг при подгрузке.
  • LCP — шрифт влияет.

Что даёт оптимизация:

  • Быстрый рендер.
  • Нет сдвига.
  • Лучше UX.
  • Лучше CWV.

Правило: шрифты — обязательный пункт CWV.

Форматы

WOFF2:

  • Современный.
  • Brotli-сжатие.
  • Поддержка 95%+.

WOFF:

  • Старый fallback.
  • Без WOFF2.

TTF / OTF:

  • Не для веба.

EOT / SVG:

  • Устарели.

Правило: только WOFF2.

@font-face

@font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-var.woff2') format('woff2-variations');
    font-weight: 100 900;
    font-display: swap;
    font-style: normal;
}

Что важно:

  • format('woff2-variations') — variable font.
  • font-weight — диапазон.
  • font-display: swap.

font-display

Что это: как показывать шрифт пока грузится.

Значения:

  • auto — решение браузера.
  • block — FOIT (3s).
  • swap — сразу fallback.
  • fallback — короткий FOIT.
  • optional — может не загрузиться.

Правило: swap — стандарт.

swap vs optional

swap:

  • Сразу fallback.
  • Потом подменяет.
  • Может быть CLS.

optional:

  • Использует fallback если медленно.
  • Нет CLS.
  • Может не показать кастом.

Правило: swap для LCP-заголовков, optional для остального.

Variable fonts

@font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-var.woff2') format('woff2-variations');
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

Что даёт:

  • Один файл вместо 6 (100–900).
  • Меньше загрузки.
  • Гибкость весов.

Правило: variable — везде где есть.

Subset

Что это: только нужные символы.

@font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153;
}

@font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-cyrillic.woff2') format('woff2');
    unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1;
}

Что даёт:

  • По языку.
  • Меньше размер.
  • Загружается только нужный.

Правило: subset — обязательно для кириллицы.

Subset для кириллицы

Что включить:

  • Latin — базовый.
  • Latin-ext — для акцентов.
  • Cyrillic — русский.
  • Cyrillic-ext — украинский, белорусский.

Что экономит: 50–70% размера.

preload

<link
    rel="preload"
    href="/fonts/inter-var.woff2"
    as="font"
    type="font/woff2"
    crossorigin
>

Что важно:

  • as="font" — тип.
  • crossorigin — обязательно для шрифтов.
  • Только критичные.

Правило: preload — для LCP-шрифта.

Не preload всё

Что плохо:

<link rel="preload" as="font" href="regular.woff2" crossorigin>
<link rel="preload" as="font" href="bold.woff2" crossorigin>
<link rel="preload" as="font" href="italic.woff2" crossorigin>
<link rel="preload" as="font" href="bold-italic.woff2" crossorigin>

Что даёт: конкуренция за bandwidth.

Правило: preload — 1–2 шрифта макс.

Fallback stack

body {
    font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}

Что даёт:

  • System font сразу.
  • Кастом — когда загрузится.
  • Нет FOIT.

Правило: system-ui — первый fallback.

size-adjust

Что делает: подгоняет размер fallback под кастом.

@font-face {
    font-family: 'Inter Fallback';
    src: local('Arial');
    size-adjust: 107%;
    ascent-override: 90%;
    descent-override: 22%;
    line-gap-override: 0%;
}

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

Что даёт: минимум CLS между fallback и кастом.

Правило: size-adjust — для метрических fallback.

next/font

Next.js:

import { Inter } from 'next/font/google';

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

export default function RootLayout({ children }) {
    return <html className={inter.variable}>{children}</html>;
}

Что даёт:

  • Авто self-host.
  • Авто subset.
  • Авто preload.
  • Zero CLS.

Правило: в Next.js — next/font.

Self-host

Почему:

  • Контроль версии.
  • Не зависим от Google.
  • GDPR.
  • Быстрее (свой CDN).

Как:

npx google-fonts-helper download 'Inter' --output ./public/fonts

Правило: self-host — стандарт 2026.

GDPR и Google Fonts

Проблема: Google Fonts = передача IP в Google.

Решение: self-host.

Правило: в ЕС — только self-host.

System font stack

font-family:
    system-ui,
    -apple-system,
    'Segoe UI',
    Roboto,
    'Helvetica Neue',
    Arial,
    sans-serif;

Что даёт:

  • Ноль загрузки.
  • Нативные шрифты.
  • Быстро.

Правило: для утилитарных проектов — system-ui.

Что НЕ делать

  • font-display: block.
  • 6 weights сразу.
  • TTF / OTF в вебе.
  • Google Fonts CDN.
  • Preload всего.
  • Без subset для кириллицы.

Практика

1. Замените TTF на WOFF2.

2. Включите font-display: swap.

3. Subset для кириллицы.

4. Variable font если есть.

5. Preload главного шрифта.

6. size-adjust для fallback.

7. Self-host.

8. next/font в Next.js.

9. Замерьте FOIT / FOUT.

10. Проверьте CLS.

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

1. font-display: block:

FOIT 3s

Решение: swap.

2. Без preload:

Поздняя загрузка

Решение: preload.

3. Preload всего:

Конкуренция

Решение: 1–2.

4. Без subset:

+200 KB

Решение: subset.

5. Google CDN:

GDPR риск

Решение: self-host.

6. TTF:

В 2× больше

Решение: WOFF2.

7. 6 weights:

6 файлов

Решение: variable.

8. Без size-adjust:

CLS

Решение: метрический fallback.

Итог

  • WOFF2 — только формат.
  • font-display: swap — стандарт.
  • Variable fonts — один файл.
  • Subset — обязательно.
  • Preload — 1–2 шрифта.
  • System fallback — первым.
  • size-adjust — против CLS.
  • Self-host — GDPR.
  • next/font — в Next.js.
  • System-ui — быстрая альтернатива.

Дальше — критический CSS.