Como trocar a cor de um ícone svg?

Como funcionam as bibliotecas de ícones? Como construir uma biblioteca de ícones? Usar svg como arquivo ou no html?

TL;DR

Como trocar a cor de um svg? R: Troque o atributo fill do svg. Como trocar a cor de um svg pelo css? R: Atribua uma classe para o svg, defina o atributo fill com a cor desejada.

Você terá algo assim:

<style>
  .circle {
    fill: black;
  }
</style>
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 50 50">
  <circle r="12" cx="25" cy="25" fill="red" class="circle" />
</svg>

Como construir uma biblioteca de ícones e chamar o svg por meio do css? R: Leia Como construir uma biblioteca de ícones

Contexto

Se você já pesquisou por essa pergunta deve ter tido como resposta essa mensagem abaixo:

Resposta StackOverflow

Acontece que a resposta mais votada não extingue todas as possibilidades.

Por esta razão estou escrevendo este artigo com o objetivo de mostrar mais algumas possibilidades pra você perder seu tempo.

Como construir uma biblioteca de ícones

Criação de um SVG

Primeiramente você vai precisar de uma ou várias imagens, em svg caso não esteja claro. Vamos pegar como exemplo esta imagem de um elfo (qualquer semelhança com o spock é apenas coincidência):

Elf

Recomenda-se fortemente que você utilize uma grade para construção dos seus ícones. Dessa forma você não terá problemas com ícones em tamanhos diferentes. Se você for um artista, o que não é o meu caso, você poderá utilizar o Inkscape como uma ferramenta para construção do seu ícone.

Não se esqueça de colocar um id/nome pra cada layer, afinal nós devs colocamos nomes em tudo. Na verdade, isso será útil se o seu ícones tiver/poder ter mais de uma camada de cor.

Se você tiver sucesso irá resultar em um arquivo com um cabeçalho parecido com o desse arquivo:

<svg
   version="1.2"
   width="125.27128mm"
   height="143.33466mm"
   viewBox="0 0 12527.128 14333.466"
   preserveAspectRatio="xMidYMid"
   fill-rule="evenodd"
   stroke-width="28.222"
   stroke-linejoin="round"
   xml:space="preserve"
   id="svg3528"
   sodipodi:docname="Elf.svg"
   inkscape:version="1.2.2 (732a01da63, 2022-12-09, custom)"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns:ooo="http://xml.openoffice.org/svg/export"><sodipodi:namedview
   id="namedview3530"
   pagecolor="#ffffff"
   bordercolor="#000000"
   borderopacity="0.25"
   inkscape:showpageshadow="2"
   inkscape:pageopacity="0.0"
   inkscape:pagecheckerboard="0"
   inkscape:deskcolor="#d1d1d1"
   inkscape:document-units="mm"
   showgrid="false"
   inkscape:zoom="0.96102398"
   inkscape:cx="150.36045"
   inkscape:cy="227.88193"
   inkscape:window-width="1920"
   inkscape:window-height="1031"
   inkscape:window-x="0"
   inkscape:window-y="25"
   inkscape:window-maximized="1"
   inkscape:current-layer="layer1" />

Parece bem inútil, certo? Não sei, eu não entendo de qualquer forma. Mas como desenvolvedor sei que ícones não precisam desse cabeçalho. Logo...

Removendo o cabeçalho

Você poderá utilizar algo como isto SVG OMG que irá otimizar seu svg, removendo itens desnecessários, caso seja você a realizar essa tarefa leia Best practices for working with SVGs que é uma Fonte deste artigo.

E aqui está o nosso elfo com as otimizações realizadas. Tomei a liberdade de colocar nomes nas camadas e trocar algumas cores padrão.

Elf otimizado

Biblioteca de ícones

Entenda que quando estamos falando de uma biblioteca de ícones na verdade estamos falando de um typeface, ou ainda, uma fonte. Sim, estou me referindo a algo como a fonte mais odiada de todos os tempos: Comic Sans.

Mas este é um tutorial de ícone, qual a relação entre os dois? Aqui vem o pulo do gato (Cuidado). Uma font nada mais é que um arquivo que associa um código a um simbolo. Se você usa UTF-8 no seu site você terá a possibilidade de escrever até 4 bytes para representar cada simbolo. Isso dá um total de

