doc: enhanced icon pack creation (#8659)

This commit is contained in:
Elian Doran 2026-02-19 22:14:10 +02:00 committed by GitHub
commit 4847f4094a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 214 additions and 171 deletions

View File

@ -1,100 +1,120 @@
<aside class="admonition note">
<p>This page describes how to create custom icon packs. For a general description
of how to use already existing icon packs, see&nbsp;<a class="reference-link"
href="#root/_help_gOKqSJgXLcIj">Icon Packs</a>.</p>
<p>This page explains, stepbystep, how to create a custom icon
pack. For a general description of how to use already existing icon packs,
see&nbsp;<a class="reference-link" href="#root/_help_gOKqSJgXLcIj">Icon Packs</a>.</p>
</aside>
<h2>Supported formats</h2>
<p>First read the quick flow to get the overall steps. After that there is
a concrete example (Phosphor) with a small Node.js script you can run to
generate the manifest.</p>
<h2>Quick flow (what you need to do)</h2>
<ol>
<li>Verify the icon set is a font (one of: .woff2, .woff, .ttf).</li>
<li>Obtain a list that maps icon names to Unicode code points (often provided
as a JSON like <code spellcheck="false">selection.json</code> or a CSS file).</li>
<li
>Create a manifest JSON that maps icon ids to glyphs and search terms.</li>
<li
>Create a Trilium note of type Code, set language to JSON, paste the manifest
as the note content.</li>
<li>Upload the font file as an attachment to the same note (MIME type must
be <code spellcheck="false">font/woff2</code>, <code spellcheck="false">font/woff</code>,
or <code spellcheck="false">font/ttf</code> and role <code spellcheck="false">file</code>).</li>
<li
>Add the label <code spellcheck="false">#iconPack=&lt;prefix&gt;</code> to
the note (prefix: alphanumeric, hyphen, underscore only).</li>
<li>Refresh the client and verify the icon pack appears in the icon selector.</li>
</ol>
<h2>Verify the icon set</h2>
<p>The first step is to analyze if the icon set being packed can be integrated
into Trilium.</p>
<p>Trilium only supports <strong>font-based icon sets</strong>, with the following
formats:</p>
<table>
<thead>
<tr>
<th>Extension</th>
<th>MIME type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code spellcheck="false">.woff2</code>
</td>
<td><code spellcheck="false">font/woff2</code>
</td>
<td>Recommended due to great compression (low size).</td>
</tr>
<tr>
<td><code spellcheck="false">.woff</code>
</td>
<td><code spellcheck="false">font/woff</code>
</td>
<td>Higher compatibility, but the font file is bigger.</td>
</tr>
<tr>
<td><code spellcheck="false">.ttf</code>
</td>
<td><code spellcheck="false">font/ttf</code>
</td>
<td>Most common, but highest font size.</td>
</tr>
</tbody>
</table>
<h2>Unsupported formats</h2>
<figure class="table">
<table>
<thead>
<tr>
<th>Extension</th>
<th>MIME type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code spellcheck="false">.woff2</code>
</td>
<td><code spellcheck="false">font/woff2</code>
</td>
<td>Recommended due to great compression (low size).</td>
</tr>
<tr>
<td><code spellcheck="false">.woff</code>
</td>
<td><code spellcheck="false">font/woff</code>
</td>
<td>Higher compatibility, but the font file is bigger.</td>
</tr>
<tr>
<td><code spellcheck="false">.ttf</code>
</td>
<td><code spellcheck="false">font/ttf</code>
</td>
<td>Most common, but highest font size.</td>
</tr>
</tbody>
</table>
</figure>
<p>Trilium <strong>does not</strong> support the following formats:</p>
<ul>
<li>SVG-based fonts.</li>
<li>Individual SVGs.</li>
<li><code spellcheck="false">.eot</code> fonts (legacy and proprietary).</li>
<li>Duotone icons, since it requires a special CSS format that Trilium doesn't
<li
>Duotone icons, since it requires a special CSS format that Trilium doesn't
support.</li>
<li>Any other font format not specified in the <em>Supported formats</em> section.</li>
<li>Any other font format not specified in the <em>Supported formats</em> section.</li>
</ul>
<p>In this case, the font must be manually converted to one of the supported
formats (ideally <code spellcheck="false">.woff2</code>).</p>
<h2>Prerequisites</h2>
<p>In order to create a new icon pack from a set of icons, it must meet the
following criteria:</p>
<ol>
<li>It must have a web font of the supported format (see above).</li>
<li>It must have some kind of list, containing the name of each icon and the
corresponding Unicode code point. If this is missing, icon fonts usually
ship with a <code spellcheck="false">.css</code> file that can be used to
extract the icon names from.</li>
</ol>
<h2>Step-by-step process</h2>
<p>As an example throughout this page, we are going to go through the steps
of integrating <a href="https://phosphoricons.com/">Phosphor Icons</a>.</p>
<h3>Creating the manifest</h3>
<p>This is the most difficult part of creating an icon pack, since it requires
processing of the icon list to match Trilium's format.</p>
<p>The icon pack manifest is a JSON file with the following structure:</p><pre><code class="language-application-ld-json">{
"icons": {
"bx-ball": {
"glyph": "\ue9c2",
"terms": [ "ball" ]
},
"bxs-party": {
"glyph": "\uec92",
"terms": [ "party" ]
}
}
}</code></pre>
<h2>Manifest format</h2>
<p>The manifest is a JSON object with an <code spellcheck="false">icons</code> map.
Each entry key is the CSS/class id you will use (Trilium uses the CSS class
when rendering). Value object:</p>
<ul>
<li>The JSON example is a sample from the Boxicons font.</li>
<li>This is simply a mapping between the CSS classes (<code spellcheck="false">bx-ball</code>),
to its corresponding code point in the font (<code spellcheck="false">\ue9c2</code>)
and the terms/aliases used for search purposes.</li>
<li>Note that it's also possible to use the unescaped glyph inside the JSON.
It will appear strange (e.g. ), but it will be rendered properly regardless.</li>
<li>The first term is also considered the “name” of the icon, which is displayed
while hovering over it in the icon selector.</li>
<li>glyph: the single character (the glyph) — can be the escaped Unicode (e.g.
"\ue9c2") or the literal character.</li>
<li>terms: array of search aliases; the first term is used as display name
in the selector.</li>
</ul>
<p>In order to generate this manifest, generally a script is needed that
processes an already existing list. In the case of Phosphor Icons, the
icon list comes in a file called <code spellcheck="false">selection.json</code> with
the following format:</p><pre><code class="language-application-ld-json">{
<p>Example minimal manifest:</p><pre><code class="language-text-x-trilium-auto">{
"icons": {
"ph-acorn": {
"glyph": "\uea3f",
"terms": ["acorn", "nut"]
},
"ph-book": {
"glyph": "\uea40",
"terms": ["book", "read"]
}
}
}</code></pre>
<aside class="admonition note">
<ul>
<li>You can supply glyph as the escaped <code spellcheck="false">\uXXXX</code> sequence
or as the actual UTF8 character.</li>
<li>It is also possible to use the unescaped glyph inside the JSON. It will
appear strange (e.g. ), but it will be rendered properly regardless.</li>
<li
>The manifest keys (e.g. <code spellcheck="false">ph-acorn</code>) should
match the class names used by the font (prefix + name is a common pattern).</li>
</ul>
</aside>
<h2>Concrete example: Phosphor Icons</h2>
<p><a href="https://phosphoricons.com/">Phosphor Icons</a> provide a
<code
spellcheck="false">selection.json</code>that includes <code spellcheck="false">properties.code</code> (the
codepoint) and <code spellcheck="false">properties.name</code> (the icon
name). The goal: convert that into Trilium's manifest.</p>
<p>Sample <code spellcheck="false">selection.json</code> excerpt:</p><pre><code class="language-text-x-trilium-auto">{
"icons": [
{
"icon": {
@ -121,9 +141,9 @@
/* [...] */
]
}</code></pre>
<p>As such, we can write a Node.js script to automatically process the manifest
file:</p><pre><code class="language-application-javascript-env-backend">import { join } from "node:path";
import { readFileSync } from "node:fs";
<p>A tiny Node.js script to produce the manifest (place <code spellcheck="false">selection.json</code> in
the same directory and run with Node 20+):</p><pre><code class="language-application-javascript-env-backend">import { join } from "node:path";
import { readFileSync, writeFileSync } from "node:fs";
function processIconPack(packName) {
const path = join(packName);
@ -143,12 +163,19 @@ function processIconPack(packName) {
};
}
return JSON.stringify({
icons
}, null, 2);
writeFileSync("manifest.json", JSON.stringify(icons, null, 2), "utf8");
console.log("manifest.json created");
}
console.log(processIconPack("light"));</code></pre>
processIconPack("light");</code></pre>
<p>What to do with the script:</p>
<ul>
<li>Put <code spellcheck="false">selection.json</code> and <code spellcheck="false">build-manifest.js</code> in
a folder.</li>
<li>Run: node build-manifest.js</li>
<li>The script writes <code spellcheck="false">manifest.json</code> — open it,
verify contents, then copy into a Trilium Code note (language: JSON).</li>
</ul>
<aside class="admonition tip">
<p><strong>Mind the escape format when processing CSS</strong>
</p>
@ -158,32 +185,6 @@ console.log(processIconPack("light"));</code></pre>
<p>As a more compact alternative, provide the un-escaped character directly,
as UTF-8 is supported.</p>
</aside>
<h3>Creating the icon pack</h3>
<ol>
<li>Create a note of type <em>Code</em>.</li>
<li>Set the language to <em>JSON</em>.</li>
<li>Copy and paste the manifest generated in the previous step as the content
of this note.</li>
<li>Go to the <a href="#root/_help_0vhv7lsOLy82">note attachment</a> and upload the
font file (in <code spellcheck="false">.woff2</code>, <code spellcheck="false">.woff</code>,
<code
spellcheck="false">.ttf</code>) format.
<ol>
<li>Trilium identifies the font to use from attachments via the MIME type,
make sure the MIME type is displayed correctly after uploading the attachment
(for example <code spellcheck="false">font/woff2</code>).</li>
<li>Make sure the <code spellcheck="false">role</code> appears as <code spellcheck="false">file</code>,
otherwise the font will not be identified.</li>
<li>Multiple attachments are supported, but only one font will actually be
used in Trilium's order of preference: <code spellcheck="false">.woff2</code>,
<code
spellcheck="false">.woff</code>, <code spellcheck="false">.ttf</code>. As such, there's not
much reason to upload more than one font per icon pack.</li>
</ol>
</li>
<li>Go back to the note and rename it. The name of the note will also be the
name of the icon pack as displayed in the list of icons.</li>
</ol>
<h3>Assigning the prefix</h3>
<p>Before an icon pack can be used, it needs to have a prefix defined. This
prefix uniquely identifies the icon pack so that it can be used throughout
@ -203,6 +204,34 @@ console.log(processIconPack("light"));</code></pre>
If the prefix doesn't match these constraints, the icon pack will be ignored
and an error will be logged in&nbsp;<a class="reference-link" href="#root/_help_bnyigUA2UK7s">Backend (server) logs</a>.</p>
</aside>
<h2>Creating the Trilium icon pack note</h2>
<ol>
<li>Create a note of type <em>Code</em>.</li>
<li>Set the language to <em>JSON</em>.</li>
<li>Rename the note. The name of the note will also be the name of the icon
pack as displayed in the list of icons.</li>
<li>Copy and paste the manifest generated in the previous step as the content
of this note.</li>
<li>Go to the <a href="#root/_help_0vhv7lsOLy82">note attachment</a> and upload the
font file (in <code spellcheck="false">.woff2</code>, <code spellcheck="false">.woff</code>,
<code
spellcheck="false">.ttf</code>) format.
<ol>
<li>Trilium identifies the font to use from attachments via the MIME type,
make sure the MIME type is displayed correctly after uploading the attachment
(for example <code spellcheck="false">font/woff2</code>).</li>
<li>Make sure the <code spellcheck="false">role</code> appears as <code spellcheck="false">file</code>,
otherwise the font will not be identified.</li>
<li>Multiple attachments are supported, but only one font will actually be
used in Trilium's order of preference: <code spellcheck="false">.woff2</code>,
<code
spellcheck="false">.woff</code>, <code spellcheck="false">.ttf</code>. As such, there's not
much reason to upload more than one font per icon pack.</li>
</ol>
</li>
<li>Add label: <code spellcheck="false">#iconPack=&lt;prefix&gt;</code> (for
Phosphor example: <code spellcheck="false">#iconPack=ph</code>).</li>
</ol>
<h3>Final steps</h3>
<ul>
<li><a href="#root/_help_s8alTXmpFR61">Refresh the client</a>
@ -217,7 +246,8 @@ console.log(processIconPack("light"));</code></pre>
</li>
<li>Optionally, assign an icon from the new icon pack to this note. This icon
will be used in the icon pack filter for a visual distinction.</li>
<li>The icon pack can then be <a href="#root/_help_mHbBMPDPkVV5">exported as ZIP</a> in
<li
>The icon pack can then be <a href="#root/_help_mHbBMPDPkVV5">exported as ZIP</a> in
order to be distributed to other users.
<ul>
<li>It's important to note that icon packs are considered “unsafe” by default,
@ -225,16 +255,17 @@ console.log(processIconPack("light"));</code></pre>
<li>Consider linking new users to the&nbsp;<a class="reference-link" href="#root/_help_gOKqSJgXLcIj">Icon Packs</a>&nbsp;documentation
in order to understand how to import and use an icon pack.</li>
</ul>
</li>
</li>
</ul>
<h3>Troubleshooting</h3>
<p>If the icon pack doesn't show up, look through the&nbsp;<a class="reference-link"
href="#root/_help_bnyigUA2UK7s">Backend (server) logs</a>&nbsp;for clues.</p>
<ul>
<li>One example is if the font could not be retrieved: <code spellcheck="false">ERROR: Icon pack is missing WOFF/WOFF2/TTF attachment: Boxicons v3 400 (dup) (XRzqDQ67fHEK)</code>.</li>
<li>Make sure the prefix is unique and not already taken by some other icon
<li
>Make sure the prefix is unique and not already taken by some other icon
pack. When there are two icon packs with the same prefix, only one is used.
The server logs will indicate if this situation occurs.</li>
<li>Make sure the prefix consists only of alphanumeric characters, hyphens
and underscore.</li>
<li>Make sure the prefix consists only of alphanumeric characters, hyphens
and underscore.</li>
</ul>

View File

@ -12408,14 +12408,14 @@
{
"type": "relation",
"name": "internalLink",
"value": "0vhv7lsOLy82",
"value": "bnyigUA2UK7s",
"isInheritable": false,
"position": 20
},
{
"type": "relation",
"name": "internalLink",
"value": "bnyigUA2UK7s",
"value": "0vhv7lsOLy82",
"isInheritable": false,
"position": 30
},

View File

@ -1,8 +1,20 @@
# Creating an icon pack
> [!NOTE]
> This page describes how to create custom icon packs. For a general description of how to use already existing icon packs, see <a class="reference-link" href="../Basic%20Concepts%20and%20Features/Themes/Icon%20Packs.md">Icon Packs</a>.
> This page explains, stepbystep, how to create a custom icon pack. For a general description of how to use already existing icon packs, see <a class="reference-link" href="../Basic%20Concepts%20and%20Features/Themes/Icon%20Packs.md">Icon Packs</a>.
## Supported formats
First read the quick flow to get the overall steps. After that there is a concrete example (Phosphor) with a small Node.js script you can run to generate the manifest.
## Quick flow (what you need to do)
1. Verify the icon set is a font (one of: .woff2, .woff, .ttf).
2. Obtain a list that maps icon names to Unicode code points (often provided as a JSON like `selection.json` or a CSS file).
3. Create a manifest JSON that maps icon ids to glyphs and search terms.
4. Create a Trilium note of type Code, set language to JSON, paste the manifest as the note content.
5. Upload the font file as an attachment to the same note (MIME type must be `font/woff2`, `font/woff`, or `font/ttf` and role `file`).
6. Add the label `#iconPack=<prefix>` to the note (prefix: alphanumeric, hyphen, underscore only).
7. Refresh the client and verify the icon pack appears in the icon selector.
## Verify the icon set
The first step is to analyze if the icon set being packed can be integrated into Trilium.
@ -14,8 +26,6 @@ Trilium only supports **font-based icon sets**, with the following formats:
| `.woff` | `font/woff` | Higher compatibility, but the font file is bigger. |
| `.ttf` | `font/ttf` | Most common, but highest font size. |
## Unsupported formats
Trilium **does not** support the following formats:
* SVG-based fonts.
@ -26,46 +36,42 @@ Trilium **does not** support the following formats:
In this case, the font must be manually converted to one of the supported formats (ideally `.woff2`).
## Prerequisites
## Manifest format
In order to create a new icon pack from a set of icons, it must meet the following criteria:
The manifest is a JSON object with an `icons` map. Each entry key is the CSS/class id you will use (Trilium uses the CSS class when rendering). Value object:
1. It must have a web font of the supported format (see above).
2. It must have some kind of list, containing the name of each icon and the corresponding Unicode code point. If this is missing, icon fonts usually ship with a `.css` file that can be used to extract the icon names from.
* glyph: the single character (the glyph) — can be the escaped Unicode (e.g. "\\ue9c2") or the literal character.
* terms: array of search aliases; the first term is used as display name in the selector.
## Step-by-step process
Example minimal manifest:
As an example throughout this page, we are going to go through the steps of integrating [Phosphor Icons](https://phosphoricons.com/).
### Creating the manifest
This is the most difficult part of creating an icon pack, since it requires processing of the icon list to match Trilium's format.
The icon pack manifest is a JSON file with the following structure:
```json
```
{
"icons": {
"bx-ball": {
"glyph": "\ue9c2",
"terms": [ "ball" ]
},
"bxs-party": {
"glyph": "\uec92",
"terms": [ "party" ]
}
}
"icons": {
"ph-acorn": {
"glyph": "\uea3f",
"terms": ["acorn", "nut"]
},
"ph-book": {
"glyph": "\uea40",
"terms": ["book", "read"]
}
}
}
```
* The JSON example is a sample from the Boxicons font.
* This is simply a mapping between the CSS classes (`bx-ball`), to its corresponding code point in the font (`\ue9c2`) and the terms/aliases used for search purposes.
* Note that it's also possible to use the unescaped glyph inside the JSON. It will appear strange (e.g. ), but it will be rendered properly regardless.
* The first term is also considered the “name” of the icon, which is displayed while hovering over it in the icon selector.
> [!NOTE]
> * You can supply glyph as the escaped `\uXXXX` sequence or as the actual UTF8 character.
> * It is also possible to use the unescaped glyph inside the JSON. It will appear strange (e.g. ), but it will be rendered properly regardless.
> * The manifest keys (e.g. `ph-acorn`) should match the class names used by the font (prefix + name is a common pattern).
In order to generate this manifest, generally a script is needed that processes an already existing list. In the case of Phosphor Icons, the icon list comes in a file called `selection.json` with the following format:
## Concrete example: Phosphor Icons
```json
[Phosphor Icons](https://phosphoricons.com/) provide a `selection.json` that includes `properties.code` (the codepoint) and `properties.name` (the icon name). The goal: convert that into Trilium's manifest.
Sample `selection.json` excerpt:
```
{
"icons": [
{
@ -95,11 +101,11 @@ In order to generate this manifest, generally a script is needed that processes
}
```
As such, we can write a Node.js script to automatically process the manifest file:
A tiny Node.js script to produce the manifest (place `selection.json` in the same directory and run with Node 20+):
```javascript
import { join } from "node:path";
import { readFileSync } from "node:fs";
import { readFileSync, writeFileSync } from "node:fs";
function processIconPack(packName) {
const path = join(packName);
@ -119,14 +125,19 @@ function processIconPack(packName) {
};
}
return JSON.stringify({
icons
}, null, 2);
writeFileSync("manifest.json", JSON.stringify(icons, null, 2), "utf8");
console.log("manifest.json created");
}
console.log(processIconPack("light"));
processIconPack("light");
```
What to do with the script:
* Put `selection.json` and `build-manifest.js` in a folder.
* Run: node build-manifest.js
* The script writes `manifest.json` — open it, verify contents, then copy into a Trilium Code note (language: JSON).
> [!TIP]
> **Mind the escape format when processing CSS**
>
@ -134,17 +145,6 @@ console.log(processIconPack("light"));
>
> As a more compact alternative, provide the un-escaped character directly, as UTF-8 is supported.
### Creating the icon pack
1. Create a note of type _Code_.
2. Set the language to _JSON_.
3. Copy and paste the manifest generated in the previous step as the content of this note.
4. Go to the [note attachment](../Basic%20Concepts%20and%20Features/Notes/Attachments.md) and upload the font file (in `.woff2`, `.woff`, `.ttf`) format.
1. Trilium identifies the font to use from attachments via the MIME type, make sure the MIME type is displayed correctly after uploading the attachment (for example `font/woff2`).
2. Make sure the `role` appears as `file`, otherwise the font will not be identified.
3. Multiple attachments are supported, but only one font will actually be used in Trilium's order of preference: `.woff2`, `.woff`, `.ttf`. As such, there's not much reason to upload more than one font per icon pack.
5. Go back to the note and rename it. The name of the note will also be the name of the icon pack as displayed in the list of icons.
### Assigning the prefix
Before an icon pack can be used, it needs to have a prefix defined. This prefix uniquely identifies the icon pack so that it can be used throughout the application.
@ -158,6 +158,18 @@ For our example with Phosphor Icons, we can use the `ph` prefix since it also ma
> [!IMPORTANT]
> The prefix must consist of only alphanumeric characters, hyphens and underscore. If the prefix doesn't match these constraints, the icon pack will be ignored and an error will be logged in <a class="reference-link" href="../Troubleshooting/Error%20logs/Backend%20(server)%20logs.md">Backend (server) logs</a>.
## Creating the Trilium icon pack note
1. Create a note of type _Code_.
2. Set the language to _JSON_.
3. Rename the note. The name of the note will also be the name of the icon pack as displayed in the list of icons.
4. Copy and paste the manifest generated in the previous step as the content of this note.
5. Go to the [note attachment](../Basic%20Concepts%20and%20Features/Notes/Attachments.md) and upload the font file (in `.woff2`, `.woff`, `.ttf`) format.
1. Trilium identifies the font to use from attachments via the MIME type, make sure the MIME type is displayed correctly after uploading the attachment (for example `font/woff2`).
2. Make sure the `role` appears as `file`, otherwise the font will not be identified.
3. Multiple attachments are supported, but only one font will actually be used in Trilium's order of preference: `.woff2`, `.woff`, `.ttf`. As such, there's not much reason to upload more than one font per icon pack.
6. Add label: `#iconPack=<prefix>` (for Phosphor example: `#iconPack=ph`).
### Final steps
* [Refresh the client](../Troubleshooting/Refreshing%20the%20application.md)