Skip to content

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:

"colors": { "topBarBg": "white", "titleText": "slate-900", "topBarWidget": "slate-600" }

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.