Ship all three blog themes (Coffee/Harbor/Gruvbox) with palette images, theme-switch guide; make 3D viewer read theme CSS variables

This commit is contained in:
trilium-share-gruvbox
2026-09-07 19:43:51 +02:00
parent b7a59be0d7
commit 9806483add
11 changed files with 3877 additions and 14 deletions
+29 -8
View File
@@ -18,13 +18,27 @@ mind-map notes.
Everything is driven by Trilium's built-in **Public Share** feature plus a few frontend/backend scripts
no external dependencies (except `highlight.js`, vendored in `lib/`).
![Coffee palette](assets/palette.png)
## Themes
*Palette from the [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) (Coffee / Coffee Latte) repository, MIT licensed.*
The share ships with **three themes**, each with a dark and a light variant (toggled via the header
switch / `html.theme-light` + `html.theme-dark`). They live in `theme/` and are set on the blog by
pointing the `shareCss` relation at the matching note see
[`docs/switch-blog-theme.md`](docs/switch-blog-theme.md) for the step-by-step guide.
| Theme | File | Palette (dark / light) | Source |
|---|---|---|---|
| **Coffee** (active by default) | `theme/blog_share_theme_coffee.css` | espresso `#1d150f` / latte-cream `#f0e2d0`, caramel `#e0a15c` · cream `#E8E3C3` / ink `#2D1A1D`, berry `#8e2d53` | [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) (MIT) |
| **Harbor** | `theme/blog_share_theme_harbor.css` | near-black `#1b1b1b` / cream `#efebdc`, coral `#e75a50` · paper `#dfe4c4` / ink `#1c2d28`, nord-blue `#5e81ac` | [HANCORE-linux/omarchy-harbor-theme](https://github.com/HANCORE-linux/omarchy-harbor-theme) (MIT) |
| **Gruvbox** | `theme/blog_share_theme_gruvbox.css` | bg `#282828` / fg `#ebdbb2`, accents red/green/yellow/blue | [morhetz/gruvbox](https://github.com/morhetz/gruvbox) (MIT) |
![Coffee palette](assets/palette-coffee.png)
![Harbor palette](assets/palette-harbor.png)
![Gruvbox palette](assets/palette-gruvbox.png)
## Features
- Coffee dark / Coffee Latte light theme for the share, with a theme toggle in the header.
- Coffee dark / Coffee Latte light theme for the share, with a theme toggle in the header
(Gruvbox and Harbor are available as well see the [Themes](#themes) section).
- Blog index, category pages, sub-categories, tags and tag overview.
- **Menu sorting**: articles in the left menu are sorted by publication date (newest first)
and labelled `Fri 2026-08-28 <shortened title>` automatically. All date displays
@@ -66,7 +80,9 @@ no external dependencies (except `highlight.js`, vendored in `lib/`).
|---|---|---|
| `blog_generator.js` | backend script that generates index, feed, sitemap, search data, category & tag pages | code, `application/javascript;env=backend` |
| `blog_publish_manager.js` | backend script that mirrors notes between `Unpublished` and the blog | code, `application/javascript;env=backend` |
| `theme/blog_share_theme.css` | the Coffee share theme (Coffee / Coffee Latte) | code, `text/css` |
| `theme/blog_share_theme_coffee.css` | Coffee / Coffee Latte share theme (active by default) | code, `text/css` |
| `theme/blog_share_theme_harbor.css` | Harbor / Harbordark share theme | code, `text/css` |
| `theme/blog_share_theme_gruvbox.css` | Gruvbox light/dark share theme | code, `text/css` |
| `theme/favicon.svg` | default site favicon | code (SVG) |
| `theme/cookie.woff2` | "Cookie" font for the Buy me a coffee button | file, `font/woff2` |
| `lib/highlight.js` | vendored highlight.js | code, `application/javascript` |
@@ -124,7 +140,9 @@ Under **`.blog`** create one code note per file, using the file name as the note
| Note title | File | mime |
|---|---|---|
| `blog_share_theme.css` | `theme/blog_share_theme.css` | `text/css` |
| `blog_share_theme_coffee.css` | `theme/blog_share_theme_coffee.css` | `text/css` |
| `blog_share_theme_harbor.css` | `theme/blog_share_theme_harbor.css` | `text/css` |
| `blog_share_theme_gruvbox.css` | `theme/blog_share_theme_gruvbox.css` | `text/css` |
| `favicon` | `theme/favicon.svg` | (SVG) |
| `Cookie` | `theme/cookie.woff2` | `font/woff2` (file note) |
| `highlight.js` | `lib/highlight.js` | `application/javascript` |
@@ -176,7 +194,7 @@ mime `application/javascript;env=backend`) and `blog_publish_manager` (from
| `shareOpenGraphURL` | `https://YOUR-INSTANCE.example.com/` | |
| `shareOpenGraphColor` | `#83a598` | |
| `iconClass` | `bx bx-edit-alt` | |
| `~shareCss` | → `blog_share_theme.css` | yes |
| `~shareCss` | → `blog_share_theme_coffee.css` (the active theme) | yes |
| `~shareFavicon` | → `favicon` | yes |
| `~shareTemplate` | → `share template` | yes |
| `~shareOpenGraphImage` | → `og-banner` | |
@@ -297,7 +315,10 @@ server {
## Customisation
- **Theme**: edit `theme/blog_share_theme.css` (Coffee / Coffee Latte via `html.theme-light/dark`).
- **Theme**: pick one of `theme/blog_share_theme_{coffee,harbor,gruvbox}.css` and point the blog's
`shareCss` relation at it see [`docs/switch-blog-theme.md`](docs/switch-blog-theme.md). Each theme
ships dark + light via `html.theme-light/dark`. The 3D viewer and Mermaid rendering read the theme's
CSS variables, so they adapt automatically to whichever theme is active.
- **Buy me a coffee**: edit `scripts/bmc_button.js` (link, text, copyright) the font is
referenced via `__COOKIE_FONT_ID__`.
- **Category logos**: copy the `examples/` icon + `ignis8_menu_icon.js` pattern to give a
@@ -309,6 +330,6 @@ This project is licensed under the **GNU General Public License v3.0** see [
The repository also contains third-party assets that keep their own licenses:
`lib/highlight.js` (BSD-3-Clause), `lib/hljs_line_numbers.js` (MIT),
`theme/cookie.woff2` (SIL OFL 1.1), `assets/palette.png` (MIT) and the embedded
`theme/cookie.woff2` (SIL OFL 1.1), the three palette images `assets/palette-*.png` (MIT) and the embedded
Trilium logo in `scripts/bmc_button.js` (AGPL-3.0). See
[`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md) for details.
+6 -2
View File
@@ -8,8 +8,12 @@ The following files in this repository are **not** covered by the GPL-3.0 and ke
| `lib/highlight.js` | [Highlight.js](https://github.com/highlightjs/highlight.js) v11.9.0 | [BSD-3-Clause](#bsd-3-clause) |
| `lib/hljs_line_numbers.js` | [highlightjs-line-numbers](https://github.com/yury-dymov/highlightjs-line-numbers) | [MIT](#mit-license) |
| `theme/cookie.woff2` | "Cookie" font, © 2011 [Ania Kruk](https://fonts.google.com/specimen/Cookie) | [SIL Open Font License 1.1](https://openfontlicense.org/) |
| `assets/palette.png` | Coffee / Coffee Latte palette image generated from [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) (repo: MIT) | [MIT](#mit-license) |
| `theme/blog_share_theme.css` | Coffee / Coffee Latte color scheme from [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) (repo: MIT) | [MIT](#mit-license) |
| `assets/palette-coffee.png` | Coffee / Coffee Latte palette image generated from [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) (repo: MIT) | [MIT](#mit-license) |
| `assets/palette-harbor.png` | Harbor / Harbordark palette image generated from [HANCORE-linux/omarchy-harbor-theme](https://github.com/HANCORE-linux/omarchy-harbor-theme) (repo: MIT) | [MIT](#mit-license) |
| `assets/palette-gruvbox.png` | Gruvbox palette image generated from [morhetz/gruvbox](https://github.com/morhetz/gruvbox) (repo: MIT) | [MIT](#mit-license) |
| `theme/blog_share_theme_coffee.css` | Coffee / Coffee Latte color scheme from [megabyte0x/omarchy-coffee-theme](https://github.com/megabyte0x/omarchy-coffee-theme) (repo: MIT) | [MIT](#mit-license) |
| `theme/blog_share_theme_harbor.css` | Harbor / Harbordark color scheme from [HANCORE-linux/omarchy-harbor-theme](https://github.com/HANCORE-linux/omarchy-harbor-theme) (repo: MIT) | [MIT](#mit-license) |
| `theme/blog_share_theme_gruvbox.css` | Gruvbox color scheme from [morhetz/gruvbox](https://github.com/morhetz/gruvbox) (repo: MIT) | [MIT](#mit-license) |
| `lib/3d/three.min.js`, `lib/3d/OrbitControls.js`, `lib/3d/STLLoader.js` | [three.js](https://github.com/mrdoob/three.js) r128 | [MIT](#mit-license) |
| `lib/3d/occt-import-js.js`, `lib/3d/occt-import-js.wasm` | [occt-import-js](https://github.com/kovacsv/occt-import-js) (OpenCASCADE, WASM) | [LGPL-2.1](#lgpl-21) |
| `lib/3d/occt-wasm-bundle.js`, `lib/3d/occt-wasm.wasm` | [occt-wasm](https://github.com/andymai/occt-wasm) (OpenCASCADE **OCCT V8**, WebAssembly, esbuild-bundled `OcctKernel`) | [MIT OR Apache-2.0](#mit-license) |
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

+65
View File
@@ -0,0 +1,65 @@
# Switching the blog theme
The blog theme is **not** selected by a file name it is controlled by the **`~shareCss` relation**
on the blog root note (`Richard's Blog`) inside Trilium. The relation points at a `text/css` note
that holds the theme stylesheet. Everything below uses the notes of this repository's live blog;
note IDs are given for reference.
## Available themes
| Theme | Repo file | Live note | Dark / light |
|---|---|---|---|
| Coffee (default) | `theme/blog_share_theme_coffee.css` | `NqZXcFRGi3II` | espresso / latte-cream |
| Harbor | `theme/blog_share_theme_harbor.css` | `Ep54EDTwKxup` | harbordark / paper |
| Gruvbox | `theme/blog_share_theme_gruvbox.css` | `edhL5cByGwqA` | gruvbox dark / light |
All three themes include a `html.theme-light` + `html.theme-dark` block, theme-aware **3D viewer**
overlays and **Mermaid** rendering. The 3D viewer reads the theme's CSS variables
(`--background-primary` for the canvas, `--background-active` for the model material), so it adapts
automatically to whichever theme is active no script changes needed.
## How the active theme is resolved
1. The blog root note (`Richard's Blog`, `OKjkpgmXZY1L`) carries `~shareCss``blog_share_theme_*.css`.
2. The target note is a `code` note with mime `text/css` living under `.blog` (the share system note).
3. The public share renders the page and loads that note's CSS.
## Switching themes
Pick the target note and re-point the relation. The theme note already exists in Trilium (see the
table above), so only the relation needs to change.
### Option A via the Trilium UI
1. Find the target theme note under `.blog` (e.g. `blog_share_theme_gruvbox.css`).
2. Open `Richard's Blog` → Attributes → edit the `~shareCss` relation to point at that note.
3. Hard-refresh the blog (Ctrl+F5). Done.
### Option B via ETAPI (scriptable)
```bash
TOKEN="your-etapi-token"
SERVER="https://notes.familie-zink.org"
# 1. Delete the current shareCss relation on the blog root (OKjkpgmXZY1L)
curl -X DELETE "$SERVER/etapi/attributes/<attributeId>" -H "Authorization: $TOKEN"
# 2. Point shareCss at the desired theme note (example: Gruvbox edhL5cByGwqA)
curl -X POST "$SERVER/etapi/attributes" \
-H "Authorization: $TOKEN" -H "Content-Type: application/json" \
-d '{"noteId":"OKjkpgmXZY1L","type":"relation","name":"shareCss","value":"edhL5cByGwqA","isInheritable":true}'
```
The attribute ID of the old `shareCss` relation can be looked up via
`GET $SERVER/etapi/notes/OKjkpgmXZY1L/attributes` (or the Trilium attribute list on the note).
### Option C create a fresh theme note from this repo
If you want to add or customise a theme on a new Trilium instance:
1. Create a `code` note with mime `text/css` under `.blog`, title e.g. `blog_share_theme_coffee.css`.
2. Copy the contents of the matching `theme/blog_share_theme_*.css` file into it.
3. Point `~shareCss` on the blog root at the new note (Option A or B).
> **Tip:** After a theme/content change the page may be cached do a hard reload. If you run
> nginx proxy caching, also clear the cache (see `AGENTS.md` / the nginx guide).
+5 -4
View File
@@ -232,8 +232,9 @@
var initSize = canvasSize();
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(initSize.w, initSize.h);
var isLight = document.documentElement.classList.contains("theme-light");
renderer.setClearColor(isLight ? 0xE8E3C3 : 0x1d150f, 1);
var cs = getComputedStyle(document.documentElement);
function cssVar(n, fb) { var v = (cs.getPropertyValue(n) || "").trim(); return v || fb; }
renderer.setClearColor(cssVar("--background-primary", "#1d150f"), 1);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, initSize.w / initSize.h, 0.1, 1000000);
@@ -309,7 +310,7 @@
geo.setAttribute("position", new THREE.Float32BufferAttribute(e.data.positions, 3));
if (e.data.indices) geo.setIndex(new THREE.BufferAttribute(e.data.indices, 1));
fixWinding(geo);
var mat = new THREE.MeshStandardMaterial({ color: isLight ? 0x8e2d53 : 0xe0a15c, metalness: 0.2, roughness: 0.6, side: THREE.DoubleSide, flatShading: false });
var mat = new THREE.MeshStandardMaterial({ color: cssVar("--background-active", "#e0a15c"), metalness: 0.2, roughness: 0.6, side: THREE.DoubleSide, flatShading: false });
chunkGeometry(geo, group, mat);
var tris = (e.data.indices && e.data.indices.length) ? e.data.indices.length / 3 : e.data.positions.length / 9;
fit();
@@ -331,7 +332,7 @@
} else {
var geo = new THREE.STLLoader().parse(data.buffer);
geo.computeVertexNormals();
var mat = new THREE.MeshStandardMaterial({ color: isLight ? 0x8e2d53 : 0xe0a15c, metalness: 0.25, roughness: 0.55, flatShading: true });
var mat = new THREE.MeshStandardMaterial({ color: cssVar("--background-active", "#e0a15c"), metalness: 0.25, roughness: 0.55, flatShading: true });
group.add(new THREE.Mesh(geo, mat));
fit();
showCanvas();
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff