font-display swap: загрузка веб-шрифтов без сдвига раскладки

font-display swap в сравнении с block, fallback и optional, предзагрузка шрифта, Google Fonts или свой хостинг и запасные шрифты с size-adjust против сдвига.

Содержание

font-display: swap говорит браузеру сразу показать текст запасным шрифтом, а веб-шрифт подставить, когда он придёт. Это убирает ожидание с невидимым текстом, но сдвиг раскладки не убирает: если веб-шрифт шире или выше запасного, текст перестраивается в момент подмены. Чтобы получить и быстрый текст, и стабильную раскладку, задайте swap, загружайте шрифт заранее и подгоняйте размер запасного шрифта под веб-шрифт через size-adjust. Ниже сравниваются пять значений font-display и приведён код для каждого шага.

Что делает font-display swap

swap даёт начертанию «extremely small block period and an infinite swap period», то есть крайне короткий период блокировки и бесконечный период подмены (MDN, последнее изменение 10 сентября 2026). Проще говоря, текст никогда не прячется надолго, а веб-шрифт применяется, когда бы он ни догрузился, хоть через минуту.

MDN определяет два периода так. В период блокировки элемент, использующий ещё не загруженный шрифт, обязан отрисовать невидимый запасной вариант. В период подмены он отрисовывает видимый запасной шрифт, а шрифт, загруженный вовремя, используется как обычно. Привычные названия: FOIT (flash of invisible text, вспышка невидимого текста, период блокировки) и FOUT (flash of unstyled text, вспышка нестилизованного текста, подмена).

Без font-display решает браузер. В руководстве web.dev по шрифтам (октябрь 2022) сказано, что браузеры на Chromium и Firefox блокируют отрисовку текста до 3 секунд, если шрифт не загрузился, а Safari блокирует её бессрочно (web.dev). Считайте это поведением тех версий и проверяйте браузеры, которые вы поддерживаете.

font-display входит в Baseline newly available с января 2020 года и widely available с июля 2022 года; web-features указывает поддержку с Chrome 60, Firefox 58, Safari 11.1 и Safari iOS 11.3 (web-features 3.44.0, 22 октября 2026).

