Sostituire le icone SVG predefinite di Discourse con icone personalizzate in un tema

You can replace a Discourse’s default SVG icons individually or as a whole with your own custom SVG and override them within a theme or theme component.

Step 1 - Create an SVG Spritesheet

To get started, you must create an SVG Spritesheet. This can contain anything from a single additional custom SVG icon up to an entire replacement set of hundreds.

The spritesheet should be saved as an SVG file. In principle, you are nesting the <svg> tag contents from the original SVG icon file into <symbol> tags and giving them a nice identifier.

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  <symbol id="my-theme-icon-1">
    <!--
      Code inside the <svg> tag from the source SVG icon file
      this is typically everything between the <svg> tags
      (but not the SVG tag itself, that's replaced by <symbol> above)
      You can transfer any attributes (i.e. ViewBox="0 0 0 0") to the <symbol> tag
      -->
  </symbol>

  <symbol id="my-theme-icon-2">
    <!-- SVG code here. Add more <symbol> blocks as needed.
      -->
  </symbol>
</svg>
  • Be sure to add a custom ID to each symbol in the spritesheet. It’s probably helpful for your sanity to prefix your IDs with your theme name my-theme-icon.

  • To have the icon color to be dynamic like the existing icons, set the fill to currentColor rather than a hardcoded color (like #333)

  • To scale or correctly centre your icon, utilise a viewBox attribute on the <symbol> tag. See How to Scale SVG | CSS-Tricks for more information.

  • Be on the lookout for style collisions within your SVGs. For example, SVGs will often have an inline style like .st0{fill:#FF0000;} defined. If you have multiple SVGs using the same classes this can cause issues (to fix these issues, edit the classes to be unique to each icon).

  • If you have many icons, there are ways to automate this. svg-sprite-generator - npm is a simple command line tool for combining SVGs into a spritesheet.

Example - single custom icon spritesheet

<?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>

Step 2 - Add the spritesheet to your theme

Once your spritesheet is built, you need to add the SVG file to your component/theme. This is easy via the UI, or you can hard code it into a component/theme.

:information_source: Once it is uploaded to any installed component/theme, it is available throughout your instance using the ID in the <symbol> tag.

Via the UI

Go to the Uploads section of the theme/component settings and add your sprite file with a SCSS var name of icons-sprite:

Hardcode into a Theme / Component

Add the spritesheet file to the Theme’s /assets folder. Then update your assets.json file in the root folder.
For an SVG sprite called my-icons.svg, your about.json should include this:

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

Step 3 (optional) - Overriding default icons

Now that your spritesheet is set, you can tell Discourse to replace icons. This is how you do it from an 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.
});

The first ID, bars, is the default icon ID in Discourse and the second is the ID of your replacement icon. The easiest way to find an ID of one of our icons is to inspect the icon in your browser.

Here the icon name follows the d-icon- prefix. So in this example it’s d-unliked

Most of our icons follow the icon names from https://fontawesome.com/, but there are exceptions (which is why checking the ID in your inspector is the most reliable method). You can see all the exceptions in the const REPLACEMENTS block here on github.

That’s it. You can now style Discourse with your own custom icons!


This document is version controlled - suggest changes on github.

57 Mi Piace

Come si potrebbe selezionare un’icona specifica in un elemento specifico? Nel mio caso, vorrei sostituire l’icona Docs nel menu della barra laterale con un’altra icona FA.

Lo nasconderei con CSS e aggiungerei un nuovo pulsante per esso.

Common / CSS

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

Aggiungi un nuovo pulsante in Altro > Personalizza questa sezione

3 Mi Piace

Questo non funziona. Ho provato a fare un:

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

da qui, ma non sembra funzionare. Penso che il metodo del tag script sia rotto, dato che questo non funziona con il link di anteprima. Onestamente non sono sicuro.

funziona per me :woman_shrugging:t2:

lo stai inserendo nella scheda head? ho anche sostituito il robot nel mio header:

potrebbe essere necessario aggiungere l’icona all’impostazione SVG icon subset dell’admin.

1 Mi Piace

[citazione=“Lilly, post:40, topic:115736”]
lo stai mettendo nella scheda head?
[/citazione]

Sì, nella scheda head. E nella scheda header, dato che lo dice la guida.

[citazione=“Lilly, post:40, topic:115736”]
SVG icon subset
[/citazione]
Fatto. Ora funziona. Grazie!

1 Mi Piace

@NateDhaliwal Potresti mandarmi un messaggio privato per favore? Ho bisogno di aiuto con qualcosa e non vedo l’opzione chat nel tuo profilo. Grazie!

1 Mi Piace
Per il menu di sinistra, ridefiniamo lo sfondo dell'elemento con la classe prefix-span all'interno della 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 clicco sulla pagina principale l’icona quadrata appare di nuovo! puoi dirmi cosa sto sbagliando? e sulla pagina della categoria stessa funziona

Ciao, qualcuno potrebbe per favore chiarire la relazione tra un nome di tema/componente, nome del file, nome della variabile SCSS e l’ID del simbolo…?

Sto cercando di sostituire l’icona del moderatore shield-halved con una nostra, ma le istruzioni sono un po’ poco chiare.

Nel passaggio 2:

  • Lo screenshot “Tramite l’interfaccia utente” mostra un nome di file baticonsprite.svg con un nome di variabile SCSS icons-sprite
  • Ma poi in “Codifica manuale in un tema”, ti dice di codificarlo manualmente in un tema/componente
  • Ma come? Non vedo un file assets.json nell’editor. Se esporto il componente, vedo un about.json, che mostra lo sprite che ho caricato tramite l’interfaccia utente
  • Ma questo esempio mostra anche un nome di file diverso /assets/my-icons.svg — è inteso come lo stesso file di baticonsprite.svg?
  • Questi sono due modi alternativi per fare la stessa cosa, e devi farne solo uno OPPURE l’altro, non entrambi…?

Nel passaggio 3:

  • Ma poi ora, in api.replaceIcon(), il secondo parametro non utilizza nessuno degli ID precedenti, né icons-spritebat-iconbaticonsprite.svgmy-icons.svg. Invece, otteniamo un my-theme-icon-bars completamente nuovo… confuso.
  • È richiesto il prefisso my-theme, e se sì, da dove proviene questa stringa “nome del tema”? Come se dovesse essere my-theme-bat-icon? E se è un componente, non un tema?
  • E per la parte icon-bars, dovrebbe essere:
  • L’ID del simbolo dal file XML dello sprite SVG
  • Il nome del file SVG
  • Il nome della variabile SCSS che gli dai
  • Una combinazione di quanto sopra (come icons-sprite-bat-icon?)

E dove metti effettivamente la chiamata api.replaceIcon()? Va bene metterla nella scheda “JS” di un componente personalizzato, che ha già il boilerplate:

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

export default apiInitializer((api) => {
  // il tuo codice qui
});

O è necessario creare un tag personalizzato <script type=”discourse/plugin”> e metterlo invece nella scheda <head>?


Scusa per la mia confusione qui.

Ho provato diverse combinazioni di quanto sopra e non sono riuscito a far apparire il mio sprite in nessun modo…

Il mio sprite XML assomiglia a:

<?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>

Il nome del file è my-logo.svg e anche il nome della variabile SCSS è my-logo

E nella scheda JS del componente personalizzato, ho:

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

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

Ma non sembra apparire nulla. Manca un passaggio, o c’è un qualche tipo di interpolazione di stringa magica che non sto capendo…?