Skip to content

Custom Theming System

The BloomSpark Maps SDK features a comprehensive, token-based theming engine. Styling variables are declared as TypeScript interfaces, compiled, and injected as CSS Custom Properties (variables) onto the document's :root node, enabling instant application-wide theme flips.


🎨 Theme Tokens Interface

typescript
export interface SpatialTheme {
  /** Mode name */
  mode: 'light' | 'dark';
  
  /** Core application styling hex declarations */
  colors: {
    primary: string;        // default '#2393d4' (BloomSpark Blue)
    primaryReshape: string; // default '#086bb7' (Focused/hovered active blue)
    secondary: string;
    accent: string;
    surface: string;
    background: string;
    text: string;
    border: string;
    alarm: string;          // default '#dc2626'
    warning: string;        // default '#c77a15'
    ok: string;             // default '#3aa655'
    nodata: string;         // default '#6a6c6c'
  };

  /** Vector floor plan layer stylings */
  map: {
    wallFill: string;
    floorFill: string;
    doorLine: string;
    amenityIcon: string;
  };

  /** Floating controls UI panel background cards styles */
  controls: {
    floorSwitcherBg: string;
    toolbarBg: string;
    infoCardBg: string;
  };

  /** Sensor kind standard color fallbacks */
  sensorDefaults: Record<SensorKind, string>;

  /** Heatmap aggregation colors */
  heatmap: {
    palette: string[];
  };

  /** Typography settings */
  font: {
    family: string;
    sizeBase: number; // in pixels
  };

  /** Rounded corners radii */
  radius: {
    sm: number;
    md: number;
    lg: number;
  };

  /** Animation timings */
  motion: {
    duration: number; // in milliseconds
    easing: string;   // CSS transition-timing-function e.g. cubic-bezier
  };
}

export type SensorKind = 'temperature' | 'humidity' | 'co2' | 'voc' | 'pressure' | 'peopleCounter';

⚡ Instant High-Performance Light/Dark Diffing

Swapping themes in mapping libraries often causes a severe "style-flash" because all vector sources and layers are completely destroyed and re-loaded.

BloomSpark avoids this by leveraging MapLibre's internal style diffing algorithms. When the theme swaps from light to dark:

  1. Style Diffing Execution: The SDK calls:
    typescript
    map.setStyle(newThemeStyleUrl, { diff: true });
  2. No Layer Unmounting: MapLibre parses the difference between the two vector sheets and updates only changed paint properties (e.g. background fill-color).
  3. Preserved Assets: Existing IMDF sources, data layers, wayfinding vectors, and current camera zoom positions remain completely intact and unaffected.
  4. UI Syncing: CSS Custom Variables update on :root concurrently, causing the HTML floating panels (like the Floor Switcher or info cards) to glide smoothly into their dark-mode variables using standard CSS transitions.

🛠️ The <ThemeBuilder> Dev Tool

To help designers and developers tune custom color schemes, the SDK includes an interactive ThemeBuilder component:

tsx
import { SpatialProvider, SpatialMap, ThemeBuilder } from '@bloomsparkagency/core';

export function DevSandbox() {
  return (
    <SpatialProvider>
      <SpatialMap buildings={[]}>
        {/* Adds an interactive floating control pane to tune custom colors */}
        <ThemeBuilder />
      </SpatialMap>
    </SpatialProvider>
  );
}

The ThemeBuilder allows you to edit color values, test light/dark transitions live in the browser, and click "Export Tokens" to download a compiled theme.ts file that can be plugged directly back into your production application configurations.

Released under commercial licensing.