Иконочные шрифты против SVG: что выбрать и почему
Иконочный шрифт грузит весь набор глифов ради пяти иконок, ломается при сбое загрузки шрифта и плохо дружит со скринридерами. Разбираем, где SVG выигрывает, а где иконочный шрифт всё ещё уместен.

Вы подключаете Font Awesome ради шести иконок в шапке — и тянете файл на сотни килобайт со всем набором глифов. При медленном соединении иконки моргают: сначала пустое место или квадрат-заглушка, потом шрифт догружается и всё встаёт на место. Плюс скринридер иногда зачитывает приватную область Unicode как случайные символы. Разберём, когда иконочный шрифт всё ещё оправдан, а когда пора переходить на SVG, и чем именно вы платите за каждый выбор.
Задача и почему очевидное решение даёт проблему
Иконочный шрифт кажется удобным: подключил CSS, поставил класс <i class="icon-cart"> — и иконка масштабируется через font-size, красится через color. Один запрос, единый способ управления. Так работали интерфейсы примерно с 2012 по 2018 год, и наследие этого подхода до сих пор в тысячах проектов.
Проблема в механике рендеринга. Браузер обращается с иконкой как с текстом: помещает глиф в PUA1 — приватную область Unicode, — и рисует его как символ шрифта. Отсюда цепочка неприятностей:
- FOIT/FOUT. Пока шрифт не загружен, вместо иконок либо пусто (Flash of Invisible Text), либо системный запасной символ (Flash of Unstyled Text). На мобильном 3G это заметно.
- Весь набор ради нескольких иконок. Классический CDN-набор — это один файл со всеми глифами. Даже если вы используете десять иконок, браузер качает шрифт целиком, если вы не собираете подмножество вручную.
- Сглаживание и позиционирование. Иконка-глиф выравнивается по базовой линии текста, а не по геометрическому центру. Отсюда вечная возня с
vertical-alignиline-height. - Доступность. Символ из PUA скринридер может прочитать как мусор. Приходится вешать
aria-hidden="true"на каждую иконку и дублировать смысл текстом. - Только один цвет. Глиф закрашивается свойством
color. Двухцветную или градиентную иконку шрифтом не сделать без хаков со стекированием слоёв.
Четыре подхода к SVG-иконкам
SVG — это вектор в разметке, а не символ в шрифте. Но у SVG несколько способов доставки, и у каждого своя цена.
1. Инлайн-SVG прямо в HTML
Разметка иконки вставляется в документ как есть.
<button aria-label="Добавить в корзину">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M6 6h15l-1.5 9h-12z" stroke="currentColor" stroke-width="2"/>
<circle cx="9" cy="20" r="1" fill="currentColor"/>
<circle cx="18" cy="20" r="1" fill="currentColor"/>
</svg>
</button>
stroke="currentColor" заставляет иконку наследовать цвет текста — то же удобство, что и у шрифта. Плюс никакого лишнего запроса: иконка уже в HTML. Минус — разметка раздувается, если одна и та же иконка встречается двадцать раз на странице.
2. SVG-спрайт с <use>
Все иконки собираются в один файл-спрайт с <symbol>, а на странице подключаются по ссылке.
<!-- Один спрайт, подключается один раз -->
<svg style="display:none">
<symbol id="icon-cart" viewBox="0 0 24 24">
<path d="M6 6h15l-1.5 9h-12z" stroke="currentColor" stroke-width="2" fill="none"/>
</symbol>
</svg>
<!-- Использование -->
<svg width="24" height="24" aria-hidden="true">
<use href="#icon-cart"></use>
</svg>
Определение иконки одно, ссылок сколько угодно. Ближе всего по эргономике к иконочному шрифту, но без символов из PUA. Внешний спрайт кешируется отдельным файлом.
3. SVG через <img> или CSS background
Подходит для декоративных иконок, которыми не надо управлять из CSS. Проще всего, но currentColor не работает: цвет фиксирован внутри файла, перекрасить его снаружи нельзя.
4. SVG-компоненты во фреймворке
В React, Vue, Svelte иконки часто импортируются как компоненты через сборщик (например, плагином SVGR для React). Дерево вырезает неиспользуемые иконки при tree-shaking, в бандл попадает только то, что реально импортировано.
Сравнение
| Критерий | Иконочный шрифт | Инлайн-SVG | SVG-спрайт |
|---|---|---|---|
| Лишний вес | Весь набор глифов | Только нужные иконки | Только нужные иконки |
| FOIT/FOUT | Есть | Нет | Нет |
| Наследование цвета | Через color | Через currentColor | Через currentColor |
| Много цветов в иконке | Нет | Да | Да |
| Доступность из коробки | Плохая (PUA) | Хорошая | Хорошая |
| Раздувание HTML | Нет | Да, при повторах | Нет |
| Отдельный кеш файла | Да (шрифт) | Нет | Да (внешний спрайт) |
Иконочный шрифт превращает картинку в букву. Все проблемы шрифта — задержка загрузки, приватная область Unicode, выравнивание по базовой линии — вы получаете в довесок к иконке бесплатно.
Когда что выбирать
- Инлайн-SVG — когда иконок немного и каждая уникальна или встречается пару раз. Ноль лишних запросов, полный контроль над стилями, лучшая доступность.
- SVG-спрайт — когда одни и те же иконки повторяются по странице десятки раз (списки, таблицы, карточки товаров). HTML не раздувается, определение одно.
- SVG-компоненты — когда проект уже на сборщике с фреймворком. Tree-shaking сам выкинет неиспользуемое.
- Иконочный шрифт — когда вы правите легаси, где он уже вшит, и переезд не окупается. Или когда нужен ровно тот набор, что даёт готовая библиотека, а времени на сборку своего спрайта нет. Это не «никогда», это «по остаточному принципу».
Что ломается
Переход на SVG не бесплатен, и вот типичные грабли.
Внешний спрайт и CORS
Если спрайт лежит на другом домене или CDN, <use href="https://cdn.example.com/sprite.svg#icon"> может не отрисоваться из-за политики безопасности. Держите спрайт на своём origin или инлайньте его в документ.
Кеширование внешнего спрайта
Ссылка вида <use href="/sprite.svg#icon"> заставляет браузер запросить файл. Пока он не загружен, иконки не видны — это тот же FOIT, только для SVG. Инлайновый спрайт в теле HTML этого лишён.
Забытый aria-label
Декоративной иконке ставят aria-hidden="true". Но если иконка — единственное содержимое кликабельного элемента (кнопка-корзина без подписи), кнопке нужен aria-label, иначе скринридер прочитает пустую кнопку. Это частая ошибка при переезде со шрифта, где текст рядом обычно был.
Fill против stroke
Иконки бывают заливочные (fill) и контурные (stroke). Если хотите красить через currentColor, проверьте, на каком свойстве иконка нарисована, — иначе перекраска не сработает и вы будете гадать, почему цвет не меняется.
Мусорные атрибуты из редактора
SVG, экспортированный из Figma или Illustrator, тащит лишние id, метаданные, инлайновые стили и жёстко прописанные цвета. Прогоняйте иконки через оптимизатор SVGO, иначе спрайт распухнет и currentColor перебьётся зашитым fill.
Что делать дальше
Если сейчас у вас иконочный шрифт, не переписывайте всё разом. Соберите список реально используемых иконок (в DevTools видно, какие классы встречаются), выгрузите их SVG-исходники, прогоните через SVGO и соберите спрайт. Дальше замерьте: откройте вкладку Network в DevTools с троттлингом Slow 3G и сравните, когда иконки появляются на экране до и после. Смотрите на два показателя — вес переданных байтов и момент, когда иконки становятся видны. Инвалидацию кеша спрайта при обновлениях — отдельная тема, здесь её не разворачиваем.
Аналитик звонков: Разбор записи звонка по этапам сделки: где сорвалось, что упущено, три исправляемые рекомендации с цитатами.
Спросить за 90 ₽Источники: MDN: SVG element <use>, SVGO — SVG Optimizer (GitHub)
Частые вопросы
А если у меня старый браузер — SVG-спрайт с use сработает?
Иконки не перекрашиваются через currentColor — что не так?
Font Awesome же удобный — правда ли, что от него надо отказываться?
Инлайн-SVG раздувает HTML — это не вредит скорости?
Как быстро понять, стал ли сайт легче после перехода?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.