Remplacer les icônes SVG par défaut de Discourse par des icônes personnalisées dans un thème

Vous pouvez remplacer les icônes SVG par défaut de Discourse individuellement ou en bloc par vos propres SVG personnalisés et les surcharger au sein d’un thème ou d’un composant de thème.

Étape 1 - Créer une feuille de sprites SVG

Pour commencer, vous devez créer une feuille de sprites SVG. Elle peut contenir n’importe quoi, depuis une seule icône SVG personnalisée supplémentaire jusqu’à un ensemble complet de remplacement composé de centaines d’icônes.

La feuille de sprites doit être enregistrée en tant que fichier SVG. En principe, vous imbriquez le contenu de la balise <svg> du fichier d’icône SVG d’origine dans des balises <symbol> et leur donnez un identifiant approprié.

<?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 à l'intérieur de la balise <svg> du fichier d'icône SVG source
      il s'agit généralement de tout ce qui se trouve entre les balises <svg>
      (mais pas la balise SVG elle-même, qui est remplacée par <symbol> ci-dessus)
      Vous pouvez transférer n'importe quels attributs (c.-à-d. ViewBox="0 0 0 0") vers la balise <symbol>
      -->
  </symbol>

  <symbol id="my-theme-icon-2">
    <!-- Code SVG ici. Ajoutez d'autres blocs <symbol> si nécessaire.
      -->
  </symbol>
</svg>
  • Assurez-vous d’ajouter un ID personnalisé à chaque symbole dans la feuille de sprites. Il est probablement utile pour votre santé mentale de préfixer vos IDs avec le nom de votre thème my-theme-icon.

  • Pour que la couleur de l’icône soit dynamique comme les icônes existantes, définissez le remplissage sur currentColor plutôt que sur une couleur codée en dur (comme #333)

  • Pour mettre à l’échelle ou centrer correctement votre icône, utilisez un attribut viewBox sur la balise <symbol>. Consultez How to Scale SVG | CSS-Tricks pour plus d’informations.

  • Soyez attentif aux collisions de styles au sein de vos SVG. Par exemple, les SVG ont souvent un style en ligne défini comme .st0{fill:#FF0000;}. Si vous avez plusieurs SVG utilisant les mêmes classes, cela peut causer des problèmes (pour corriger ces problèmes, modifiez les classes pour qu’elles soient uniques pour chaque icône).

  • Si vous avez beaucoup d’icônes, il existe des moyens d’automatiser cela. https://www.npmjs.com/package/svg-sprite-generator est un simple outil en ligne de commande pour combiner des SVG en une feuille de sprites.

Exemple - feuille de sprites avec une seule icône personnalisée

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

Étape 2 - Ajouter la feuille de sprites à votre thème

Une fois votre feuille de sprites créée, vous devez ajouter le fichier SVG à votre composant/thème. C’est facile via l’interface utilisateur, ou vous pouvez le coder en dur dans un composant/thème.

:information_source: Une fois qu’il est téléversé dans n’importe quel composant/thème installé, il est disponible dans toute votre instance en utilisant l’ID de la balise <symbol>.

Via l’interface utilisateur

Allez dans la section Téléversements des paramètres du thème/composant et ajoutez votre fichier sprite avec un nom de variable SCSS icons-sprite :

Codage en dur dans un thème / composant

Ajoutez le fichier de feuille de sprites dans le dossier /assets du thème. Puis mettez à jour votre fichier assets.json dans le dossier racine.
Pour un sprite SVG nommé my-icons.svg, votre about.json doit inclure ceci :

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

Étape 3 (optionnelle) - Surcharger les icônes par défaut

Maintenant que votre feuille de sprites est prête, vous pouvez indiquer à Discourse de remplacer les icônes. Voici comment faire depuis un 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.
});

Le premier ID, bars, est l’ID de l’icône par défaut dans Discourse et le second est l’ID de votre icône de remplacement. La façon la plus simple de trouver l’ID de l’une de nos icônes est d’inspecter l’icône dans votre navigateur.

Ici, le nom de l’icône suit le préfixe d-icon-. Donc dans cet exemple, c’est d-unliked

La plupart de nos icônes suivent les noms d’icônes de https://fontawesome.com/, mais il y a des exceptions (c’est pourquoi vérifier l’ID dans votre inspecteur est la méthode la plus fiable). Vous pouvez voir toutes les exceptions dans le bloc const REPLACEMENTS ici sur github.

C’est tout. Vous pouvez maintenant personnaliser Discourse avec vos propres icônes personnalisées !


Ce document est sous contrôle de version - suggérez des modifications sur github.

58 « J'aime »

Comment cibler une icône spécifique dans un élément spécifique ? Dans mon cas, j’aimerais remplacer l’icône Docs dans le menu de la barre latérale par une autre icône FA.

1 « J'aime »

Je le cacherais avec CSS et j’ajouterais un nouveau bouton pour cela.

Commun / CSS

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

Ajoutez un nouveau bouton dans Plus > Personnaliser cette section

4 « J'aime »

Cela ne fonctionne pas. J’ai essayé de faire :

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

d’ici, mais cela ne semble pas fonctionner. Je pense que la méthode de la balise script est cassée, car ceci ne fonctionne pas avec le lien de prévisualisation. Honnêtement, je ne suis pas sûr.

1 « J'aime »

ça marche pour moi :woman_shrugging:t2:

le mettez-vous dans l’onglet head ? j’ai aussi remplacé le robot dans mon en-tête :

vous devrez peut-être ajouter l’icône au paramètre SVG icon subset de l’administrateur.

2 « J'aime »

