Substituir ícones SVG padrão do Discourse por ícones personalizados em um tema

Você pode substituir os ícones SVG padrão do Discourse individualmente ou todos de uma vez por seus próprios SVGs personalizados e substituí-los dentro de um tema ou componente de tema.

Passo 1 - Criar uma Spritesheet SVG

Para começar, você deve criar uma Spritesheet SVG. Ela pode conter qualquer coisa, desde um único ícone SVG personalizado adicional até um conjunto de substituição completo com centenas.

A spritesheet deve ser salva como um arquivo SVG. Em princípio, você aninha o conteúdo da tag <svg> do arquivo de ícone SVG original em tags <symbol> e atribui a elas um identificador agradável.

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  <symbol id="my-theme-icon-1">
    <!--
      Código dentro da tag <svg> do arquivo de ícone SVG de origem
      isso é tipicamente tudo entre as tags <svg>
      (mas não a tag SVG em si, que é substituída por <symbol> acima)
      Você pode transferir quaisquer atributos (ou seja, ViewBox="0 0 0 0") para a tag <symbol>
      -->
  </symbol>

  <symbol id="my-theme-icon-2">
    <!-- Código SVG aqui. Adicione mais blocos <symbol> conforme necessário.
      -->
  </symbol>
</svg>
  • Certifique-se de adicionar um ID personalizado a cada símbolo na spritesheet. Provavelmente será útil para sua sanidade prefixar seus IDs com o nome do seu tema, como my-theme-icon.

  • Para que a cor do ícone seja dinâmica como os ícones existentes, defina o fill como currentColor em vez de uma cor codificada (como #333)

  • Para dimensionar ou centralizar corretamente seu ícone, utilize um atributo viewBox na tag <symbol>. Consulte How to Scale SVG | CSS-Tricks para mais informações.

  • Fique atento a colisões de estilo dentro de seus SVGs. Por exemplo, SVGs geralmente terão um estilo embutido como .st0{fill:#FF0000;} definido. Se você tiver vários SVGs usando as mesmas classes, isso pode causar problemas (para corrigir esses problemas, edite as classes para serem exclusivas de cada ícone).

  • Se você tiver muitos ícones, existem maneiras de automatizar isso. https://www.npmjs.com/package/svg-sprite-generator é uma ferramenta simples de linha de comando para combinar SVGs em uma spritesheet.

Exemplo - spritesheet de ícone personalizado único

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  <symbol id="bat-icon" viewBox="6 6 36 36">
    <path
      fill="currentColor"
      d="M24,18.2c0.7,0,0.9,0.2,0.9,0.2l0.4-1.7c0,0,0.4,1.5,0.4,2.8c0.2,1.1,2.2,0.4,3.9,0C31.4,19.1,32,16,32,16h16c0,0-9.4,3.5-7,10c0,0-14.8-2-17,7l0,0c-2.2-9-17-7-17-7c2.4-6.5-7-10-7-10h16c0,0,0.6,3.1,2.3,3.5c1.7,0.4,3.9,1.1,3.9,0c0.2-1.1,0.4-2.8,0.4-2.8l0.4,1.7C23.1,18.4,23.4,18.2,24,18.2L24,18.2L24,18.2z"
    />
  </symbol>
</svg>

Passo 2 - Adicionar a spritesheet ao seu tema

Depois que sua spritesheet for criada, você precisa adicionar o arquivo SVG ao seu componente/tema. Isso é fácil através da interface do usuário, ou você pode codificá-lo em um componente/tema.

:information_source: Uma vez carregado em qualquer componente/tema instalado, ele fica disponível em toda a sua instância usando o ID na tag <symbol>.

Via a Interface do Usuário

Vá para a seção Uploads das configurações do tema/componente e adicione seu arquivo de sprite com um nome de variável SCSS de icons-sprite:

Codificar em um Tema / Componente

Adicione o arquivo da spritesheet à pasta /assets do Tema. Em seguida, atualize seu arquivo assets.json na pasta raiz.
Para um sprite SVG chamado my-icons.svg, seu about.json deve incluir isto:

"assets": {
  "icons-sprite": "/assets/my-icons.svg"
}

Passo 3 (opcional) - Substituir ícones padrão

Agora que sua spritesheet está definida, você pode instruir o Discourse a substituir ícones. É assim que você faz isso a partir de um api-initializer:

// {theme}/javascripts/discourse/api-initializers/init-theme.gjs

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
  api.replaceIcon("bars", "my-theme-icon-bars");
  api.replaceIcon("link", "my-theme-icon-link");
  // etc.
});

