An admin can offer themes to every user of the server, in Control panel > Branding > Server themes. The field takes a YAML list of themes. Every user finds them at the top of the theme pickers in Settings > Appearance, under Server themes, and the one marked default: true is the theme a new device starts with in that mode.
The format
Each entry is one theme:
id: letters, digits, dots, dashes or underscores, unique in the list, at most 64 characters. It is how a user's choice is remembered, so keep it when you edit a theme.name: what the pickers show.base:darkorlight. Every prop the theme does not set is taken from the built-in base of that name.props: the theme props, the same as in a theme made with the theme editor. Quote every color: an unquoted#2e3440starts a YAML comment and reads as nothing.default: true, optional, on at most one theme of each base.authoranddesc, optional.
The list holds at most 32 themes of 8192 characters each once read, and the source at most 65536 characters. YAML tags beyond plain data (!!binary, !!timestamp and the like) are refused. When Save refuses the list, the message names the line of a YAML error, or the position and id of the entry that is not a theme.
Give a theme an id of its own: one that a bundled theme already uses takes its place wherever the app picks themes by id, so the built-in one can no longer be chosen. The list travels in the server's metadata, which every client fetches — up to about 256 KB at the caps above — so keep it to the themes you actually offer.
Reusing a theme
YAML anchors (&name) and merge keys (<<: *name) let one theme reuse another and write only what differs. Below, Nord is the server's default dark theme, and an OLED variant takes the whole Nord entry, then its props, and overrides the surfaces only.
- &nord
id: nord
name: Nord
base: dark
default: true
props: &nord-props
bg: '#2e3440'
panel: '#3b4252'
popup: '#434c5e'
navBg: '#2e3440'
fg: '#d8dee9'
fgHighlighted: '#eceff4'
fgOnAccent: '#2e3440'
accent: '#88c0d0'
link: '#81a1c1'
hashtag: '#8fbcbb'
mention: '#88c0d0'
renote: '#a3be8c'
love: '#bf616a'
success: '#a3be8c'
warn: '#ebcb8b'
error: '#bf616a'
divider: 'rgba(216, 222, 233, 0.1)'
shadow: 'rgba(0, 0, 0, 0.3)'
modalBg: 'rgba(46, 52, 64, 0.6)'
- <<: *nord
id: nord-oled
name: Nord OLED
default: false
props:
<<: *nord-props
bg: '#000000'
panel: '#0b0d11'
popup: '#14171d'
navBg: '#000000'
shadow: 'rgba(0, 0, 0, 0.6)'
modalBg: 'rgba(0, 0, 0, 0.75)' Merging a whole entry takes its default: true along, hence the default: false on the variant. Without it, Save says that two themes claim to be the default dark theme.
With the default theme fields
The two older fields above the list, the server-wide default light theme and default dark theme, keep working. A theme the list marks as default replaces the field of its base; a base the list marks no default for keeps its field. A server that never fills the list sees no change.
What users see
In Settings > Appearance, a user whose account is on this server keeps a server theme they pick across reloads. If you edit the theme, their copy follows the edit the next time they load the app; if you remove it from the list, they fall back to the server's default theme of that base. A copy of a server theme taken before this release carries no mark saying where it came from, so an edit or a removal does not reach it: its owner takes the edited theme by picking another theme and then this one again, and a theme you remove keeps applying on that device until they pick another. The theme pickers of a blog profile offer the same themes, though a blog keeps the theme its owner picked, as it does a built-in one. The app reads the server's settings when it loads and keeps the copy it has for up to an hour, so a change reaches a device on its first load after that.