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:
"contributes": {
"themes": [
{ "label": "Dracula Warm", "uiTheme": "dark", "path": "themes/dracula-warm.json" }
]
}| Field | Required | Notes |
|---|---|---|
label | yes | Name shown in the theme list and used by aero.theme.apply(label). |
uiTheme | yes | "light" or "dark" — which base palette to extend. |
path | yes | Relative 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.
{
"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"
}
}| Key | Type | Meaning |
|---|---|---|
name | string | Display name of the theme. |
type | string | "light" or "dark" — base palette to extend. |
monaco | string | Base Monaco theme: "vs" (light) or "vs-dark". |
tokens | object | Token → 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)
| Token | Role |
|---|---|
bg | App canvas — the outermost background. |
raised | Cards, command palette, the editor surface. |
panel | Sidebars, tab strips, inert chrome. |
surface | Generic resting surface. |
text | Primary text. |
subtext | Secondary text, labels. |
border | Hairlines, dividers. |
accent | The 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
| Token | Role |
|---|---|
accent | Accent for text/icons (keep AA-safe). |
accent-hover | Darker (light) / lifted (dark) hover & press. |
accent-fill | The signature clay for solid fills. |
accent-soft | Tinted background for active rows / chips. |
accent-ring | Focus ring color. |
on-accent | Text drawn on a solid accent fill. |
Extended surfaces & lines
| Token | Role |
|---|---|
surface-hover | Hover wash on rows/buttons. |
surface-active | Pressed surface. |
muted | Tertiary text, placeholders, hints. |
border-strong | Emphasized edges, input borders. |
State colors
| Token | Role |
|---|---|
green | Success / additions. |
yellow | Warnings. |
red | Errors / deletions. |
Scrollbars, selection, scrim & shadows
| Token | Role |
|---|---|
scrollbar-thumb | Scrollbar thumb. |
scrollbar-thumb-hover | Scrollbar thumb on hover. |
selection | Text selection highlight. |
scrim | Overlay scrim behind the command palette. |
shadow-sm | Small soft shadow. |
shadow-md | Medium shadow (cards, popovers). |
shadow-lg | Large 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:
- sets each
--<token>CSS custom property ondocument.documentElement; - sets
data-themeto yourtype("light"or"dark"); - calls
monaco.editor.setTheme(monaco)with yourmonacobase; - persists the choice via Aero's existing
theme.jsmechanism; - emits
theme:changeonAeroBus.
5. Applying a theme from code (optional)
If your extension also ships a command, you can switch themes programmatically via the aero.theme API:
const themes = await aero.theme.list(); // labels of all registered themes
await aero.theme.apply('Dracula Warm'); // switch by contributed labelThis 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.