Interface themes¶
A theme is a JSON file that sets every colour the interface draws, plus its spacing and text
sizes. Light and Dark ship with RustGIS; users add their own to the themes folder. The map is
drawn by the renderer, which takes its light or dark defaults from the theme's dark flag.
Where themes live¶
| Theme | File |
|---|---|
| Built in | crates/ui-egui/themes/*.json, compiled into the app |
| User | <config>/themes/*.json, read at startup, when a file changes, and by Reload Themes |
The config folder is %APPDATA%\RustGIS on Windows, ~/Library/Application Support/RustGIS on
macOS and $XDG_CONFIG_HOME/rustgis, which is ~/.config/rustgis when the variable is unset,
elsewhere. The default Linux themes folder is ~/.config/rustgis/themes.
RUSTGIS_CONFIG_DIR overrides the config folder.
A user file whose name matches a built-in replaces it. A reworked Light sits in the folder as
light.json, and the menu lists one Light. Two files that claim the same name: the first in name
order is used, and the second is listed in red.
Picking one¶
Settings › Theme lists Follow the System and every theme that loaded. Light and Dark are the brightness choices, so there is no separate Dark Mode switch. A choice is applied at once and saved for the next start. Interface scale, 80% to 200%, sits beside it, and it is the only control for it: Ctrl + and Ctrl - do nothing, so the scale the app records is the scale it draws.
Follow the System follows the platform theme where the platform reports one: Windows, macOS and the
browser, through prefers-color-scheme. Linux desktops report no theme, so the entry is greyed out
there and the button names the brightness in force instead.
What the browser build does¶
The browser has no config folder, so there is no themes folder there and Load a Theme from File… is not offered. The browser offers Follow the System, Light, Dark and the interface scale: a theme in a file is a desktop route. The two built-in themes are the same files, compiled in, so nothing else differs.
On the desktop, to start from a theme you already have, use Load a Theme from File…. The app
keeps the source text, including inherited settings and exact colour strings, in the themes folder
(adding the resolved name if the file omitted it); edit that copy and the app follows it. Loading a file whose name
matches a theme already in the folder replaces that copy, and the version it replaces is kept beside
it as <file>.json.bak: one backup, replaced on each save. An interrupted save, or a save whose final syncing or cleanup fails, may leave
<file>.json.recover, containing the theme from before the save. Copy it somewhere safe,
compare it with the theme and backup, then remove the recovery file before saving again. A file that does not load is listed in
red at the bottom of the same menu with its reason. Unknown tokens and bad colour strings name
the token; values of the wrong JSON type give a line and column. A notice says when a file was refused.
While the themes folder is readable, the app checks file names, sizes and modification times once a second. Without that folder, the app does not wake just to check for themes. If you create the folder while the app is idle, interact with the app or use Reload Themes to discover it. A save that changes that metadata is picked up automatically. If a save preserves both size and modification time, use Reload Themes to reread it. A theme in use that stops loading leaves the window on the nearest built-in until it loads again: the button names the theme drawn, the menu gives the reason, and the choice returns with the file.
A short theme¶
extends names a theme that ships, and the file then sets only what it changes:
{
"schema": 1,
"name": "Cobalt",
"extends": "Dark",
"colours": {
"accent": "#3B82F6",
"accent_text": "#93C5FD",
"on_accent": "#FFFFFF",
"hover": "#22314D",
"checked": "#1E3A8A"
}
}
Colours, sizes and brightness the file leaves out come from the theme it extends. A short file is the better start.
The file¶
Without extends every colour is required, and a size the file leaves out keeps the standard one.
An unknown name, colour or size is refused by name. The built-in Light is the reference:
{
"schema": 1,
"name": "Light",
"dark": false,
"colours": {
"header": "#EEF1F5",
"ribbon": "#FFFFFF",
"tab_strip": "#EEF1F5",
"canvas": "#E4E8EE",
"pane": "#FBFCFD",
"pane_header": "#F3F5F8",
"border": "#DDE2E9",
"border_strong": "#C3CAD4",
"text": "#1F2630",
"text_dim": "#5E6977",
"text_disabled": "#A9B1BC",
"icon": "#3A4554",
"hover": "#E9F0FB",
"pressed": "#D4E2F8",
"checked": "#DCE8FA",
"accent": "#2563EB",
"accent_text": "#1D4EC4",
"on_accent": "#FFFFFF",
"input": "#FFFFFF",
"input_border": "#C6CDD7",
"group_label": "#707A88",
"status_bar": "#F3F5F8",
"selection": "#CFE0FC",
"menu": "#FFFFFF",
"shadow": "#0000001C",
"green": "#169A5A",
"red": "#D13C3C",
"amber": "#E59E1A",
"context": "#0E9384",
"context_tint": "#E3F5F2",
"control": "#DDE3EA"
},
"metrics": {
"item_spacing": [6.0, 5.0],
"button_padding": [8.0, 3.0],
"row_height": 22.0,
"corner_radius": 5,
"body": 12.5,
"button_text": 12.5,
"small": 10.5,
"heading": 17.0,
"mono": 12.0
}
}
An unknown token stops the file from loading, and the error names it. Colours are #RRGGBB, or
#RRGGBBAA where alpha matters, as in shadow. schema is 1; a file written for a later build is
refused by number.
dark is the brightness the theme reports to egui, which sets its default widget shapes and the
map's light or dark defaults. It is not inferred from the colours.
metrics are in points: item spacing, button padding, row height, corner radius and text sizes.
Corner radius is held between 0 and 32 and rounded to the nearest whole point; an adjustment is counted.
Sizes are held to what a window can draw, and a notice says when a value was held. A file over 64 KB
is not read.
Tokens¶
Surfaces: header, ribbon, tab_strip, canvas, pane, pane_header, menu, status_bar,
input.
Lines and text: border, border_strong, input_border, text, text_dim, text_disabled,
icon, group_label, on_accent.
Interaction: hover, pressed, checked, selection, control (slider rails and checkbox
wells, which must stay visible on white).
Meaning: accent, accent_text, context, context_tint, green, red, amber, shadow.
Commands¶
| Command | Effect |
|---|---|
ui.themeFile with name |
Uses that theme; no name follows the system again |
ui.themeLoad |
Opens a theme file from anywhere, keeps it in the themes folder and uses it |
ui.theme with dark |
Uses Light or Dark, the brightness asked for |
ui.scale with factor |
Interface scale, held between 80% and 200% |
ui.themeReload |
Reads the themes folder again |
What a theme cannot change¶
The map is drawn by the renderer, so a theme's colours stop at the map view; canvas is the
surface behind it. Icon shapes stay as drawn, taking their ink and accent from the theme. Plugin
panels use the host's widgets and follow it.