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.

  • Make one in the app: Settings → Appearance → Custom themes → Create theme. The editor shows every change on the whole app as you make it, and Export saves it as a .json file.
  • Share it: send it to the community. Once approved, it appears for everyone under Settings → Appearance → Community themes.
  • Use someone else's: pick a community theme, or Import a .json file in Settings → Appearance.

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. Themes sent to the community shop can't load files from other websites.

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.