Branding & Theme¶
The branding block in app/settings.ts sets your app's identity — title, logo, the localStorage namespace, and the color palette that themes the entire UI.
branding: {
title: 'My App',
appId: 'my-app',
logo: '/logo.svg',
colors: { primary: 'indigo-700', /* … */ },
}
Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
title |
string |
— (required) | App name, shown in the top bar |
logo |
string |
— | Path to a logo shown in the top bar. Drop a file in app/public/ and reference it (e.g. /logo.svg) |
appId |
string |
slug of title |
Stable id prefixed onto every localStorage key the app writes (bookmarks, drawings, measure prefs, info-dismiss). Set a distinct appId per app when several are served from one origin so their saved state doesn't collide |
colors |
ThemeColors |
all defaults | The color tokens (below). Entirely optional |
The color system¶
Every color in the chrome traces to a named token. Change one token and that role re-skins everywhere it's used. The whole point is that you re-theme the app from one place rather than hunting through CSS.
Two rules make this pleasant:
- Every token is optional. Delete any line — or the whole
colorsblock — and it falls back to the documented default. You're never forced to set one. - Many tokens default to
primary. So changing justprimaryre-skins most of the app. Set the others only when you want contrast.
Token values are Tailwind color names¶
A token's value is a Tailwind color name + shade, like 'indigo-700' or 'rose-600' — pick from Tailwind's curated palette. Internally each resolves to the matching Tailwind CSS variable (var(--color-indigo-700)), so colors stay consistent and the (planned) settings UI can offer a swatch picker instead of a raw hex wheel.
Why not free hex?
Restricting to the Tailwind palette keeps colors harmonious and tooling-friendly. A handful of map paint colors (label text, hover highlight on the map canvas) are the exception — MapLibre style values can't read CSS variables, so those are resolved to a computed color at runtime.
The tokens¶
| Token | Default | Role |
|---|---|---|
primary |
indigo-700 |
Brand color — top bar, active states, focus rings, primary buttons |
text |
slate-900 |
Default body text |
mutedText |
slate-600 |
Muted secondary text + subtle hover backgrounds |
surface |
slate-100 |
"Info box" gray — group/section/popup-bar headers, the measure unit panel |
danger |
rose-600 |
Destructive actions (delete / clear) — fill |
dangerHover |
rose-700 |
Danger button hover |
success |
emerald-600 |
Affirmative actions (e.g. draw "done") — fill |
successHover |
emerald-700 |
Success button hover |
disabledBg |
gray-200 |
Disabled button background |
disabledText |
gray-400 |
Disabled button text |
hover |
amber-500 |
Row-hover tint + left accent + map highlight |
slider |
primary |
Toggle-switch "on" + range-slider accent |
actionActive |
primary |
Tool-button selected fill (draw/measure) |
actionActiveHover |
primary |
…its hover |
actionNotActive |
white |
Tool-button idle fill |
actionNotActiveHover |
slate-50 |
…its hover |
save |
primary |
Save button fill (bookmarks/draw) |
saveHover |
primary |
…its hover |
popupHeaderBg |
surface |
Popup header bar background |
popupArrow |
mutedText |
Popup pager-arrow color |
dropdownHover |
primary |
Native <select> focus/accent (measure unit dropdowns) |
Example¶
colors: {
primary: 'blue-900', // re-skins top bar, active states, save buttons, …
danger: 'rose-600',
success: 'emerald-600',
hover: 'amber-500',
// everything else inherits its default (and many inherit `primary`)
}
Neutral grays in widget chrome (plain slate text, white backgrounds) intentionally use Tailwind slate-* directly and aren't tokenized — the token system covers themeable roles, not every pixel.
Logo & favicon¶
The demo uses an SVG containing the ⛰️ emoji for both the top-bar logo (app/public/logo.svg) and the favicon (app/public/favicon.svg) — modern browsers render the emoji as native color art, so it's crisp at any size. To use your own, drop an image in app/public/ and point branding.logo at it.
Widget header styling¶
A related knob, ui.widgetHeader, controls the title bar shared by widget panels — see UI & Layout.