O primeiro ID, bars, é o ID do ícone padrão no Discourse e o segundo é o ID do seu ícone de substituição. A maneira mais fácil de encontrar um ID de um de nossos ícones é inspecionar o ícone no seu navegador.

Aqui, o nome do ícone segue o prefixo d-icon-. Então, neste exemplo, é d-unliked

A maioria dos nossos ícones segue os nomes de ícones do https://fontawesome.com/, mas há exceções (é por isso que verificar o ID no seu inspetor é o método mais confiável). Você pode ver todas as exceções no bloco const REPLACEMENTS aqui no github.

É isso. Agora você pode estilizar o Discourse com seus próprios ícones personalizados!


Este documento é controlado por versão - sugira alterações no github.

57 curtidas

Como se pode direcionar um ícone específico em um elemento específico? No meu caso, gostaria de substituir o Ícone de Documentos no menu da barra lateral por outro ícone FA.

1 curtida

Eu o ocultaria com CSS e adicionaria um novo botão para ele.

Common / CSS

.sidebar-section-wrapper {
  li[data-list-item-name=docs] {
    display: none !important;
  }
}

Adicione um novo botão em Mais > Personalizar esta seção

4 curtidas

Isso não funciona. Eu tentei fazer um:

<script type="text/discourse-plugin" version="0.8">
    api.replaceIcon("shield-halved", "hat-wizard");
</script>

a partir daqui, mas não parece funcionar. Acho que o método da tag script está quebrado, já que isto não funciona com o link de pré-visualização. Honestamente, não tenho certeza.

1 curtida

funciona para mim :woman_shrugging:t2:

você está colocando na aba head? eu também substituo o robô no meu header:

você pode ter que adicionar o ícone à configuração SVG icon subset do admin.

2 curtidas

Sim, na aba head. E na aba header, já que o guia diz isso.

Feito. Agora funciona. Obrigado!

1 curtida

@NateDhaliwal Você poderia me enviar uma mensagem privada por favor? Preciso de ajuda com algo e não vejo uma opção de chat no seu perfil. Obrigado!

1 curtida
Para o menu esquerdo, redefinimos o plano de fundo do elemento com a classe prefix-span dentro da categoria Audi */
.navigation-category [data-category-id="6"] .prefix-span {
  background: url("https://raw.githubusercontent.com/tima4502/car-icons/bb0d0fae3e5b66c512a27a130b219ec0ee342ada/audi.svg") center/contain no-repeat !important;

quando clico na página principal, o ícone quadrado aparece novamente! você pode me dizer o que estou fazendo de errado? e na própria página da categoria funciona

Olá, alguém poderia esclarecer a relação entre o nome de um tema/componente, nome do arquivo, nome da variável SCSS e o ID do símbolo…?

Estou tentando substituir o ícone do moderador shield-halved por um nosso, mas as instruções são um pouco confusas.

Na Etapa 2:

  • A captura de tela “Via a UI” mostra um nome de arquivo baticonsprite.svg com um nome de variável SCSS icons-sprite
  • Mas então em “Hardcode em um Tema”, ele instrui você a codificar diretamente em um tema/componente
  • Mas como? Não vejo um arquivo assets.json no editor. Se eu exportar o componente, vejo um about.json, que mostra o sprite que carreguei via UI
  • Mas este exemplo também mostra um nome de arquivo diferente /assets/my-icons.svg — isso deveria ser o mesmo arquivo que baticonsprite.svg?
  • Essas são duas maneiras alternativas de fazer a mesma coisa, e você só precisa fazer uma OU outra, não ambas…?

Na Etapa 3:

  • Mas então, em api.replaceIcon(), o segundo parâmetro não usa nenhum dos IDs anteriores, nem icons-sprite nem bat-icon nem baticonsprite.svg nem my-icons.svg. Em vez disso, obtemos um my-theme-icon-bars totalmente novo… confuso.
  • O prefixo my-theme é necessário e, em caso afirmativo, de onde vem essa string “nome do tema”? Como se fosse para ser my-theme-bat-icon? E se for um componente, não um tema?
  • E para a parte icon-bars, deveria ser:
    • O ID do símbolo do XML da folha de sprites SVG
    • O nome do arquivo do arquivo SVG
    • O nome da variável SCSS que você dá a ele
    • Alguma combinação dos acima (como icons-sprite-bat-icon?)

E onde você realmente coloca a chamada api.replaceIcon()? Tudo bem colocá-la na aba “JS” de um componente personalizado, que já tem o boilerplate:

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
   // seu código aqui
});

