Привет, Хабр! Меня зовут Артём Поморцев. Я фронтенд-разработчик компании «Криптонит» и хочу поделиться своим опытом создания набора иконок (icon pack).

Скажу сразу, что иконочные шрифты не всегда являются наиболее эффективным и надёжным вариантом, но они по-прежнему относительно просты и легки в реализации.

Проблема

Наша проблема заключалась в том, что мы переходили на новый дизайн. Тогда мы работали с фреймворком blueprint.js, и он не всегда отвечал требованиям UX-дизайнеров. Также было желание сделать собственный пакет иконок, который можно было бы легко дополнять и изменять по мере необходимости.

Решение

Первоначально мы попробовали использовать другие пакеты иконок, такие как FontAwesome, Vue-Unicons и HeroIcons. Однако дизайнерам они тоже не подошли, поэтому мы решили подготовить собственный пакет иконок. Поначалу хотелось найти готовое программное решение, в которое можно добавить файлы SVG и билдить со спокойной душой. Этого не нашлось, поэтому мы решили собрать его из двух библиотек: svgtofont и SVGO.

1) svgtofont

svgtofont — это библиотека для генерации иконочных шрифтов из файлов SVG. Документация объёмная, поэтому расскажу лишь о самых интересных возможностях:

  • outSVGReact — передача из SVG в React-компонент;

  • outSVGReactNative — передача из SVG в компонент React Native;

  • outSVGPath — возможность конвертации SVG в CSS-полигон;

  • startUnicode — код символа, с которого стартует шрифт;

  • useCSSVars — возможность использования CSS-переменных.

Также svgtofont поддерживает свойства svgicons2svgfont.

Быстрая настройка для проекта (package.json)

С svgtofont возникли проблемы. Главная из них состояла в том, что иконки заполнялись, когда не следует, или вообще не отображались (об этом расскажу ниже).

2) SVGO

SVGO — это оптимизатор SVG, избавляющий файл от метаданных — обычно ненужной информации, которая заложена в различных дизайн-тулзах. Поэтому первым делом я решил использовать его, чтобы решить проблемы с пустыми иконками, а заодно уменьшить размер файлов SVG. В SVGO можно указывать, что вам конкретно нужно убрать, начиная от удаления декларации XML, до атрибутов. В нём есть работа со стилями и классами, поэтому SVGO подойдёт для всех уровней оптимизации. Подробнее ознакомиться с его возможностями вы можете в документации.

Быстрая настройка для проекта (package.json)

SVGO запускает под капотом оптимизацию файлов.

--folder ./svg — указывается входная папка.

-o или --output — выходной файл или папка (по умолчанию такая же, как и входная).

./svg_generated — папка с оптимизированными SVG-файлами.

 

С SVGO тоже возникла проблема. При билде или запуске проекта каждый раз генерировался новый иконочный шрифт. Он менял свой идентификатор, поэтому при переходе на новую версию ломались иконки. Эта проблема решилась быстро. Мы просто поменяли конечную папку, в которую записали оптимизированные файлы SVG для того, чтобы не было перезаписи исходных.

В результате применения SVGO размер пакета уменьшился на 19%

Так в чём же была проблема с отображением иконок?

Причина этой проблемы — алгоритм, используемый для определения внутренней части фигуры. Алгоритм задаётся атрибутом fill-rule тега <path> внутри SVG. Значение по умолчанию: «nonzero». Есть ещё одно значение «evenodd», которое поддерживается редко.

Если вы просто удалите атрибут, направление пути d будет таким же. Поэтому нужно изменить направление путей самостоятельно. Вот как решить эту проблему:

1. Скачайте Fill Rule Editor:

2. Перетащите или вставьте значок SVG, шрифт которого выглядит странно, и откройте плагин:

3. Нажимаем на SVG:

4. А теперь, чтобы это исправить, нажмите на внутренний или внешний контур:

Проблема решена!

Полный билд сборки:

Результат:

Итоги:

Размер упакованного пакета: 485 Кб
Размер проекта после распаковки: 1.75 Мб
Количество файлов SVG: 400

