Back to all examples

Map theme from colours, an image or CSS

This example on GitHub

Theme the whole map from a few colours (a brand, an artist, a mood), an image's dominant colours or a website's stylesheet

Map theme from colours, an image or CSS
import { TomTomConfig } from '@tomtom-org/maps-sdk/core';
import {
    GeometriesModule,
    PlacesModule,
    RoutingModule,
    StylingFoundationsModule,
    TomTomMap,
} from '@tomtom-org/maps-sdk/map';
import { calculateRoute } from '@tomtom-org/maps-sdk/services';
import { mapColorsLightDark, mapThemeModes } from '@tomtom-org/maps-sdk-plugin-map-theme';
import './style.css';
import { API_KEY } from './config';
import { ileDeLaCite, landmarks, louvre, pantheon } from './overlays';
import { type Derive, initSources } from './sources';
import { showSwatches } from './swatches';
import { initTogglePanel } from './togglePanel';

// (Set your own API key when working in your own environment)
TomTomConfig.instance.put({ apiKey: API_KEY, language: 'en-GB' });

(async () => {
    const map = new TomTomMap({ mapLibre: { container: 'sdk-map', center: [2.3527, 48.8527], zoom: 14 } });
    const styling = await StylingFoundationsModule.get(map);

    // Neither the area, the pins nor the route pick a colour of their own, so all three draw in the
    // theme's `accent` and follow it on every change.
    const geometries = await GeometriesModule.create(map);
    await geometries.show(ileDeLaCite);
    const places = await PlacesModule.create(map);
    await places.show(landmarks);
    const routing = await RoutingModule.create(map);
    await routing.showRoutes(await calculateRoute({ locations: [louvre, pantheon] }));

    const modeSelector = document.querySelector('#ui-mode') as HTMLSelectElement;
    const swatches = document.querySelector('#ui-swatches') as HTMLElement;

    // What the map was last themed from, so switching light/dark re-themes the same source.
    let current: Derive | undefined;

    // Deriving throws on colours it cannot parse and on files it cannot read; show that instead.
    const themeMap = async (derive: Derive) => {
        current = derive;
        const mode = mapThemeModes.find((candidate) => candidate === modeSelector.value) ?? 'auto';
        try {
            const colors = await derive(mode);
            // A dark theme recolours a dark style, whose own shades then stay in range.
            const lightDark = mapColorsLightDark(colors);
            if (lightDark !== map.styleLightDarkTheme) {
                await map.setStyle(lightDark === 'dark' ? 'streetDark' : 'streetLight');
            }
            styling.setMapColors(colors);
            showSwatches(swatches, colors);
        } catch (error) {
            swatches.textContent = error instanceof Error ? error.message : String(error);
        }
    };

    modeSelector.addEventListener('change', () => current && themeMap(current));
    initSources(themeMap);
    initTogglePanel();
})();

Pick a palette, type colours or drop an image or a CSS file, and the whole map takes that theme, pins and route included.

  • The Map Theme plugin’s deriveMapColors gives any handful of colours a map colour role each and derives the rest with readable labels.
  • deriveMapColorsFromImage and deriveMapColorsFromCss pick those colours, taking an image’s dominant ones or a stylesheet’s most used.
  • setMapColors applies them as knobs that survive a style switch, and sets the accent the area, pins and route draw in. The route’s summary bubble takes the theme’s label halo and label colours.
  • mapColorsLightDark tells a dark theme from a light one, so a dark theme recolours streetDark.

Related examples