Ou é necessário criar uma tag <script type=”discourse/plugin”> personalizada e colocá-la na aba <head> em vez disso?


Desculpe pela minha confusão aqui.

Tentei várias combinações acima e não consegui fazer meu sprite aparecer, não importa o quê…

Meu XML de sprite se parece com:

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">


<symbol id="my-logo" viewBox="0 0 94.652 95.261"><defs><linearGradient id="a" y1="47.631" x2="94.652" y2="47.631" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ff593d"/><stop offset="1" stop-color="#ff7751"/></linearGradient></defs><title>d_only</title><path d="M47.326,0H0V95.261H47.326c23.67,0,47.326-21.326,47.326-47.624S71,0,47.326,0Zm0,69.274a21.644,21.644,0,1,1,21.65-21.637A21.635,21.635,0,0,1,47.326,69.274Z" fill="url(#a)"/></symbol>

</svg>

O nome do arquivo é my-logo.svg e o nome da variável SCSS também é my-logo

E na aba JS do componente personalizado, eu tenho:

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
    api.replaceIcon("shield-halved", "my-logo")
});

Mas nada parece aparecer. Há alguma etapa que estou perdendo, ou algum tipo de interpolação mágica de string que estou entendendo mal…?

1 curtida

Alguém conseguiu descobrir isso? Ainda estou com dificuldades com isso…

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
    api.replaceIcon("shield-halved", "my-logo")
});

Não tenho certeza de onde vem o segundo ID (“my-logo”) se você carrega o arquivo pela interface do usuário:

Nem $my-logo, $test, my-logo.svg, o URL absoluto para o SVG no disco, etc. funcionam. O escudo é substituído, mas por nada. O SVG apenas se torna um \u003cuse href=”#my-logo”\u003e vazio sem conteúdo.

1 curtida

Finalmente descobri (obrigado, Claude)!

Então:

  1. O nome do arquivo SVG não importa.

  2. Dentro da spritesheet SVG, é o id do símbolo que determina o nome final do ícone, como \u003csymbol id=”my-logo” …\u003e

  3. Mas o nome da variável SCSS DEVE ser exatamente icons-sprite, e não algo relacionado aos IDs finais da spritesheet:

  4. Depois de carregado, ficará assim:


    (Note que o nome da spritesheet / variável não é encontrado em lugar nenhum. Deve ser exatamente $icons-sprite, com o $ automaticamente anteposto e não fazendo parte do que você inseriu na etapa anterior)

  5. Então, finalmente, na aba “JS” do tema (não em \u003chead\u003e), é onde você usa o ID da spritesheet da etapa 1:

    import { apiInitializer } from “discourse/lib/api”;
    
    export default apiInitializer((api) =\u003e {
    api.replaceIcon(“shield-halved”, “my-logo”)
    });
    

Portanto, o nome da variável SCSS deve ser exatamente icons-sprite, o nome do arquivo não importa, e o nome do ícone da API é determinado pelo ID do símbolo dentro da folha de sprites.

3 curtidas