Ersetze Discourse' Standard-SVG-Icons mit benutzerdefinierten Icons in einem Theme

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 „Gefällt mir“

Wie kann man ein bestimmtes Symbol in einem bestimmten Element ansprechen? In meinem Fall möchte ich das Docs-Symbol im Sidebar-Menü durch ein anderes FA-Symbol ersetzen.

Ich würde es mit CSS ausblenden und einen neuen Button dafür hinzufügen.

Common / CSS

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

Fügen Sie einen neuen Button in Mehr > Diesen Abschnitt anpassen hinzu

3 „Gefällt mir“

Das funktioniert nicht. Ich habe versucht:

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

von hier, aber es scheint nicht zu funktionieren. Ich glaube, die Skript-Tag-Methode ist kaputt, da dies mit dem Vorschau-Link nicht funktioniert. Ehrlich gesagt, ich bin mir nicht sicher.

works for me :woman_shrugging:t2:

steckst du es in den Kopf-Tab? ich habe auch den Roboter in meinem Header ersetzt:

du musst das Icon möglicherweise zur Admin-Einstellung SVG icon subset hinzufügen.

1 „Gefällt mir“

Ja, im Kopf-Tab. Und im Header-Tab, da die Anleitung das besagt.

Erledigt. Es funktioniert jetzt. Danke!

1 „Gefällt mir“

@NateDhaliwal Könnten Sie mir bitte eine private Nachricht senden? Ich brauche Hilfe bei etwas und sehe keine Chat-Option in Ihrem Profil. Danke!

1 „Gefällt mir“
Für das linke Menü definieren wir den Hintergrund des Elements mit der Klasse prefix-span innerhalb der Kategorie Audi neu */
.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;

Wenn ich auf die Hauptseite klicke, erscheint das quadratische Symbol wieder! Können Sie mir sagen, was ich falsch mache? Und auf der Kategorieseite selbst funktioniert es.

Hallo, könnte mir bitte jemand die Beziehung zwischen einem Theme-/Komponenten-Namen, einem Dateinamen, einem SCSS-Variablennamen und der Symbol-ID erläutern?

Ich versuche, das Moderator-Icon shield-halved durch eines unserer eigenen zu ersetzen, aber die Anweisungen sind etwas unklar.

In Schritt 2:

  • Der Screenshot unter „Über die Benutzeroberfläche“ zeigt einen Dateinamen von baticonsprite.svg mit einem SCSS-Variablennamen von icons-sprite.
  • Aber dann wird unter „Hardcode in ein Theme“ erklärt, wie man es in ein Theme/eine Komponente hartkodiert.
  • Aber wie? Ich sehe keine assets.json-Datei im Editor. Wenn ich die Komponente exportiere, sehe ich eine about.json, die den über die Benutzeroberfläche hochgeladenen Sprite anzeigt.
  • Aber dieses Beispiel zeigt auch einen anderen Dateinamen von /assets/my-icons.svg – soll das dieselbe Datei wie baticonsprite.svg sein?
  • Sind dies zwei alternative Wege, um dasselbe zu tun, und man muss nur das eine ODER das andere tun, nicht beides…?

In Schritt 3:

  • Aber dann verwendet der zweite Parameter in api.replaceIcon() keine der vorherigen IDs, weder icons-sprite noch bat-icon oder baticonsprite.svg oder my-icons.svg. Stattdessen erhalten wir ein völlig neues my-theme-icon-bars… verwirrt.
  • Ist das Präfix my-theme erforderlich, und wenn ja, woher stammt dieser „Theme-Name“-String? Ist er als my-theme-bat-icon gedacht? Und was ist, wenn es sich um eine Komponente und nicht um ein Theme handelt?
  • Und für den Teil icon-bars, soll es sein:
    • Die Symbol-ID aus dem SVG-Spritesheet-XML
    • Der Dateiname der SVG-Datei
    • Der SCSS-Variablenname, den Sie ihm geben
    • Eine Kombination aus den oben genannten (wie icons-sprite-bat-icon?)

Und wo platziert man den Aufruf api.replaceIcon()? Ist es in Ordnung, ihn im „JS“-Tab einer benutzerdefinierten Komponente zu platzieren, die bereits den Boilerplate-Code enthält:

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

export default apiInitializer((api) => {
   // dein Code hier
});

Oder ist es notwendig, ein benutzerdefiniertes <script type=”discourse/plugin”>-Tag zu erstellen und dieses stattdessen im <head>-Tab zu platzieren?


Entschuldigung für meine Verwirrung hier.

Ich habe mehrere Kombinationen der oben genannten versucht und konnte meinen Sprite nicht anzeigen lassen, egal was ich tat…

Mein Sprite-XML sieht so aus:

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

Der Dateiname ist my-logo.svg und der SCSS-Variablenname ist ebenfalls my-logo.

Und im JS-Tab der benutzerdefinierten Komponente habe ich:

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

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

Aber es scheint nichts angezeigt zu werden. Fehlt ein Schritt oder verstehe ich eine Art magische String-Interpolation falsch…?