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 мс | Нет | Основной текст, где стабильная раскладка важнее точного шрифта |

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.