استبدال أيقونات SVG الافتراضية في Discourse بأيقونات مخصصة في قالب

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 إعجابًا

كيف يمكن استهداف أيقونة معينة في عنصر معين؟ في حالتي، أود استبدال أيقونة المستندات في قائمة الشريط الجانبي بأيقونة FA أخرى.

سأقوم بإخفائه باستخدام CSS وإضافة زر جديد له.

Common / CSS

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

أضف زرًا جديدًا في المزيد > تخصيص هذا القسم

3 إعجابات

هذا لا يعمل. حاولت القيام بـ:

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

من هنا، ولكن يبدو أنه لا يعمل. أعتقد أن طريقة وسم النص البرمجي معطلة، لأن هذا لا يعمل مع رابط المعاينة. بصراحة لست متأكدًا.

يعمل معي :woman_shrugging:t2:


هل تضعه في علامة التبويب head؟ لقد قمت أيضًا باستبدال الروبوت في الرأس الخاص بي:

قد تحتاج إلى إضافة الأيقونة إلى إعداد SVG icon subset الخاص بالمسؤول.

إعجاب واحد (1)

[اقتباس=“Lilly، المشاركة: 40، الموضوع: 115736”]
هل تضعها في علامة تبويب الرأس؟
[/اقتباس]

نعم، علامة تبويب الرأس. وعلامة تبويب الرأس (header tab)، لأن الدليل يقول ذلك.

[اقتباس=“Lilly، المشاركة: 40، الموضوع: 115736”]
SVG icon subset
[/اقتباس]
تم. إنه يعمل الآن. شكرا!

إعجاب واحد (1)

@NateDhaliwal هل يمكنك مراسلتي من فضلك؟ أحتاج إلى مساعدة في شيء ما ولا أرى خيار دردشة في ملفك الشخصي. شكراً لك!

إعجاب واحد (1)
بالنسبة للقائمة اليسرى، نعيد تعريف خلفية العنصر بفئة prefix-span داخل فئة 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;

عندما أنقر على الصفحة الرئيسية، يظهر الرمز المربع مرة أخرى! هل يمكنك إخباري بما أفعله بشكل خاطئ؟ وعلى صفحة الفئة نفسها، يعمل الأمر.

مرحباً، هل يمكن لأي شخص توضيح العلاقة بين اسم السمة/المكون، واسم الملف، واسم متغير SCSS، ومعرف الرمز…؟

أحاول استبدال أيقونة المشرف shield-halved بأيقونة خاصة بنا، لكن التعليمات غير واضحة بعض الشيء.

في الخطوة 2:

  • لقطة الشاشة “عبر واجهة المستخدم” تعرض اسم ملف baticonsprite.svg مع اسم متغير SCSS icons-sprite
  • ولكن بعد ذلك في “التشفير الثابت في سمة”، تخبرك بتشفيره الثابت في سمة/مكون
  • ولكن كيف؟ لا أرى ملف assets.json في المحرر. إذا قمت بتصدير المكون، أرى about.json، والذي يعرض الـ sprite الذي قمت بتحميله عبر واجهة المستخدم
  • ولكن هذا المثال يعرض أيضًا اسم ملف مختلف /assets/my-icons.svg - هل المقصود أن يكون هذا هو نفس ملف baticonsprite.svg؟
  • هل هذان طريقتان بديلتان للقيام بنفس الشيء، وتحتاج فقط إلى القيام بواحد أو الآخر، وليس كليهما…؟

في الخطوة 3:

  • ولكن الآن، في api.replaceIcon(), المعامل الثاني لا يستخدم أيًا من المعرفات السابقة، لا icons-sprite أو bat-icon أو baticonsprite.svg أو my-icons.svg. بدلاً من ذلك، نحصل على my-theme-icon-bars جديد تمامًا… مرتبك.
  • هل البادئة my-theme مطلوبة، وإذا كان الأمر كذلك، فمن أين يأتي هذا السلسلة “اسم السمة”؟ مثل هل يجب أن يكون my-theme-bat-icon؟ وماذا لو كان مكونًا، وليس سمة؟
  • وبالنسبة لجزء icon-bars، هل يجب أن يكون:
    • معرف الرمز من XML الخاص بـ SVG spritesheet
    • اسم ملف ملف SVG
    • اسم متغير SCSS الذي تعطيه له
    • مزيج من ما سبق (مثل icons-sprite-bat-icon؟)

وأين تضع فعليًا استدعاء api.replaceIcon()؟ هل من المقبول وضعه في علامة التبويب “JS” لمكون مخصص، والذي يحتوي بالفعل على الكود الأساسي:

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

export default apiInitializer((api) => {
   // your code here
});

أم أنه من الضروري إنشاء علامة <script type=”discourse/plugin”> مخصصة ووضعها في علامة التبويب <head> بدلاً من ذلك؟


آسف لهذا الارتباك.

لقد جربت عدة مجموعات مما سبق ولم أتمكن من عرض الـ sprite الخاص بي مهما حاولت…

ملف XML الخاص بالـ sprite الخاص بي يبدو كالتالي:

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

اسم الملف هو my-logo.svg واسم متغير SCSS هو أيضًا my-logo

وفي علامة التبويب “JS” للمكون المخصص، لدي:

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

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

ولكن لا يبدو أن شيئًا ما يظهر. هل هناك خطوة فاتنيها، أو نوع من استيفاء السلسلة السحري الذي أسأت فهمه…؟