Custom themes in Typhon
Colors, radii, spacing and fonts in one small file. How to write it, import it and restore the default appearance.
Updated October 10, 2026
Where to find it in the launcher
All themes live in Settings → Appearance. That is also where you will find:
- theme picker — click a card. "System" switches between the built-in Light and Dark themes to follow your system setting;
- import — the "Import" button loads a theme file from disk (a
.typhonthemeor.jsonfile); - editor — opens under the selected theme: the name, the token values with a color picker for colors, and the "Custom CSS" field under the "Advanced" button. A live preview sits next to it;
- export — saves the selected theme to a
.typhonthemefile so you can move it to another computer or share it; - delete — custom themes only. The built-in "Dark", "Light" and "High contrast" themes can be neither deleted nor overwritten: if you edit a built-in theme, your changes are saved as a new theme next to it.
Custom themes are stored in themes.json in the Typhon data folder: %APPDATA%\Typhon\ on Windows and ~/Library/Application Support/Typhon/ on macOS. There is no need to edit it by hand — use import and export to move themes around.
Quick start
Save the text below as dusk.typhontheme (a .json extension works too), then open Settings → Appearance → Import in the launcher and pick that file. The theme appears in the list with a "custom" tag — click it to apply.
{
"version": 1,
"theme": {
"id": "dusk",
"name": "Dusk",
"base": "dark",
"tokens": {
"--bg": "#0d1117",
"--bg-sidebar": "#0b0e13",
"--surface": "#131922",
"--surface-2": "#18202b",
"--surface-3": "#202a37",
"--surface-4": "#293444",
"--border": "rgba(255, 255, 255, 0.08)",
"--border-strong": "rgba(255, 255, 255, 0.14)",
"--hover": "rgba(255, 255, 255, 0.05)",
"--hover-strong": "rgba(255, 255, 255, 0.08)",
"--text": "#e8edf3",
"--text-2": "#a0abb8",
"--text-3": "#6b7785",
"--accent": "#2fb3a3",
"--accent-hover": "#3cc4b3",
"--accent-subtle": "rgba(47, 179, 163, 0.15)",
"--accent-text": "#6fd6c8",
"--accent-ring": "rgba(47, 179, 163, 0.5)",
"--radius-md": "1rem",
"--radius-lg": "1.4rem"
},
"css": ".sidebar { border-right: 1px solid var(--border); }"
}
}
From here it is easiest to fine-tune the theme in the launcher's own editor: select it, change the values, check the preview and click "Save".
File format
A theme file is JSON with two top-level fields: version (currently always 1) and theme. Any extra field is an import error, so a typo in a field name does not slip through unnoticed.
| Field | What it is | Rules |
|---|---|---|
id | Theme identifier | Lowercase Latin letters, digits and hyphens, up to 64 characters, starting with a letter or digit |
name | Name in the theme list | 1 to 64 characters, no control characters; repeated spaces are collapsed |
base | Base palette | dark or light |
tokens | Interface variable values | Only the names from the tables below |
css | Custom CSS | Optional, up to 32 KiB |
The whole file must be no larger than 256 KiB. The builtIn and updatedAt fields that appear in an exported file are accepted on import, but the launcher sets them itself.
Importing again. If a custom theme with the same id already exists, the import replaces it, which makes it easy to update a theme you edit in an external editor. If the id matches a built-in theme (dark, light, contrast), the theme is saved under a new identifier such as dark-2 and the built-in one stays as it was.
What base does. The launcher uses it to tell whether the theme is light or dark and to pick shades of the personal accent to match. base does not fill in missing colors: a token the theme does not define is taken from the built-in dark palette. A light theme therefore has to define every color from the tables, or part of the interface will stay dark.
Tokens
A token is a CSS variable the interface is built on. A theme may set any number of them; the rest keep their built-in values. Sizes are given in rem: with the interface size at 100% (Settings → General → Interface size), 1rem equals 11 pixels, and everything defined in rem scales together with that setting.
Colors
| Token | Where it is used |
|---|---|
--bg | Window background |
--bg-sidebar | Sidebar background |
--surface | Cards and first-level panels |
--surface-2 | Second-level surfaces: input fields, nested blocks |
--surface-3 | Third-level surfaces |
--surface-4 | The most raised elements: buttons, popovers |
--border | Regular borders and dividers |
--border-strong | Prominent borders |
--hover | Hover highlight |
--hover-strong | Stronger highlight on hover and press |
--text | Primary text |
--text-2 | Secondary text |
--text-3 | Muted text: captions, hints |
--accent | Accent color: primary buttons, selection |
--accent-hover | Accent on hover |
--accent-subtle | Pale accent background |
--accent-text | Accent text and links |
--accent-ring | Focus ring and active field border |
--success, --success-subtle | Success and its pale background |
--warning, --warning-subtle | Warning and its pale background |
--danger, --danger-subtle | Error, dangerous action and their pale background |
Radii and spacing
| Token | Built-in value |
|---|---|
--radius-xs, --radius-sm, --radius-md, --radius-lg, --radius-xl | 0.4rem, 0.6rem, 0.8rem, 1.2rem, 1.6rem |
--cut | 0.2rem — corner radius of the active sidebar item marker |
--space-1 … --space-6 | 0.4rem … 2.4rem in steps of 0.4rem |
--space-8, --space-10, --space-12 | 3.2rem, 4rem, 4.8rem |
Layout
| Token | Built-in value |
|---|---|
--page-x | 3.6rem — page side margins |
--page-max | 232rem — maximum content width |
--prose-max | 76rem — maximum width of long text |
--sidebar-w | 23.2rem — sidebar width |
--topbar-h | 5.6rem — top bar height |
--control-sm, --control-md, --control-lg | 3.2rem, 3.8rem, 4.4rem — button and field height |
Text
| Token | Built-in value |
|---|---|
--font-sans | Inter Tight Variable, Inter, Segoe UI, system-ui, sans-serif |
--font-xs, --font-sm, --font-md, --font-lg, --font-xl | 1.3rem, 1.4rem, 1.5rem, 1.7rem, 1.9rem |
--font-title | 3.2rem — page titles |
--font-hero | clamp(3.6rem, 2.6vw, 4.4rem) — not currently used in the interface |
--tracking-title, --tracking-heading | -0.024em, -0.012em — letter spacing of headings |
Fonts come only from those installed on the system: a theme cannot load its own font file. Write font names in --font-sans without quotes, separated by commas: Segoe UI, sans-serif.
Animations and shadows
| Token | Built-in value |
|---|---|
--ease | cubic-bezier(0.2, 0.8, 0.2, 1) — the curve of all transitions |
--dur, --dur-fast, --dur-panel | 160ms, 120ms, 180ms — transition durations |
--shadow-pop | Shadow of popup menus and tooltips |
--shadow-modal | Shadow of modal windows |
--ui-scale is not part of a theme: the interface size is controlled by a separate setting, and a theme cannot override it.
Allowed values
A token value is 1 to 120 characters from this set: Latin letters, digits, the space and # . , % ( ) / + * -. That is enough for colors (#1a2b3c, rgb(26 43 60 / 80%), hsl(210 40% 16%), color-mix(in srgb, #fff 8%, transparent)), sizes, calc(...), clamp(...) and references to other tokens via var(--accent).
Not allowed: quotes, semicolons, curly braces, !important, url(, expression( and /*.
Custom CSS
Tokens cover almost everything, but sometimes you want to change something that has no variable. For that a theme has the css field, shown in the editor as "Custom CSS" under the "Advanced" button. The launcher adds this CSS to the page on top of the built-in styles for as long as the theme is active.
Limits:
- no more than 32 KiB;
@import,url(,expression(,javascript:,behavior:,-moz-bindingand</styleare forbidden — a theme cannot load anything from the network or from disk, so there are no images or custom fonts through CSS either. The launcher's editor checks the CSS together with its comments, so do not write these words there either;--ui-scaleis forbidden in CSS for the same reason as in tokens;- curly braces must be balanced and nested no deeper than four levels.
@mediaand@keyframeswork.
Specificity. The launcher's component styles are scoped to the components themselves, so a rule such as .sidebar { background: ... } loses to the built-in one if it changes the same property. Properties a component does not set apply as written; to override one that is set, add !important.
Class names are not a contract. The tokens in the tables above are the stable interface for themes. Class names in the markup are internal to the launcher: they can change in any version, and CSS that relies on them may stop working after an update.
An example for a light theme: the scrollbar in the built-in styles is designed for a dark background and is barely visible on a light one.
::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.14);
background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.24);
background-clip: padding-box;
}
In a JSON file the CSS is written as a single line with line breaks as \n. It is easier to paste it into the "Custom CSS" field in the editor and then export the theme — the launcher writes it out correctly.
Personal accent
The Appearance section also has "Personal accent" — a color applied on top of any theme that adjusts its shades for readability. While one is selected, the theme's accent tokens (--accent, --accent-hover, --accent-subtle, --accent-text, --accent-ring) are not visible. To see the theme's own accent, choose "Theme color" in the personal accent. The personal accent is your local setting and is not included in an exported theme file.
If a theme breaks the window
Bad CSS can hide or cover the interface so that you cannot reach the settings. There are three ways to restore the default appearance:
- The "Reset appearance" button in the bottom right corner of the window. It is always visible while a custom theme, or a theme with custom CSS, is active, and the theme's CSS does not style it.
- The Ctrl+Shift+Alt+T shortcut in the launcher window.
- Settings → Appearance → Reset, if you can still reach the settings: the "Reset" button there asks for confirmation.
In addition, a new theme counts as a trial for its first five seconds. If the launcher closes or crashes during that time, it returns to the previous theme on the next start.
Reset deletes all custom themes, along with the personal accent and the icon tint, and restores the built-in Dark theme. The corner button and the shortcut act immediately, without asking. So export the theme you are working on to a file from time to time — after a reset you can import it back and fix whatever broke the window.
Import errors
| Message | What to check |
|---|---|
| Invalid theme file path | The file has the wrong extension: it must be .typhontheme or .json |
| The theme file exceeds the allowed size | The file is larger than 256 KiB |
| Unsupported theme file version | version must be 1 |
| Invalid theme id | id: lowercase Latin letters, digits and hyphens |
| Invalid theme name | name is empty, longer than 64 characters or contains control characters |
| Invalid theme base palette | base must be dark or light |
| Unknown theme token name | The token is not in the tables above, or it is --ui-scale |
| Invalid theme token value | An empty value, longer than 120 characters or a character outside the allowed set — most often quotes |
| The custom CSS exceeds the allowed size | The CSS is larger than 32 KiB |
| The custom CSS contains a forbidden construct | The CSS contains @import, url( or another word from the list above |
| Unbalanced or too deeply nested braces in the CSS | An extra or unclosed brace, or nesting deeper than four levels |
| Could not complete the theme action | The file does not parse as JSON or contains an extra field |
If the launcher says "Could not complete the theme action", the file most often does not parse as JSON or has an extra field. Check the commas, the quotes around field names and the names of the top-level fields.