Theme¶
Every color in the app comes from your project's theme.json. Pick a built-in preset, then override individual color tokens if you want. The app reads the file as settings.theme, the same way it reads layers.json and basemaps.json.
{
"$schema": "../../schema/theme.schema.json",
"preset": "ocean",
"colors": { "primary": "blue-900" }
}
Type " inside colors in VS Code to get the list of every token, with its role and default.
Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
preset |
"conness" \| "light" \| "dark" \| "atomic" \| "ocean" \| "wacky" |
"conness" |
The built-in palette (below) |
colors |
object | none | Per-token overrides on top of the preset |
widgetTitleColor |
"text" \| "primary" |
"text" |
Color of widget panel titles in the top-bar panel, the side panel and floating pop-outs. A placement's own header.titleColor wins |
mapControls |
"light" \| "dark" |
follows the preset | The map's own controls and the floating widget buttons: light (white buttons, dark icons) or dark (dark buttons, light icons). See Map controls |
Colors resolve the same way as every other setting in the kit:
kit default ←
preset←colors
An unknown preset name is reported when the project's files are read (theme.json → preset), with the valid options; see Concepts.
Presets¶
| Preset | Mode | Look |
|---|---|---|
conness |
light | Navy brand color and top bar, slate text on white panels. The kit default |
light |
light | White panels, a white top bar, sky-blue accents |
dark |
dark | Slate-900 panels and top bar, blue accents |
atomic |
dark | Charcoal panels, deep orange brand color and top bar, red accents |
ocean |
light | Sea-glass cyan panels, teal brand color, deep-cyan top bar, sky-blue accents |
wacky |
light | Lavender panels, fuchsia brand color, hot-pink top bar, lime, cyan and orange accents |
light and dark color the top bar like the panels. The other presets give it a bold color of its own.
A dark preset changes more than colors. The browser's own controls (scrollbars, dropdown menus) render dark, and the map's controls turn dark too, unless mapControls says otherwise.
Color tokens¶
A token's value is one of:
- a Tailwind color name and shade, like
"indigo-700"or"rose-600", from Tailwind's palette; "white","black"or"transparent";- another token's name (
"primary","panelBg","text") to follow that token:"save": "primary"keeps the Save button on your brand color, whatever it is.
Names are case-sensitive. The settings editor only offers these, and refuses anything else.
Every token is optional. Leave out a token, or the whole colors block, and it falls back to the preset. Many tokens follow primary, so changing primary alone recolors most of the app.
Why Tailwind names instead of hex?
The Tailwind palette keeps colors consistent with each other, and lets the settings editor offer a swatch picker instead of a color wheel. A hex, rgb(), hsl() or oklch() value in colors stops the app at startup with an error naming the token, so a wrong value can't quietly blank a color. Inside your own widgets you can use any color you like.
Defaults below are from the conness preset, in the order the editor lists them.
Text and top bar
| Token | Default | Role |
|---|---|---|
primary |
blue-900 |
Brand color: active states, focus rings, primary buttons, and the top bar through topBarBg |
text |
slate-600 |
Body text, inputs, dropdowns, readouts and idle icons: most of the text in panels, popups and the info page |
titleText |
white |
The app title in the top bar. Must contrast with topBarBg |
strongText |
slate-900 |
Names, titles and emphasized text: layer, legend and basemap names, bookmark and drawing names, dialog titles, popup titles and values. Icon buttons turn this color on hover |
topBarBg |
primary |
Top bar background |
topBarWidget |
white |
Top-bar widget icons, the info button and the mobile carousel. Must contrast with topBarBg |
Panels
| Token | Default | Role |
|---|---|---|
panelBg |
white |
Background of the side panel, widget panels, popups, dialogs, dropdowns and inputs |
panelBorder |
slate-200 |
Panel borders and dividers |
panelWidget |
text |
Side rail widget icons and the side panel's collapse chevron |
surface |
slate-100 |
Info sections, the popup header and footer, section headers, chips, Measure's unit boxes |
layerGroupBg |
surface |
Layer list group header rows |
Actions
| Token | Default | Role |
|---|---|---|
danger / dangerHover |
rose-600 / rose-700 |
Delete and clear buttons |
success / successHover |
emerald-600 / emerald-700 |
Confirm buttons, like draw's "done" |
disabledBg / disabledText |
gray-200 / gray-400 |
Disabled buttons |
actionActive / actionActiveHover |
primary / primaryDarker |
Selected tool button (draw, measure) |
actionNotActive / actionNotActiveHover |
white / slate-50 |
Idle tool button |
save / saveHover |
primary / primaryDarker |
Save buttons (bookmarks, draw) |
Accents
| Token | Default | Role |
|---|---|---|
hover |
amber-500 |
List row hover: a light tint of this color |
slider |
primary |
Toggle switches when on, and range sliders |
popupHeaderBg |
surface |
Popup title bar |
popupArrow |
text |
Popup pager arrows |
dropdownHover |
primary |
Dropdown focus (measure's unit selectors) |
primaryDarker is a special value: primary made about 15% darker, recomputed from whatever primary is. Hover states use it.
Buttons with a solid colored fill (primary, danger, success, save, actionActive) and colored top bars use white text, so keep those tokens dark enough for white text to read.
Map controls¶
MapLibre's controls (zoom, compass, geolocate, globe, terrain, fullscreen), the zoom readout, the scale bar and the floating widget buttons over the map share one look, set by mapControls: "light" (white buttons, dark icons) or "dark" (dark buttons, light icons). Unset, it follows the preset, so dark presets get dark controls. Their pop-outs, like Search's results, keep the panel colors.
Two tokens fine-tune it:
| Token | Default | Role |
|---|---|---|
mapControlBg |
set by mapControls (white, or dark slate) |
Background of the map's controls and the floating widget buttons |
mapControlIcon |
set by mapControls (near-black, or near-white) |
Floating widget icons, the zoom readout and the scale bar text |
MapLibre's own control icons are images, so they stay black or white by mapControls whatever mapControlIcon is.
Examples¶
Keep a preset and change only the brand color:
{
"$schema": "../../schema/theme.schema.json",
"preset": "conness",
"colors": { "primary": "emerald-700" }
}
A light top bar on the default palette:
What the theme doesn't control¶
- Map geometry colors. The shapes the Draw and Measure widgets paint on the map, and the amber highlight on a hovered drawing, use fixed colors. They need many distinct colors and must read on any basemap. The widgets' panels do follow the theme.
- Layer styling. Set per layer through Symbology, with label defaults in
ui.layerDefaults. - The map background. That's
map.backgroundColor; see Map & Camera.
Widget header styling¶
widgetTitleColor sets the title color. Whether titles and their divider show at all is set in ui.widgetHeader; see UI & Layout.