The Hub themes
A theme is a small JSON file: a base theme plus the colours you want to change. Everything you leave out comes from the base, so a handful of colours is enough to make The Hub feel like yours.
There are three ways to make or get one:
- In the app: Settings → Appearance → Custom themes → Create theme. The editor starts from the small base below, shows every change on the whole app as you make it, and can Export the result as a
.jsonfile (and copy it). - By hand: copy
_base.json, change the colours in a text editor, then import it or install it on the server. - From someone else: Import in Settings → Appearance takes a pasted theme or a
.jsonfile.
Your own themes are kept in your browser (export them to keep a copy). Themes in this folder are for everyone on the server.
Install a theme for everyone
Put the .json file in this themes/ folder. It shows up for everyone in Settings → Appearance → Custom themes, marked Server. To change it, edit the file; to remove it, delete the file (people using it go back to its base theme).
- Files whose name starts with
_are ignored (like_base.json, the guide). - Invalid files are skipped, unknown tokens and unsafe values are dropped, and files over 64 KB are ignored.
sunset.jsonis a complete example: a warm dark theme that sets every colour.
The format
{
"id": "sunset",
"name": "Sunset",
"base": "night",
"author": "The Hub",
"description": "Warm dusk: plum panels, a glowing orange accent and lavender links.",
"vars": {
"--bg": "#1c1220",
"--air": "#ff8f4f"
},
"css": ""
}
| Field | What it is |
|---|---|
id | Lower-case letters, digits and dashes, up to 40 characters, starting with a letter or digit. Unique: a server file without an id uses its file name. |
name | What people see, up to 40 characters. |
base | night, day, mono or mono-light. Tokens you leave out come from it, and so does anything the base does on its own (Monochrome's status shapes, for example). Build light themes on day or mono-light. |
vars | The tokens below, each with a value. |
css | Optional extra CSS for power users (see below). |
author, description | Optional; up to 40 and 160 characters. |
Values
- Colours:
#rgb,#rgba,#rrggbb,#rrggbbaa,rgb(),rgba(),hsl()andhsla()with plain numbers (rgba(255, 143, 79, .15),hsl(20 100% 65%)), ortransparent. - Corners: a length such as
14px,1remor0. - Shadow:
none, or up to four shadows of 2–4 lengths and a colour, optionallyinset:0 18px 50px rgba(0, 0, 0, .5).
Anything else (names like red, var(), url(), gradients, ; or braces) is dropped, in the app and on the server alike.
The small base
The colours that matter most. _base.json has them with Night's values, and the in-app editor starts from them.
| Token | Name | What it colours |
|---|---|---|
--bg | Backdrop | Behind everything: the top bar and the gaps between panels |
--s1 | Side panels | The room, channel and people lists |
--s2 | Main panel | The chat, and the settings |
--s3 | Raised | Inputs, cards and hover highlights |
--line | Lines | Borders and dividers |
--text-strong | Strong text | Headings, names and selected items |
--text | Text | Messages and most other text |
--muted | Muted text | Timestamps, hints and labels |
--air | Accent | The on-air glow, main buttons and highlights |
--air-ink | On accent | Text and icons on the accent colour |
--rose | Danger | Delete buttons, errors and a muted mic |
Every other token
| Token | Name | What it colours |
|---|---|---|
--s4 | Pressed | Selected items and pressed buttons (auto) |
--line-soft | Soft lines | Faint dividers, usually see-through |
--faint | Faint text | Placeholders and the quietest labels |
--air-soft | Accent tint | Selections and focus halos (auto) |
--air-glow | Accent glow | The glow around people who are talking (auto) |
--mint | Online | Online status and success |
--rose-ink | On danger | Text on danger buttons |
--lilac | Links | Links and mentions |
--lilac-soft | Mention tint | Behind mentions (auto) |
--idle | Away | Away status |
--offline | Offline | Offline status |
--pop-bg | Menus | Menus, popovers and dialogs (auto) |
--stage-bg | Voice stage | Behind voice and video tiles (auto) |
--tile-mix | Voice tiles | Mixed with each person's colour on their tile (auto) |
--tile-screen | Shared screens | Behind a shared screen (auto) |
--scrim | Dimmer | Over the app behind dialogs, usually see-through |
--shadow | Shadow | Under menus and dialogs |
--r-panel | Panel corners | How round panels are |
--r-card | Card corners | How round cards and list items are |
--r-ctl | Control corners | How round inputs and small buttons are |
(auto): when a theme leaves it out, it is worked out from the theme's other colours (for example the accent tint from --air, menus from --s1 and --bg) rather than taken from the base, so a theme made from the small base stays in one family. The rest come straight from the base theme.
Some settings still win over a theme, as they should: Accent colour replaces --air (and its tint, glow and ink), Corners set to Sharp or Round replaces the theme's corners, and High contrast strengthens the text and lines using the theme's own colours.
Extra CSS
css is added to the whole app, after the theme's colours, exactly as written. It can restyle anything: fonts, spacing, a background picture. Target your theme with [data-custom-theme="<id>"], and the base with [data-theme="night"] etc.
"css": ".msg-author { letter-spacing: .02em; }\n[data-custom-theme=\"sunset\"] .topbar { background: linear-gradient(90deg, #2a1424, #1c1220); }"
It is capped at 20,000 characters, and < is written as the CSS escape \3c so it can never break out of its <style>.
A word of warning: CSS with url(), @import or image-set() can load files from other websites, which then see the IP address of everyone using the theme, and what they send can change at any time. The app warns about this when someone imports such a theme. Only use extra CSS you trust; on a server theme, it is your responsibility as the owner.
For plugin authors
Plugins can add themes with api.themes.register(def), using this same format; the theme disappears when the plugin is turned off (and people using it go back to its base theme). In the app, CustomThemes.list() returns every theme someone can pick.