$$ 2^{4*8} = 4.294.967.296 $$

símbolos possíveis.


Não acredita em mim? Você pode tentar ver (tentar, visto que os símbolos dependem de fonte) os códigos/ símbolos aqui

Você encontrará coisas como o símbolo musical "𝄠" representado por U+1D120, experimente ver o html:

<style>
  .clef-symbol {
    font-family: sans-serif;
    font-size: 32px;
  }
  .clef-symbol::before {
    content: '\1d120';
  }
</style>
<span class="clef-symbol"></span>

Com essa informação podemos então substituir em nosso código com algo parecido com:

<style>
  @font-face{
    font-family: "Nome inédito para minha fonte de ícones";
    src: url("../fonts/nipmfdi.ttf") format("truetype"),
         url("../fonts/nipmfdi.woff") format("woff"),
         url("../fonts/nipmfdi.svg") format("svg");
  }
  .meu-icone {
    font-family: "Nome inédito para minha fonte de ícones";
    font-size: 22px;
  }
  .mi-elfo::before {
    content: '\01';
  }
  .text-blue{
    color: blue;
  }
</style>
<span class="meu-icone mi-elfo text-blue"></span>

Transformando ícones em fonte

Você pode tentar descobrir como fazer na mão e criar um tutorial sobre isso. Mas se quiser ser mais rápido você usar o Icomoon ou o Fontello.

Neste tutorial utilizei o Icomoon. Ele já retorna um pacote com os arquivos fonte e um demo pra visualizar se está ok. Caso queira você pode olhar o gist em Fonte e visualizar como ficaria o svg e o html do nosso elfo. Infelizmente os aquivos binários não estão disponíveis, até pq não é esse o objetivo. Mas você pode tentar criar o seu próprio. Boa sorte.

Parabéns! Agora é só sair usando

Veja como ficou o nosso elfo:

<style>
    @font-face {
        font-family: "Nome inédito para minha fonte de ícones";
        src:  url('fonts/nipmfdi.eot?c5o7ll');
        src:  url('fonts/nipmfdi.eot?c5o7ll#iefix') format('embedded-opentype'),
            url('fonts/nipmfdi.ttf?c5o7ll') format('truetype'),
            url('fonts/nipmfdi.woff?c5o7ll') format('woff'),
            url('fonts/nipmfdi.svg?c5o7ll#nipmfdi') format('svg');
        font-weight: normal;
        font-style: normal;
        font-display: block;
    }

    [class^="icon-"], [class*=" icon-"] {
        /* use !important to prevent issues with browser extensions that change fonts */
        font-family: "Nome inédito para minha fonte de ícones" !important;
        speak: never;
        font-style: normal;
        font-weight: normal;
        font-variant: normal;
        text-transform: none;
        line-height: 1;

        /* Better Font Rendering =========== */
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
    }
    .icon-elf{
        font-size: 32px;
        position: relative;
    }
    .icon-elf *:before{
        position: absolute;
        top: 0;
        left: 0;
    }
    .icon-elf .face:before {
        content: "\e900";
        color: rgb(229, 205, 194);
    }
    .icon-elf .t-shirt:before {
        content: "\e901";
        color: rgb(20, 83, 105);
    }
    .icon-elf .hair:before {
        content: "\e902";
        color: rgb(141, 74, 67);
    }
    .icon-elf .outline:before {
        content: "\e903";
        color: rgb(0, 0, 0);
    }

</style>
<span class="icon-elf">
    <span class="face"></span>
    <span class="t-shirt"></span>
    <span class="hair"></span>
    <span class="outline"></span>
</span>

A imagem é a mesma que a última mas em tamanho 32px.

Agora, sabendo de tudo isso... Convém usar ícone svg como arquivo, biblioteca de ícone ou direto no html?

Fontes

Creating an SVG icon typeface

Best practices for working with SVGs

Arquivos gist elfo

Para visualizar o conteúdo do svg e não a imagem clique em "<>" (display the source blob)

curto a abordagem do tailwind para esse caso deixa bem fácil para tais mudanças