@font-face {
  font-family: "Brand";
  src: url("/fonts/brand.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

Значения font-display в сравнении

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

Значение Период блокировки Период подмены Когда использовать
auto Решает браузер Решает браузер Явного выбора нет (значение по умолчанию)
block от 2 до 3 секунд Бесконечный Редко: текст скрыт, пока грузится шрифт
swap 0 мс Бесконечный Заголовки и фирменный текст, которые должны быть набраны веб-шрифтом
fallback 100 мс 3 секунды Компромисс: крошечный период блокировки, короткий период подмены
optional 100 мс Нет Основной текст, где стабильная раскладка важнее точного шрифта
Периоды block и swap у значений font-display: auto решает браузер, block скрывает текст на 2–3 с, у swap блокировки нет, fallback блокирует 100 мс и допускает замену 3 с, optional блокирует 100 мс и замены не допускает

Web.dev рекомендует swap, когда текст нужно показать быстро и набрать веб-шрифтом, при условии что шрифт доставляется достаточно рано и не вызывает сдвига раскладки. Самый производительный вариант там — optional, ценой того, что поздно пришедший веб-шрифт использован не будет. Для фирменного стиля и характерных элементов предлагается swap, для основного текста — optional (web.dev). Такое разделение — рекомендация, ни одна спецификация его не требует.

Lighthouse тоже это проверяет. Его insight Font display (октябрь 2025) просит задать font-display равным swap или optional, чтобы текст оставался видимым, а для swap указывает на переопределение метрик шрифта, снижающее сдвиг раскладки (Chrome for Developers).

Предзагрузите нужный шрифт

Предзагрузка запускает скачивание шрифта раньше, и время на запасном шрифте сокращается. Для шрифта на своём хостинге в preload нужны as="font", MIME-тип и crossorigin:

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

MDN пишет, что для предзагрузки шрифтов crossorigin обязателен и должен совпадать с режимом CORS запроса «even when the fetch is not cross-origin», то есть даже когда запрос не межсайтовый, потому что шрифты загружаются в анонимном режиме CORS (MDN, май 2026).

Предзагружайте только то, что нужно первому экрану. Web.dev предупреждает, что preload игнорирует объявления unicode-range и при разумном использовании применяется для загрузки одного формата шрифта. Для шрифтов, которые нужны не сразу, его пропустите. Preload входит в Baseline newly available с января 2021 года и widely available с июля 2023 года (web-features 3.44.0).

Google Fonts или свой хостинг

Для Google Fonts добавьте параметр display в URL таблицы стилей и заранее подключитесь к обоим источникам шрифтов:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Crimson+Pro&display=swap" rel="stylesheet">

Параметр display принимает те же пять значений, а в документации Google сказано, что по умолчанию стоит auto и что другое значение обычно уместнее (Google Fonts, обновлено в июле 2024). У preconnect для файлов шрифтов нужен crossorigin, потому что «в отличие от таблиц стилей, файлы шрифтов должны передаваться по соединению CORS» («unlike stylesheets, font files must be sent over a CORS connection», web.dev).

Что быстрее, Google Fonts или свой хостинг? Web.dev пишет, что разница «less clear cut», то есть не столь однозначна, как кажется: в данных Web Almanac, которые там приведены, сторонние шрифты отображались быстрее собственных, а свой хостинг скорее выигрывает вместе с CDN и HTTP/2. Если вы размещаете шрифты у себя, оптимизации лежат на вас: отдавайте WOFF2 (по данным web.dev, он сжимается на 30% лучше WOFF), делайте подмножества через unicode-range и сначала проверьте лицензию шрифта.

Убираем сдвиг раскладки запасным шрифтом

Сдвиг раскладки при подмене возникает потому, что метрики запасного и веб-шрифта различаются, а решение — подогнать запасной шрифт под веб-шрифт. В руководстве web.dev по CLS «fonts that render larger or smaller than its initial fallback» (шрифты, которые отображаются крупнее или мельче исходного запасного) названы причиной сдвигов раскладки, а хорошее значение CLS — 0,1 или меньше (web.dev, апрель 2023). Обо всех причинах подробнее — в руководстве по cumulative layout shift.

Заголовок узким запасным шрифтом занимает две строки; когда подгружается более широкий веб-шрифт, он занимает три, и кнопка под ним сдвигается вниз: это сдвиг макета

Инструмент — второй @font-face, который указывает на локальный шрифт и масштабирует его. size-adjust масштабирует контуры глифов и метрики на заданный процент, а ascent-override, descent-override и line-gap-override задают строку:

@font-face {
  font-family: "Poppins fallback";
  src: local("Arial");
  size-adjust: 60.85%;
}

body {
  font-family: "Poppins", "Poppins fallback", sans-serif;
}

Значение 60,85% относится к одной паре шрифтов, Poppins поверх Arial, поэтому своё нужно рассчитать. В статье Chrome сказано, что size-adjust — это средняя ширина символа веб-шрифта, делённая на среднюю ширину запасного (здесь 538,01 / 884,14 = 60,85%), что в большинстве случаев её можно только приблизить, а три дескриптора переопределения рассчитываются из подъёма, спуска и межстрочного интервала веб-шрифта, делённых на units per em и значение size-adjust. Рассчитайте их для своего шрифта одним из инструментов, которые перечислены в статье (обновлена в феврале 2023): Fontaine, Capsize, next/font (начиная с Next 13) или @nuxtjs/fontaine (Chrome for Developers), и проверьте результат в браузере, прежде чем на него полагаться.

Поддержка по дескрипторам различается, по MDN browser-compat-data 8.1.7 (октябрь 2026):

Дескриптор Chrome Firefox Safari
size-adjust 92 92 17
ascent-override, descent-override, line-gap-override 87 89 не поддерживается

Web-features не относит три переопределения к Baseline, поэтому Safari на iPhone их игнорирует и может всё же немного сдвигать содержимое. Запасной шрифт к тому же зависит от устройства: в статье Chrome отмечено, что для запасного Roboto на Android нужны другие значения, чем для Arial.

Влияние на текст LCP на телефонах

Веб-шрифты могут задерживать Largest Contentful Paint, если самый большой элемент — текст. В руководстве web.dev по LCP сказано, что текст считается отрисованным только после периода блокировки, поэтому LCP может считаться меньший элемент, пока не появится текст с веб-шрифтом; блочные текстовые элементы вроде <p> и <h1> считаются кандидатами в LCP, а хороший LCP — 2,5 секунды или меньше (web.dev, сентябрь 2025).

Если LCP страницы — заголовок в веб-шрифте, загрузка шрифта соревнуется со всем остальным на медленном телефонном соединении, поэтому swap или preload стоят усилий. Как найти элемент LCP, описано в руководстве по мобильному аудиту Lighthouse, а остальные причины медленного LCP разобраны в руководстве по тесту скорости мобильной страницы.

Проверьте результат на ширине телефона

После изменения посмотрите на страницу при холодной загрузке. Перезагрузите её с отключённым кэшем на ширинах телефонов и понаблюдайте, прыгает ли заголовок, когда приходит шрифт. Mobile View Extension перезагружает вкладку с user agent, размером экрана и pixel ratio телефона, так что вы видите мобильную раскладку и то, какой запасной шрифт появляется. Время загрузки, сдвиг раскладки и другие метрики скорости расширение не измеряет, а использует шрифты и соединение вашего десктопа, поэтому запасной шрифт на телефоне может выглядеть иначе. Измеряйте через Lighthouse и полевые данные.

Частые вопросы

Что делает font-display swap?

Он сразу показывает текст запасным шрифтом и заменяет его веб-шрифтом, когда тот загрузится, без ограничения на то, насколько поздно может произойти подмена. Период блокировки крайне короткий, а период подмены бесконечный (MDN).

Что лучше: font-display swap или optional?

Ни один не лучше везде: swap всегда заканчивается вашим веб-шрифтом, но может сдвинуть раскладку, а optional избегает сдвига и при медленной загрузке может пропустить веб-шрифт. Web.dev предлагает swap для фирменного стиля и optional для основного текста.

Вызывает ли font-display swap сдвиг раскладки?

Может. Когда метрики запасного и веб-шрифта различаются, подмена меняет размер текста и высоту строки и может сдвинуть содержимое; web.dev называет это причиной сдвигов раскладки. Подгонка запасного шрифта через size-adjust его уменьшает.

Нужно ли предзагружать веб-шрифты?

Предзагружайте один-два шрифта, которые нужны первому экрану, с as="font" и crossorigin. Web.dev советует пользоваться этим умеренно, потому что preload игнорирует unicode-range.

Откройте следующий сайт в мобильном виде.

Установите расширение и запускайте превью устройств прямо в той вкладке, где уже работаете.