Skip to content

Themes

A theme recolors the Aero UI and the Monaco editor by mapping the editor's design tokens to your own palette. Themes are pure JSON — they carry no code, need no main, and need no activation events. Trusted core applies them, so they're safe by construction.

1. Declare the theme in aero.json

Add a contributes.themes[] entry pointing at your theme file:

json
"contributes": {
  "themes": [
    { "label": "Dracula Warm", "uiTheme": "dark", "path": "themes/dracula-warm.json" }
  ]
}
FieldRequiredNotes
labelyesName shown in the theme list and used by aero.theme.apply(label).
uiThemeyes"light" or "dark" — which base palette to extend.
pathyesRelative path to the theme JSON, inside the archive.

2. Write the theme file

The file maps Aero's design tokens (the CSS variables in src/renderer/styles/tokens.css) to colors. Unknown keys are ignored; missing keys fall back to the active built-in theme — so you only override what you want to change.

json
{
  "name": "Dracula Warm",
  "type": "dark",
  "monaco": "vs-dark",
  "tokens": {
    "bg": "#211f26",
    "raised": "#282a36",
    "panel": "#2f3140",
    "text": "#f8f8f2",
    "subtext": "#b9b6c8",
    "border": "#3b3d4d",
    "accent": "#ff9e64",
    "accent-hover": "#e8853f"
  }
}
KeyTypeMeaning
namestringDisplay name of the theme.
typestring"light" or "dark" — base palette to extend.
monacostringBase Monaco theme: "vs" (light) or "vs-dark".
tokensobjectToken → color map. Keys are token names (below).

3. The token set

These are the tokens the runtime reads off :root at run time. The terminal (xterm) and editor (Monaco) read the first group to theme their canvases — overriding those gives the most visible change. The remaining tokens refine chrome, state colors, and shadows.

Core surfaces & text (highest impact)

TokenRole
bgApp canvas — the outermost background.
raisedCards, command palette, the editor surface.
panelSidebars, tab strips, inert chrome.
surfaceGeneric resting surface.
textPrimary text.
subtextSecondary text, labels.
borderHairlines, dividers.
accentThe accent color (links, active states, focus).

Minimum viable theme

Override bg, raised, panel, text, subtext, border, and accent and you have a coherent theme. Everything else inherits from the matching built-in type.

Accent family

TokenRole
accentAccent for text/icons (keep AA-safe).
accent-hoverDarker (light) / lifted (dark) hover & press.
accent-fillThe signature clay for solid fills.
accent-softTinted background for active rows / chips.
accent-ringFocus ring color.
on-accentText drawn on a solid accent fill.

Extended surfaces & lines

TokenRole
surface-hoverHover wash on rows/buttons.
surface-activePressed surface.
mutedTertiary text, placeholders, hints.
border-strongEmphasized edges, input borders.

State colors

TokenRole
greenSuccess / additions.
yellowWarnings.
redErrors / deletions.

Scrollbars, selection, scrim & shadows

TokenRole
scrollbar-thumbScrollbar thumb.
scrollbar-thumb-hoverScrollbar thumb on hover.
selectionText selection highlight.
scrimOverlay scrim behind the command palette.
shadow-smSmall soft shadow.
shadow-mdMedium shadow (cards, popovers).
shadow-lgLarge shadow (palette, modals).

Token names are the contract

These are the exact --<token> custom-property names Aero sets on document.documentElement. Don't invent new ones — unknown keys are silently ignored.

4. How the host applies it

When a theme is selected, the host:

  1. sets each --<token> CSS custom property on document.documentElement;
  2. sets data-theme to your type ("light" or "dark");
  3. calls monaco.editor.setTheme(monaco) with your monaco base;
  4. persists the choice via Aero's existing theme.js mechanism;
  5. emits theme:change on AeroBus.

5. Applying a theme from code (optional)

If your extension also ships a command, you can switch themes programmatically via the aero.theme API:

js
const themes = await aero.theme.list();      // labels of all registered themes
await aero.theme.apply('Dracula Warm');      // switch by contributed label

This is exactly what the example dracula-warm.apply command does — see Examples.

Brand reference — Aero's built-in palette

For contrast targets, the built-in warm Claude.ai palette:

Light (the signature): bg #faf9f5 · raised #ffffff · panel #f0eee6 · text #1f1e1d · subtext #6b6a66 · border #e5e3da · accent #c15f3c · accent-fill #d97757.

Dark (warm, not cold): bg #1f1e1d · raised #262624 · panel #30302e · text #f5f4ee · subtext #b7b5ad · border #3a3a37 · accent #e08a6a · accent-fill #d97757.

Lean, AI-ready, under your control.