Files
TREK/client/src/theme/README.md
T
Maurice 1f66ef1d32 fix(maps): let users and admins enter a CARTO API key (#2060)
CARTO stamps an "API KEY REQUIRED" watermark onto every basemap tile
fetched without a key since 26.08.2026, and both CartoDB presets plus the
built-in fallback are CARTO. The key is free and needs no account, so add
it as a setting rather than dropping the provider.

carto_api_key sits next to mapbox_access_token: per user in Settings/Map,
instance-wide under Admin/User Defaults, encrypted at rest, injected from
CARTO_API_KEY on managed instances. Unlike Mapbox it never switches the
map provider, since a raster key says nothing about the renderer.

The key is appended as ?key= at render time instead of being stored in the
template, so a saved URL stays portable and survives a rotation. That is
also what reaches the four maps that pinned CARTO and ignored the user's
tile setting: atlas, collections, the public trip share and the public
journey. The two share pages read it from their token-gated payload, the
same way baseCurrency already travels.

Also lifts the release notice above page chrome. Its overlay sat at
z-index 50, under the Vacay mode toolbar at 61, so the dialog was neither
on top nor modal. Adds a six-step layer scale to index.css and moves the
three notice shells onto it.

Closes #2054
Closes #2052
2026-08-27 12:06:55 +02:00

4.4 KiB

Appearance & theming

TREK's look is driven by design tokens (CSS custom properties) so a single per-user config can re-skin the whole app — color scheme, accent, transparency, text size, density — without touching component code. New pages get this for free if they follow the contract below.

How it works

  • The per-user config is one validated blob (AppearanceConfig in @trek/shared), stored under the appearance settings key.
  • applyAppearance() (src/theme/applyAppearance.ts) is the only code that writes styling to the DOM: it toggles .dark, sets data-scheme / data-no-transparency / data-density / data-reduce-motion on <html>, and writes the custom-accent + type-scale CSS variables.
  • A render-blocking public/theme-boot.js replays a cached snapshot before first paint to avoid FOUC (external classic script — the prod CSP blocks inline scripts).
  • Schemes are data: swatch metadata in src/theme/schemes.ts, token values in the [data-scheme="…"] blocks in src/index.css.

Token taxonomy

Purpose Token / utility
Surfaces --bg-primary/secondary/tertiary/elevated/card/input/hover/selected · bg-surface*
Text --text-primary/secondary/muted/faint · text-content*
Borders --border-primary/secondary/faint · border-edge*
Accent --accent (fill) · --accent-text (on fill) · --accent-on (on surface) · --accent-hover · --accent-subtle · bg-accent / text-accent-on / bg-accent-subtle
Status --success/-soft, --danger/-soft, --warning/-soft, --info/-soft · bg-danger-soft text-danger etc.
Elevation --shadow-sm/md/lg/card/elevated/modal/dropdown/popover · shadow-modal etc.
Overlay / inverse --overlay · --bg-inverse / --text-inverse · bg-inverse text-inverse-text
Type tiers text-title / text-subtitle / text-body / text-caption (scale with the user's per-tier multipliers)

Layering

Fixed and sticky surfaces read their stacking order from one scale in src/index.css, so two of them cannot end up in the wrong order by accident.

Token Value For
--z-bar 60 BottomNav, page-bound sticky bars
--z-nav 200 the desktop navbar
--z-modal 10000 shared/Modal, ConfirmDialog
--z-overlay 99990 page-owned full-screen overlays
--z-notice 99998 app-wide system and release notices
--z-toast 100000 toasts, tooltips, context menus

A new fixed surface docks onto the step it belongs to (z-[var(--z-modal)] as a class, z-index: var(--z-modal) in CSS) instead of inventing a number. Older files still carry literal values; the steps are far enough apart that those keep their current position until the file gets converted.

The contract (enforced by npm run theme:lint)

  1. Surfaces/text/borders use the semantic utilities (bg-surface*, text-content*, border-edge*) or var(--token) — never raw bg-slate-* / text-gray-* / bg-white.
  2. The primary/"black" action look uses bg-accent + text-accent-text (+ ring-accent) — never bg-slate-900 / bg-black / bg-indigo-*. This is what lets a user's accent reach new surfaces automatically.
  3. Semantic text size uses the tier utilities text-title/subtitle/body/caption — never an inline fontSize: <px>, never raw text-sm/text-xs to mean a tier.
  4. Translucent/blurred surfaces consume an alpha/glass token (--bg-elevated, --tooltip-bg, --glass-*) or a backdrop-blur utility, so transparency-off can neutralize them centrally — never inline rgba() backgrounds or backdrop-filter in JSX.
  5. Components never read AppearanceConfig to compute styles. Only applyAppearance reads the config and writes tokens; components read tokens. Dark state is read off the .dark class, never duplicated into React state.
  6. New appearance dimensions are added to AppearanceConfig + applyAppearance + schemes.ts — never a second applier or a parallel token family. New feature palettes derive their accent from var(--accent).

Allowed to stay inline / literal

Genuinely dynamic values (data-driven colors like cat.color, computed geometry/transforms/sizes), and the surfaces CSS variables can't reach: injected map popup/marker HTML, Mapbox/MapLibre paint, and the standalone @react-pdf documents. Mark intentional exceptions with a theme-lint-disable line comment.