Skip to content

Theme tokens

A theme is a flat map of CSS custom-property values. Pluma ships two:

  • pluma-teal — default. Cool teal, written quiet.
  • pluma-rose — warm sibling. Same token shape, rose-tinted neutrals.

Switch under Settings → General → Theme. Choice persists across reloads.

Token list

All colour tokens are stored as raw R G B triples (no rgb() wrapper) so Tailwind's <alpha-value> mechanism works at the use site: bg-accent/15background-color: rgb(20 184 166 / 0.15).

Neutral scale

The ink-* tokens drive surfaces and text. ink-950 is the deepest background; ink-50 the brightest foreground. The 400-600 band sits at ~4:1 contrast against ink-900 so secondary text reads cleanly.

Token Used for
--ink-950 Page background
--ink-900 Card / surface background
--ink-850 Hover-state surface, slightly raised
--ink-800 Default border
--ink-700 Active border, hover border
--ink-600 Disabled text, low-contrast captions
--ink-500 Secondary text, uppercase labels, footnotes
--ink-400 Italic action text, mid-tone descriptions
--ink-300 Primary captions
--ink-200 Body text
--ink-100 Headline text, primary actions
--ink-50 Maximum-contrast foreground (rarely used)

Accents

Token Used for
--accent Primary actions, links, brand glyph
--accent-soft Tinted backgrounds (chips, halos) — same RGB as --accent, consumed with /<alpha>
--accent-bright "Now live" cues, badges
--speech-accent In-chat dialogue prose colour (kept warm cream across themes)

Semantic

Token Used for
--danger Errors, destructive actions
--success Confirmations, "running" states
--warning Cautions, low-priority alerts
--scrollbar-thumb Scrollbar thumb colour (typically same as --accent-bright)

Writing a theme

A theme is one .ts file under web/src/themes/. Mirror pluma-teal or pluma-rose for the token shape. Register in web/src/main.ts:

import { plumaRose } from './themes/pluma-rose'
import { myTheme } from './themes/my-theme'

registerTheme(plumaTeal)
registerTheme(plumaRose)
registerTheme(myTheme)

The theme picker (Settings → General) reads listThemes() at render time, so adding one shows up automatically.

Future: shareable themes

A planned feature (tracked under smelt-sze) will let users drop theme JSONs into a <datadir>/themes/ directory. Before that ships, Pluma will add:

  • A token-name allowlist (only the keys above; rejects anything else).
  • A value-shape validator (RGB triples only; rejects raw URLs and arbitrary CSS).
  • A Content-Security-Policy header on responses.

This is so a shared theme can't smuggle a tracking pixel via a url(https://evil.com/log) value in some unexpected token. Until the validator lands, theme registration stays code-only.