SVG спрайты. Как правильно готовить?
Во время рефакторинга одного древнего проекта оказалось, что в нём иконки на страницы добавлялись 5-ю различными способами, от инлайновой вставки SVG до иконочных шрифтов. Для этого были написаны 3 Gulp задачи. Так как стояла задача рефакторинга, я решил унифицировать способ вставки иконок. Сначала хотел использовать библиотеку символов, но этот популярный метод создания SVG спрайтов имеет один серьёзный недостаток — он не умеет работать с фоновыми изображениями. Сейчас мы исправим этот недостаток.
В этой статье я не буду рассматривать классический способ создания спрайтов, так как он хорошо всем известен. SVG символы затрону лишь кратко и остановлюсь на той части, которая позволяет реализовать вставку фоновых иконок.
В чём проблема классических спрайтов?
Классический спрайт представляет собой один файл (SVG или растровый) с размещёнными на нём иконками. Выборку используемой иконки предлагается делать используя позиционирование фонового изображения.
В чём недостатки такого способа?
- Нужны дополнительные инструменты, чтобы создать комплексное изображение (онлайн или в виде npm пакета).
- Нельзя менять цвет иконок в CSS.
- Нельзя получить доступ к иконкам из JavaScript.
Часть этих недостатков компенсируется использованием библиотеки SVG символов.
Вкратце напомню. В файл SVG добавляются иконки в теге <symbol>
.
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="cart" viewBox="0 0 32 32">
<path fill="currentcolor" d="M21.65…" />
</symbol>
<symbol id="user" viewBox="0 0 32 32">
<path fill="currentcolor" d="M18.34…" />
</symbol>
</svg>
В темплейте иконка выводится в виде инлайнового SVG со ссылкой на ID символа.
<svg viewBox="0 0 32 32" width="32" height="32" aria-hidden="true">
<use href="sprite.svg#cart" />
</svg>
Достоинства таких спрайтов:
- Можно менять стили из CSS.
- Можно использовать JavaScript.
- Можно использовать CSS Custom Properties (CSS переменные).
- Назначив
currentColor
вместо цвета, можно управлять цветом из CSS по свойствуcolor
. - Возможность полностью контролировать SVG, вырезая из него ненужное для облегчения веса.
- Возможность использовать несколько спрайтов для разных секций сайта, не переписывая задачи для Gulp/Webpack.
Единственный недостаток SVG символов, как уже указывалось — отсутствие поддержки фоновых картинок. И тут к нам на помощь приходит технология SVG Stacks.
SVG Stacks — решение проблемы
В темплейте спрайт выводится тем же образом как и в SVG символах.
<svg viewBox="0 0 32 32" width="32" height="32" aria-hidden="true">
<use href="sprite.svg#cart" />
</svg>
В файле спрайта начинаются перемены. Первым делом заменяем <symbol>
на <svg>
. Да, именно так, SVG внутри SVG, как в фильме Нолана «Начало». Таким образом у нас есть внешний SVG и внутренние обёртки.
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<style>
:root svg:not(:target) {
display: none;
}
</style>
</defs>
<svg id="cart" viewBox="0 0 32 32">
<path fill="currentcolor" d="M21.65…" />
</svg>
<svg id="user" viewBox="0 0 32 32">
<path fill="currentcolor" d="M18.34…" />
</svg>
</svg>
Мы добавляем display: none
, т. к. все внутренние SVG будут отображаться по умолчанию. Используя псевдоэлемент :target
мы отображаем только текущую иконку, к которой обращаемся по ID. Всё остальное ничем не отличается от использования SVG символов.
Фоновые изображения
А теперь самое интересное. Как вывести иконки фоновым изображением?
a {
background-image: url('sprite.svg#cart');
}
Единственное ограничение такого подхода, в том, что мы не можем явно поменять цвет заливки иконки таким образом:
a {
background-image: url('sprite.svg#cart');
}
a:hover {
fill: 'darkgray';
}
Для этого нам нужно создать «экземпляры» иконки внутри спрайта:
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<style>
:root svg:not(:target) {
display: none;
}
</style>
<!-- Иконка -->
<path id="cart" d="M21.65…" />
</defs>
<!-- «Экземпляр» иконки -->
<svg id="cart-default" viewBox="0 0 32 32">
<use href="#cart" fill="gray" />
</svg>
<!-- «Экземпляр» иконки на ховере -->
<svg id="cart-hover" viewBox="0 0 32 32">
<use href="#cart" fill="darkgray" />
</svg>
</svg>
И теперь можно вывести фоновые иконки:
a {
background-image: url('sprite.svg#cart-default');
}
a:hover {
background-image: url('sprite.svg#cart-hover');
}
Прозрачные области в Firefox
Если в иконке есть прозрачные области, в браузере Firefox курсор может пропускать их на ховере. Поэтому добавляем прозрачный квадрат вокруг иконки.
<svg id="cart" viewBox="0 0 32 32">
<rect width="32" height="32" fill-opacity="0" />
<path fill="currentcolor" d="M21.65…" />
</svg>
Я думаю эти два недостатка с лихвой компенсируют содержание в проекте зоопарка из нескольких методов создания спрайтов.
Ссылки: