SVG в вёрстке: спрайты, иконки и анимация без лишних байтов
Как вставлять SVG-иконки так, чтобы их можно было красить через CSS, собирать в спрайт и анимировать — и где каждый подход ломается.

Иконка в вёрстке кажется пустяком, пока их не станет двести. И тогда всплывают неприятные вопросы: почему <img> нельзя перекрасить через CSS, почему инлайновый SVG раздувает HTML на десятки килобайт, а спрайт из <symbol> не хочет анимироваться. Ниже — четыре способа вставить SVG на страницу, с ценой каждого, и что учесть при анимации. Актуально для любых современных браузеров (проверено поведение в Chrome, Firefox, Safari на конец 2024 года).
Четыре способа вставить SVG
Один и тот же файл иконки можно подключить минимум четырьмя способами, и они не взаимозаменяемы. Разница в том, доступен ли вам DOM иконки, можно ли красить её из CSS и сколько это стоит в байтах и запросах.
1. Через <img>
Самый простой вариант: <img src="icon.svg" alt="">. Браузер рисует картинку, файл кешируется, HTML остаётся чистым. Но SVG попадает в замкнутый контекст: вы не достучитесь до его внутренних элементов ни из CSS страницы, ни из JS. Значит, fill и stroke задаются только внутри самого файла. Перекрасить иконку под наведение курсора через :hover не выйдет.
Обходной приём — красить через filter, но это боль: чистый цвет получается только подбором значений вроде invert() и sepia(), и точный HEX так не задать.
2. Через background-image в CSS
Тот же принцип, что и <img>: SVG изолирован. Плюс в том, что можно держать иконку декоративной и не засорять разметку. Красить извне тоже нельзя. Но есть трюк — data-URI с параметром цвета, если вы генерируете CSS сборщиком и подставляете fill прямо в закодированный SVG.
3. Инлайновый SVG прямо в HTML
Вставляете весь <svg>...</svg> в разметку. Тогда все его пути — часть DOM страницы. Красите через CSS, анимируете через CSS или JS, доходите до отдельного <path>. Цена: иконка не кешируется отдельно и раздувает HTML. Если одна и та же иконка встречается на странице сто раз, её код повторится сто раз.
4. Спрайт из <symbol> + <use>
Собираете все иконки в один файл со <symbol id="...">, а на странице ссылаетесь через <use>:
<!-- один раз в начале body, скрытый спрайт -->
<svg width="0" height="0" style="position:absolute">
<symbol id="icon-cart" viewBox="0 0 24 24">
<path d="M7 4h14l-2 9H9L7 4Z" fill="currentColor"/>
</symbol>
</svg>
<!-- использование в любом месте -->
<svg class="icon">
<use href="#icon-cart"></use>
</svg>
Определение иконки встречается один раз, дальше идут короткие ссылки. Это лучший компромисс для большого набора иконок.
Как красить иконку из CSS
Ключевое слово — currentColor. Если внутри SVG у путей задано fill="currentColor", иконка наследует значение свойства color от родителя. Так одна иконка меняет цвет в разных контекстах без правки файла:
.icon {
width: 24px;
height: 24px;
color: #555; /* цвет иконки в покое */
}
.button:hover .icon {
color: #0a84ff; /* цвет при наведении на кнопку */
}
Для <use> есть тонкость: стили страницы проникают в клонированное содержимое только если внутри <symbol> использовано currentColor или CSS-переменные. Жёстко прописанный в символе fill="#333" через внешний CSS вы не перебьёте.
Правило, которое экономит часы: в исходниках иконок замените все конкретные цвета на currentColor ещё на этапе сборки спрайта. Тогда красить будет CSS страницы, а не каждый файл по отдельности.
Двухцветные иконки через переменные
Если у иконки два цвета, currentColor хватит только на один. Второй задают через CSS-переменную:
<symbol id="icon-bell" viewBox="0 0 24 24">
<path d="..." fill="currentColor"/>
<circle cx="18" cy="6" r="4" fill="var(--accent, red)"/>
</symbol>
.icon { color: #333; --accent: #ff3b30; }
Сравнение способов
| Способ | Красится из CSS | Кешируется отдельно | Вес HTML | Когда брать |
|---|---|---|---|---|
| <img> | Нет | Да | Минимум | Одиночная цветная иллюстрация |
| background-image | Нет | Да | Минимум | Декоративный фон, псевдоэлементы |
| Инлайновый SVG | Да | Нет | Высокий при повторах | Уникальная иконка с анимацией |
| <symbol> + <use> | Да, с оговорками | Спрайт целиком | Низкий | Большой набор монохромных иконок |
Анимация SVG
Анимировать можно только то, до чего есть доступ из CSS или JS — то есть инлайновый SVG или содержимое <use>. Иконку в <img> анимируют лишь целиком, как обычную картинку (поворот, масштаб через transform), но не её внутренние пути.
CSS-анимация: transform и opacity
Самое дешёвое, что рисует браузер без пересчёта геометрии, — transform и opacity. Спиннер загрузки — классика:
.spinner {
animation: spin 1s linear infinite;
transform-origin: center;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
Одна оговорка: transform-origin: center у SVG-элементов ведёт себя не так, как у HTML-блоков. Firefox исторически считал точку отсчёта от угла системы координат SVG, а не от центра фигуры. Если вращение «уезжает», задайте начало координат явно в единицах: transform-origin: 12px 12px для иконки с viewBox="0 0 24 24".
Анимация рисования линии: stroke-dasharray
Эффект «линия прочерчивается сама» — на свойствах stroke-dasharray и stroke-dashoffset. Приём: делаете пунктир длиной во всю линию, сдвигаете его целиком за пределы, потом возвращаете:
.checkmark path {
stroke-dasharray: 40; /* примерная длина пути */
stroke-dashoffset: 40;
animation: draw 0.5s ease forwards;
}
@keyframes draw {
to { stroke-dashoffset: 0; }
}
Число 40 — не магия, а длина контура. Точное значение берут из JS-метода getTotalLength() у элемента пути, а не подбирают на глаз, иначе линия либо не дорисуется, либо оборвётся.
Морфинг формы
Плавный переход одной фигуры в другую (d атрибута <path>) чистым CSS в большинстве браузеров не анимируется надёжно. Здесь нужна JS-библиотека, которая интерполирует координаты между двумя контурами. Морфинг — отдельная тема, здесь разбираем только штатные CSS-приёмы.
Где всё это ломается
- Кросс-доменный <use>. Ссылка
hrefна внешний файл спрайта работает только в пределах того же источника или при правильных CORS-заголовках. Если спрайт лежит на CDN без нужных заголовков, иконки просто не появятся, а в консоли будет тишина или ошибка загрузки. - currentColor не пробивается в <img>. Иконка, подключённая картинкой, никогда не возьмёт цвет родителя. Если нужен цвет из CSS — только инлайн или
<use>. - Анимация в <img> внутри SVG. SMIL-анимация (
<animate>внутри файла) в картинке через<img>в некоторых браузерах не проигрывается или замирает. Надёжнее CSS-анимация на инлайновом SVG. - Раздутый HTML. Сотня инлайновых иконок на странице — это лишние килобайты в каждом ответе сервера, которые не кешируются. Для повторяющихся иконок берите спрайт.
- Доступность. Декоративной иконке ставьте
aria-hidden="true", а смысловой —<title>внутри SVG илиaria-labelна обёртке. Пустой<svg>без подписи скринридер прочитает как непонятный графический объект.
Что делать дальше
Соберите иконки в один спрайт сборщиком (svgo для чистки, любой плагин сборки для склейки <symbol>), прогоните исходники через оптимизатор и замените жёсткие цвета на currentColor. Дальше смотрите на два показателя: размер HTML-ответа в DevTools (Network) и число запросов за иконками. Если иконок больше десяти и они повторяются — переход со <img> на спрайт заметно сократит и то, и другое. Для сложной анимации формы изучайте специализированные JS-библиотеки морфинга — CSS здесь предел возможностей.
Редактор изображений: Убрать лишнее, заменить фон, добавить объект, расширить кадр — на вашем изображении.
Спросить за 20 ₽Источники: SVG: Scalable Vector Graphics — MDN Web Docs, SVGGeometryElement.getTotalLength() — MDN
Частые вопросы
Почему иконка через <img> не красится по <hover>?
У меня иконки не появляются при <use href="sprite.svg#id">, консоль молчит.
Вращение иконки через transform уезжает в сторону, а не крутится по центру.
Как узнать точное значение для stroke-dasharray?
Работает ли всё это в старых браузерах?
Спрайт или инлайн — что выбрать для проекта с сотней иконок?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.