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 .json file (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 .json file.

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.json is 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": ""
}
FieldWhat it is
idLower-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.
nameWhat people see, up to 40 characters.
basenight, 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.
varsThe tokens below, each with a value.
cssOptional extra CSS for power users (see below).
author, descriptionOptional; up to 40 and 160 characters.

Values

  • Colours: #rgb, #rgba, #rrggbb, #rrggbbaa, rgb(), rgba(), hsl() and hsla() with plain numbers (rgba(255, 143, 79, .15), hsl(20 100% 65%)), or transparent.
  • Corners: a length such as 14px, 1rem or 0.
  • Shadow: none, or up to four shadows of 2–4 lengths and a colour, optionally inset: 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.

TokenNameWhat it colours
--bgBackdropBehind everything: the top bar and the gaps between panels
--s1Side panelsThe room, channel and people lists
--s2Main panelThe chat, and the settings
--s3RaisedInputs, cards and hover highlights
--lineLinesBorders and dividers
--text-strongStrong textHeadings, names and selected items
--textTextMessages and most other text
--mutedMuted textTimestamps, hints and labels
--airAccentThe on-air glow, main buttons and highlights
--air-inkOn accentText and icons on the accent colour
--roseDangerDelete buttons, errors and a muted mic

Every other token

TokenNameWhat it colours
--s4PressedSelected items and pressed buttons (auto)
--line-softSoft linesFaint dividers, usually see-through
--faintFaint textPlaceholders and the quietest labels
--air-softAccent tintSelections and focus halos (auto)
--air-glowAccent glowThe glow around people who are talking (auto)
--mintOnlineOnline status and success
--rose-inkOn dangerText on danger buttons
--lilacLinksLinks and mentions
--lilac-softMention tintBehind mentions (auto)
--idleAwayAway status
--offlineOfflineOffline status
--pop-bgMenusMenus, popovers and dialogs (auto)
--stage-bgVoice stageBehind voice and video tiles (auto)
--tile-mixVoice tilesMixed with each person's colour on their tile (auto)
--tile-screenShared screensBehind a shared screen (auto)
--scrimDimmerOver the app behind dialogs, usually see-through
--shadowShadowUnder menus and dialogs
--r-panelPanel cornersHow round panels are
--r-cardCard cornersHow round cards and list items are
--r-ctlControl cornersHow 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.

This page is built from themes/README.md in The Hub's source.