Themes
Plugins can add colour themes. They show up in Settings → Appearance next to Phantom's own themes and the editor themes it ships, like Nord, Dracula, Tokyo Night and Catppuccin.
A theme is twenty colours in the manifest. There's no code, no permissions and no dist/plugin.js, so a theme plugin can be a single package.json. Phantom builds the full set of tokens from your colours the same way it does for its own themes, so yours applies to every screen, the tabs and other plugins' frames.
Themes only colour Phantom. They can't change the game, which the client draws with its own art.
Start a theme
npm create @phantomclient/plugin@latest -- --starters themeThe theme starter puts a dark theme and a light theme in the manifest. There's no source code and nothing to build.
Write the colours
Themes go in contributes.themes, up to 40 of them. This is Phantom's dark theme with a violet accent, to copy and change:
{
"name": "phantom-plugin-midnight",
"version": "1.0.0",
"phantom": {
"id": "acme.midnight",
"title": "Midnight",
"description": "A dark theme, made from Phantom's own.",
"author": "Acme",
"contributes": {
"themes": [
{
"id": "midnight",
"title": "Midnight",
"description": "Phantom Dark with a violet accent, to start from.",
"mode": "dark",
"colors": {
"canvas": "#070708",
"shell": "#0b0b0c",
"panel": "#111113",
"surface": "#19191c",
"selected": "#1f1f23",
"elevated": "#212125",
"control": "#28282d",
"border": "#242428",
"borderStrong": "#3a3a40",
"text": "#f0f0f2",
"textSoft": "#d4d4d8",
"muted": "#98989f",
"subtle": "#76767e",
"accent": "#8b5cf6",
"accentContrast": "#ffffff",
"danger": "#f06860",
"amber": "#deb269",
"positive": "#5fbd8a",
"info": "#78b0e4",
"chart": "#4c8fe6"
}
}
]
}
}
}mode is light or dark: which of Phantom's themes yours is based on, so things like shadows suit it.
Write colours as #rrggbb. Colour names and alpha aren't allowed; Phantom works out any tints it needs.
The colours
| Key | What it paints |
|---|---|
canvas, shell | The window behind everything, and its frame. |
panel | The raised surface pages sit on. |
surface, selected | Fills on the panel: cards and rows, and the chosen one. |
elevated, control | Menus and dialogs, and inputs. |
border, borderStrong | Hairlines, and stronger lines that need to stand out. |
text, textSoft, muted, subtle | The four weights of words, strongest first. |
accent, accentContrast | Primary buttons, and the words on them. |
danger, amber, positive, info | Errors, warnings, success and neutral notes. |
chart | Bars and lines, such as a packet rate's. |
A plugin's theme gets the id plugin:<plugin id>/<theme id> so it can't clash with a built-in one. The theme above ends up as plugin:acme.midnight/midnight.
Tune it live
npm run devWith Developer mode on (Plugins → Developer), Phantom rereads the manifest every two seconds. Pick your theme in Settings → Appearance and change a colour. The terminal confirms the save and the app updates a moment later.
● 14:12:08 package.json: the manifest is valid, with 1 theme. Phantom picks it up in a moment.Check contrast
Phantom checks themes against WCAG contrast. Text needs 4.5:1 against its background. Quieter text, marks and charts need 3:1.
| Pair | At least |
|---|---|
text on panel, on canvas and on selected | 4.5:1 |
textSoft and muted on panel | 4.5:1 |
accentContrast on accent | 4.5:1 |
subtle, danger, positive and chart on panel | 3:1 |
A theme that falls short still installs, since some themes are meant to be soft, but you're told about it. phantom-plugin validate lists the failing pairs, phantom-plugin dev prints them each time you save, and the Appearance page labels the theme "Some text may be hard to read".
● The theme midnight may be hard to read in places:
muted on panel is 3.92:1, below 4.5:1, so it may be hard to readPublish it
Publish it like any other plugin, with phantom-plugin publish (see Publishing). A theme can't touch the game, so it goes live as soon as it passes the checks, unless they warn about contrast, in which case a person looks at it first. The install dialog shows no permissions, since a theme has none.