Комментарии (19)


  1. pikus_spb
    21.05.2024 07:19
    +2

    Интересная статья, может пригодиться, спасибо!


    1. Tema23noni Автор
      21.05.2024 07:19

      Спасибо большое)


  1. copyhold
    21.05.2024 07:19
    +1

    Fontawesome в платной версии позволяет создавать пакет из своих иконок.
    Есть также API . Что мы у себя и используем.


    1. vagon333
      21.05.2024 07:19
      +1

      Что мы у себя и используем.

      По вашему опыту, насколько легко сконвертировать 3к+ SVG?
      У меня лежит архив натыбреных иконок в SVG формате, которые я мечтаю перевести в мега-шрифтовый файл.
      Пытался как автор. Получилась фигня.


      1. Tema23noni Автор
        21.05.2024 07:19

        Спасибо за комментарий, а что именно не получилось?


    1. Tema23noni Автор
      21.05.2024 07:19

      Круто, спасибо)
      Не знал, что у Fontawesome есть такая возможность)


  1. ImagineTables
    21.05.2024 07:19
    +1

    А зачем это всё?

    Шрифты удобны как контейнер — сделал ссылку, и сразу пиши в нужных местах <i class="…". И голова не болит. Но если лезть в пучины SVG и разбираться с оптимизациями, не лучше ли оптимизированный код поместить в начале <body> вот в такой контейнер:

    <svg version="1.1" xmlns="http://www.w3.org/2000/svg"
    	xmlns:xlink="http://www.w3.org/1999/xlink" class="template">
    	<defs>
    		<symbol id="icon1" class="icon1" viewBox="…">
    			…
    		</symbol>
    		<symbol id="icon2" class="icon2" viewBox="…">
    			…
    		</symbol>
    		<symbol id="icon3" class="icon3" viewBox="…">
    			…
    		</symbol>
    	</defs>
    </svg>
    

    , а затем юзать вместо <i> в нужных местах:

    <svg>
    	<use href="#icon1"></use>
    </svg>
    

    ?

    Заодно, хоть это уже и не сильно актуально, число запросов уменьшится.

    А если на стороне сервера есть шаблонизатор, то всю суб-библиотеку иконок можно «заказывать» из общей библиотеки, перечисляя идентификаторы, нужные на данной странице.


    1. pikus_spb
      21.05.2024 07:19
      +1

      На мой взгляд, через шрифты код получается чище. Мы до шрифтов еще не дошли - по всему проекту разбросаны свгшки, логичным следующим этапом выглядит создание своего шрифта.


      1. NickyX3
        21.05.2024 07:19
        +2

        Проблема шрифтов - их нельзя делать цветными, то есть красится через стиль все целиком. Вернее (как у гугла) их можно сделать разноцветными, но не перекрашивать через CSS. SVG спрайты могут содержать что угодно, и цветные иконки, и перекрашиваемые через fill="currentColor". Ну а сами спрайты можно вгружать в body как статику из файлов через JS (они еще и браузером кэшируются).

        Другая проблемка шрифтовых иконок, особенно подвязанных через <i> - это инлайновость элемента по-определению, контроль за его размером через CSS это отдельная боль, тут и геометрия и font-size, и что начнется, если оно не очень совпадает и имееет прозрачный фон лучше не показывать. В этом смысле <svg><use> гораздо удобнее


        1. Tema23noni Автор
          21.05.2024 07:19

          Да, есть проблемы со шрифтовыми иконками, соглашусь, но есть случаи, когда удобнее использовать иконочный шрифт.

          Например: Если нужно использовать много иконок без изменений стиля

          У нас было предложено оставить иконочный шрифт по причине того, что во многих проектах используют именно такой подход и людям будет привычнее работать именно с ним


          1. NickyX3
            21.05.2024 07:19

            Да тут как удобнее, у нас в одном проекте используются шрифты, благо иконок там не сказать чтоб сильно много, они разбиты по разным шрифтам пачками по десятку-двум штук (ну например по разделам сайта) и собраны в https://icomoon.io/. Но добавление иконок или правка по факту это обновление шрифта полностью, и перегрузка его на клиентах может происходить довольно долго из кешей браузера, если специально ему не выставлять что-то типа номера версии, но это опять же еще и править стили с font-face.

            В другом проекте для новой верстки мы использовали svg-спрайты. Это проще, меняется только спрайт-файл, который в текстовом формате svg по факту и его проще править, проще автоматически генерировать и т.п.

            Еще одна особенность шрифтов в том, что в разных OS растрирование шрифтов таки разное, более того, даже в Windows оно зависит от настроек CrearType, и для не особо больших размеров иконок это может давать забавные артефакты (например под OSX/MacOS шрифты рендерятся чуть жирнее, а в Linux вообще технология сглаживания отличная от всего). SVG же практически одинаково рендерятся на разных платформах


        1. pikus_spb
          21.05.2024 07:19

          Ну это спорное утверждение. CSS как и раз и создан чтобы делать всё стильным)). Мне вот кажется что шрифты как раз убоднее раскрашивать чем каждый SVG в отдельности, так как структура у каждого SVG разная. У нас вот появилась задача "брендирование" - создание цветовой схемы под конкретного клиента. В этом контексте перекрасить иконки шрифта выглядит гораздо проще.


          1. NickyX3
            21.05.2024 07:19

            шрифты как раз убоднее раскрашивать чем каждый SVG в отдельности

            Зачем каждый SVG раскрашивать? В спрайте у path fill="currentColor" и всё - иконка вставленная из спайта через <svg><use href="id"></use></svg> прекрасно красится через CSS color так же как и шрифтовая иконка


        1. ImagineTables
          21.05.2024 07:19

          Проблема шрифтов - их нельзя делать цветными, то есть красится через стиль все целиком.

          Да, это частный случай общей проблемы — текст стилизуется ограниченно, а SVG без ограничений.

          Причём, до смешного доходит. Почему не привязать в CSS'е, который идёт со шрифтом, толщину линий в иконках к font-weight? (Соответственно, референсить разные автогенерируемые woff'ы). Это же так и напрашивается. А я подобного не видел.


          1. NickyX3
            21.05.2024 07:19

            Вы и сейчас можете так сдеkать. Два разных шрифта (MyIcons-Normal, MyIcons-Bold) с иконками с разными толщинами линий делаете и пишете CSS типа такого

            @font-face {
               font-family: 'MyIcons';
                  src: url('MyIcons-Normal.eot');
                  src: url('MyIcons-Normal.eot?#iefix') format('embedded-opentype'),
                       url('MyIcons-Normal.woff') format('woff'),
                       url('MyIcons-Normal.ttf') format('truetype'),
                       url('MyIcons-Normal.svg#UbuntuItalic') format('svg');
               font-weight: 400;
               font-style: normal;
            }
            
            @font-face {
               font-family: 'MyIcons';
                  src: url('MyIcons-Bold.eot');
                  src: url('MyIcons-Bold.eot?#iefix') format('embedded-opentype'),
                       url('MyIcons-Bold.woff') format('woff'),
                       url('MyIcons-Bold.ttf') format('truetype'),
                       url('MyIcons-Bold.svg#UbuntuBold') format('svg');
               font-weight: 700;
               font-style: normal;
            }

            И соответственно все что через верстку или css назначено font-weight: 700 - будет соответственно из другого шрифта, более "жирного"


            1. ImagineTables
              21.05.2024 07:19

              Я-то конечно могу )) В дикой природе такого не видно. Почему-то.


              1. NickyX3
                21.05.2024 07:19

                Ну в принципе этот вопрос бывает всплывает, но да, просто никто не хочет изменять иконки для разных начертаний обычно.


    1. Tema23noni Автор
      21.05.2024 07:19
      +1

      Спасибо за комментарий

      Было решено оставить именно использование <i class=>, чтобы поддержать легаси, т.к. на проектах уже был использован такой формат.


      1. ImagineTables
        21.05.2024 07:19
        +1

        Всегда пожалуйста. Рад, если было полезно.

        Просто я раньше разделял расхожее заблуждение, что SVG это формат файла. Пользовался им примерно как .png'шками, имел с этого много проблем, и предпочитал иконочные шрифты.

        Но в какой-то момент до меня дошло, что SVG правильно рассматривать как язык разметки (ну или хотя бы как один из разметочных DSL'ей). То есть, оптимизировать от шлака (могу для автоматизации посоветовать сервис Nano, хоть он в России больше не работает), использовать переменные, делать каскадные библиотеки, ну и вообще — читать код. Так я пришёл к решению, описанному в комментарии, а иконочные шрифты для меня с тех пор стали… как вы выразились, «поддержать легаси» ))