Files
2026-05-20 00:09:06 +00:00

6.8 KiB

Hermes Web UI — Themes

Hermes Web UI splits appearance into two independent pickers:

  • Theme — the mode: System, Dark, or Light. Drives the background, text, surface, and chrome colors.
  • Skin — the accent palette: built-in skins ship as named keys. Drives only the --accent family (active states, links, focus rings, primary actions).

You pick one of each and they combine, so the look adapts to your environment without losing your favorite accent — pure CSS, no Python changes needed.


Switching Appearance

Settings panel: Click the gear icon → Appearance. The Theme card toggles Light/Dark/System; the Skin grid offers the built-in accent palettes. Preview is instant — the UI updates as you click.

Slash command: Type /theme <name> in the composer. The command accepts both theme names (system, dark, light) and skin names (default, ares, mono, slate, poseidon, sisyphus, charizard, sienna, catppuccin, nous, geist-contrast). It updates the matching axis and leaves the other one alone.

Persistence: Both choices are stored in localStorage for flicker-free loading, and saved server-side via POST /api/settings (under theme and skin keys in settings.json).


Built-in Themes

Theme Description
System (default) Follows the OS prefers-color-scheme preference and updates live.
Dark Deep dark surfaces, low-glare for long sessions.
Light Bright surfaces with dark text, high contrast for daylight environments.

The theme is applied as a class on <html>: .dark is present for dark mode, absent for light. System mode tracks the OS preference at runtime.


Built-in Skins

Skin Description
Default The original Hermes gold accent. Warm and understated.
Ares Fiery red. High-energy and assertive.
Mono Neutral gray. Distraction-free, for deep focus.
Slate Slate blue-gray. Subtle and grown-up.
Poseidon Ocean blue. Calm and focused for long sessions.
Sisyphus Vivid purple. Distinctive without being loud.
Charizard Warm orange. Energetic and easy on the eyes.
Sienna Warm clay and sand earth palette. Soft and natural.
Catppuccin Catppuccin Latte/Mocha palette with Mauve accent.
Nous Steel-blue accent with dashed technical surfaces.
Geist Contrast (geist-contrast) Geist-inspired monochrome surfaces with a restrained dark-mode #FFF175 accent.

Each skin defines paired light + dark variants so it reads cleanly on either theme. The skin is applied as data-skin="<name>" on <html> (the default skin clears the attribute).


Creating a Custom Skin

A skin is a small CSS block that overrides the accent variables for both the light and dark variants:

/* Light variant */
:root[data-skin="my-skin"] {
  --accent:           #2E7D32;                   /* Active states, links, primary buttons */
  --accent-hover:     #1B5E20;                   /* Hover */
  --accent-bg:        rgba(46,125,50,0.08);      /* Soft tinted backgrounds */
  --accent-bg-strong: rgba(46,125,50,0.15);      /* Highlighted backgrounds */
  --accent-text:      #1B5E20;                   /* Text on accent bg */
}

/* Dark variant — usually lighter or more saturated for contrast */
:root.dark[data-skin="my-skin"] {
  --accent:           #66BB6A;
  --accent-hover:     #43A047;
  --accent-bg:        rgba(102,187,106,0.08);
  --accent-bg-strong: rgba(102,187,106,0.15);
  --accent-text:      #66BB6A;
}

Two ways to ship it:

  1. In the repo (built-in): add the block to static/style.css, register it in the Settings skin picker (static/index.html) and in the /theme command list (static/commands.js), then open a PR.

  2. Self-hosted (no fork): use the WebUI extensions surface — see docs/EXTENSIONS.md. Drop your CSS in HERMES_WEBUI_EXTENSION_DIR and declare it in HERMES_WEBUI_EXTENSION_STYLESHEET_URLS. No code changes needed; the skin attribute can be set from your own JS.

Tips

  • Test both themes. A skin that pops on Dark can be illegible on Light. Always check :root[data-skin] (light) and :root.dark[data-skin] (dark).
  • Pick contrasting --accent-text on --accent-bg. The strong variant appears behind small labels and chips; weak contrast there reads as blur.
  • The logo gradient uses --accent automatically, so it adapts to your skin without any extra work.
  • No server changes needed. The skin setting in settings.json accepts any string, so your custom skin name persists without code changes once you load the CSS.

Creating a Custom Theme

A full custom theme (a different overall mood, not just an accent change) is a larger task than a skin: it has to redefine the core palette variables (--bg, --surface, --text, --border, --code-bg, and friends) for one or both modes. The contract is defined in the top :root and :root.dark blocks of static/style.css — start there.

Most of the time, a custom skin is what you actually want. Reach for a custom theme only when the existing Light/Dark modes don't fit (for example, a high-contrast accessibility theme or an OLED black variant).


Font Size

Right under Theme/Skin in Settings → Appearance: Small, Default, Large. Applied as data-font-size on <html> and scales the WebUI's root font size. Persists alongside theme and skin.


How It Works Internally

  1. Theme: document.documentElement.classList.toggle('dark', isDark) — light mode removes the class. System mode tracks matchMedia('(prefers-color-scheme: dark)').
  2. Skin: document.documentElement.dataset.skin = name (or remove the attribute for default).
  3. Font size: document.documentElement.dataset.fontSize = size (or remove for default).
  4. No flash on load: a tiny inline <script> in <head> reads localStorage before the stylesheet does, so the right look is applied before paint.
  5. Server sync: preferences are saved via POST /api/settings and rehydrated on boot via GET /api/settings.

Contributing a Skin

Skins are the easiest extension point — pure CSS, no Python, no JS logic. To contribute one upstream:

  1. Add your :root[data-skin="name"] and :root.dark[data-skin="name"] blocks to static/style.css.
  2. Register it in the Settings skin picker in static/index.html and in the skin list used by cmdTheme() in static/commands.js.
  3. Test on desktop and mobile across both Light and Dark themes.
  4. Open a PR — skins are pure CSS additions with no backend changes needed.

For a custom theme (overriding the base palette), prefer opening an issue first to discuss scope, since it touches many selectors.