[citation=“Lilly, post:40, topic:115736”]
êtes-vous en train de le mettre dans l’onglet d’en-tête ?
[/citation]

Oui, l’onglet d’en-tête. Et l’onglet d’entête (header), puisque le guide dit ça.

[citation=“Lilly, post:40, topic:115736”]
SVG icon subset
[/citation]
C’est fait. Ça marche maintenant. Merci !

1 « J'aime »

@NateDhaliwal Pourriez-vous m’envoyer un message privé s’il vous plaît ? J’ai besoin d’aide pour quelque chose et je ne vois pas d’option de chat sur votre profil. Merci !

1 « J'aime »
Pour le menu de gauche, nous redéfinissons l'arrière-plan de l'élément avec la classe prefix-span dans la catégorie 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;

lorsque je clique sur la page principale, l’icône carrée réapparaît ! Pouvez-vous me dire ce que je fais de mal ? et sur la page de catégorie elle-même, cela fonctionne.

Bonjour, quelqu’un pourrait-il clarifier la relation entre un nom de thème/composant, un nom de fichier, un nom de variable SCSS et l’ID du symbole… ?

J’essaie de remplacer l’icône de modérateur shield-halved par la nôtre, mais les instructions sont un peu confuses.

Dans l’étape 2 :

  • La capture d’écran “Via l’interface utilisateur” montre un nom de fichier baticonsprite.svg avec un nom de variable SCSS icons-sprite.
  • Mais ensuite, dans “Codage en dur dans un thème”, il vous est demandé de le coder en dur dans un thème/composant.
  • Mais comment ? Je ne vois pas de fichier assets.json dans l’éditeur. Si j’exporte le composant, je vois un about.json, qui montre le sprite que j’ai téléchargé via l’interface utilisateur.
  • Mais cet exemple montre également un nom de fichier différent /assets/my-icons.svg — est-ce censé être le même fichier que baticonsprite.svg ?
  • S’agit-il de deux façons alternatives de faire la même chose, et vous n’avez besoin d’en faire qu’une OU l’autre, pas les deux… ?

Dans l’étape 3 :

  • Mais alors maintenant, dans api.replaceIcon(), le deuxième paramètre n’utilise aucun des ID précédents, ni icons-sprite, ni bat-icon, ni baticonsprite.svg, ni my-icons.svg. Au lieu de cela, nous obtenons un tout nouvel my-theme-icon-bars… confus.
  • Le préfixe my-theme est-il requis, et si oui, d’où vient cette chaîne “nom du thème” ? Est-ce censé être my-theme-bat-icon ? Et qu’en est-il si c’est un composant, pas un thème ?
  • Et pour la partie icon-bars, est-ce censé être :
    • L’ID du symbole du XML de la spritesheet SVG
    • Le nom du fichier du fichier SVG
    • Le nom de la variable SCSS que vous lui donnez
    • Une combinaison des éléments ci-dessus (comme icons-sprite-bat-icon) ?

Et où placez-vous réellement l’appel api.replaceIcon() ? Est-il acceptable de le placer dans l’onglet “JS” d’un composant personnalisé, qui contient déjà le boilerplate :

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

export default apiInitializer((api) => {
   // votre code ici
});

Ou est-il nécessaire de créer une balise personnalisée <script type=”discourse/plugin”> et de la placer dans l’onglet <head> à la place ?


Désolé pour ma confusion ici.

J’ai essayé plusieurs combinaisons des éléments ci-dessus et je n’ai pas réussi à faire apparaître mon sprite, quoi que j’aie fait…

Mon XML de sprite ressemble à ceci :

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

Le nom du fichier est my-logo.svg et le nom de la variable SCSS est également my-logo.

Et dans l’onglet JS du composant personnalisé, j’ai :

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

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

Mais rien ne semble apparaître. Y a-t-il une étape que j’ai manquée, ou une sorte d’interpolation magique de chaîne que je ne comprends pas… ?

1 « J'aime »

Quelqu’un a-t-il réussi à comprendre cela ? Toujours en difficulté avec ceci…

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

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

Je ne suis pas sûr d’où vient le deuxième identifiant (« my-logo ») si vous téléchargez le fichier via l’interface utilisateur :

Ni $my-logo, $test, my-logo.svg, l’URL absolue du SVG sur le disque, etc. ne fonctionnent. Le bouclier est bien remplacé, mais par rien. Le SVG devient simplement un \u003cuse href=”#my-logo”\u003e vide sans contenu.

1 « J'aime »

J’ai finalement trouvé la solution (merci, Claude) !

Donc :

  1. Le nom du fichier SVG n’a pas d’importance.

  2. À l’intérieur de la feuille de sprites SVG, c’est l’ID du symbole (id) qui détermine le nom final de l’icône, comme <symbol id=”my-logo” …>

  3. Mais le nom de la variable SCSS DOIT être exactement icons-sprite, PAS quelque chose en rapport avec les ID de la feuille de sprites finale :

  4. Une fois téléchargé, cela ressemblera à ceci :


    (Notez que le nom de la feuille de sprites / variable n’apparaît nulle part. Il doit être exactement $icons-sprite, le $ étant automatiquement ajouté et ne faisant pas partie de ce que vous avez saisi à l’étape précédente)

  5. Ensuite, enfin, dans l’onglet « JS » du thème (pas <head>), c’est là que vous utilisez l’ID de la feuille de sprites de l’étape 1 :

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

Donc, le nom de la variable SCSS doit être exactement icons-sprite, le nom du fichier n’a pas d’importance, et le nom de l’icône de l’API est déterminé par l’ID du symbole à l’intérieur de la feuille de sprites.

3 « J'aime »