@unmap/themes 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,7 +20,7 @@ import { encodeTheme, decodeTheme, validateTheme, ThemeError } from '@unmap/them
20
20
  const code = await encodeTheme({
21
21
  v: 1,
22
22
  name: 'copper',
23
- base: 'outdoor', // base | muted | outdoor | blueprint | blush | orchid | canopy | lagoon | tropic | sunset | bold | pastel
23
+ base: 'outdoor', // base | muted | outdoor | blueprint | blush | orchid | canopy | lagoon | tropic | sunset | bold | pastel | atlas | swiss
24
24
  light: { basemap: { water: '#9CCFD2' }, route: { color: '#B85C00', width: 4 } },
25
25
  dark: { basemap: { water: '#12303A' }, route: { color: '#FFA24D', width: 4 } },
26
26
  })
@@ -31,7 +31,7 @@ const doc = await decodeTheme(code) // validated ThemeDoc, or th
31
31
 
32
32
  ## What is in a theme
33
33
 
34
- - `base` picks the style: `base`, `muted`, `outdoor`, `blueprint`, `blush`, `orchid`, `canopy`, `lagoon`, `tropic`, `sunset`, `bold` or `pastel`. Each of `light` and `dark` carries
34
+ - `base` picks the style: `base`, `muted`, `outdoor`, `blueprint`, `blush`, `orchid`, `canopy`, `lagoon`, `tropic`, `sunset`, `bold`, `pastel`, `atlas` or `swiss`. Each of `light` and `dark` carries
35
35
  sparse overrides on that style, so a theme with no overrides renders exactly like its
36
36
  `flavor=` twin.
37
37
  - `basemap` overrides are colours on the style's recipe (land, water, vegetation, roads, labels,
@@ -57,5 +57,5 @@ const doc = await decodeTheme(code) // validated ThemeDoc, or th
57
57
  themes are both built from.
58
58
 
59
59
  Runs in browsers, Cloudflare Workers, and Node 18 or later (it uses the web `CompressionStream`).
60
- Docs: [unmap.dev/docs/tiles-and-styles](https://unmap.dev/docs/tiles-and-styles) covers the
60
+ Docs: [unmap.dev/docs/apis/maps](https://unmap.dev/docs/apis/maps) covers the
61
61
  `?theme=` parameter and the `invalid_theme` error.
package/dist/index.d.ts CHANGED
@@ -363,6 +363,89 @@ declare const amber: {
363
363
  readonly 600: "#C19100";
364
364
  readonly 700: "#916D00";
365
365
  };
366
+ /** Bay Atlas reference colours, re-authored as light/dark ramps for Canada's basemap. */
367
+ declare const atlasPaper: {
368
+ readonly 25: "#F6F3EA";
369
+ readonly 100: "#EDE7DC";
370
+ readonly 200: "#DDD4C4";
371
+ readonly 500: "#A08A70";
372
+ readonly 700: "#786B5E";
373
+ readonly 800: "#57504A";
374
+ readonly 900: "#2A2521";
375
+ readonly 950: "#211C17";
376
+ readonly 1000: "#15110D";
377
+ };
378
+ declare const atlasWater: {
379
+ readonly 200: "#B3D2E6";
380
+ readonly 400: "#86B3D4";
381
+ readonly 700: "#3E6D96";
382
+ readonly 800: "#274F73";
383
+ readonly 950: "#142B3D";
384
+ readonly 980: "#0B1C29";
385
+ };
386
+ declare const atlasGreen: {
387
+ readonly 100: "#E9EFD4";
388
+ readonly 200: "#DCE8CB";
389
+ readonly 300: "#CFE4B8";
390
+ readonly 700: "#3B6B2B";
391
+ readonly 800: "#2A4822";
392
+ readonly 900: "#1B3018";
393
+ };
394
+ declare const atlasRoad: {
395
+ readonly 300: "#F7D686";
396
+ readonly 400: "#E67F50";
397
+ readonly 500: "#C8412A";
398
+ readonly 600: "#A8231C";
399
+ readonly 800: "#5E1712";
400
+ };
401
+ /** Swiss national-map conventions: white ground, blue water, leaf greens, yellow roads. */
402
+ declare const swissPaper: {
403
+ readonly 25: "#FDFDFE";
404
+ readonly 100: "#F2F2F2";
405
+ readonly 200: "#E6E4E2";
406
+ readonly 500: "#96938E";
407
+ readonly 700: "#686660";
408
+ readonly 800: "#3C3C3C";
409
+ readonly 900: "#252525";
410
+ readonly 950: "#191919";
411
+ readonly 1000: "#101010";
412
+ };
413
+ declare const swissWater: {
414
+ readonly 200: "#D2EEFF";
415
+ readonly 400: "#4DA4DA";
416
+ readonly 700: "#2D78A4";
417
+ readonly 800: "#205674";
418
+ readonly 950: "#152F40";
419
+ readonly 980: "#0A1B28";
420
+ };
421
+ declare const swissGreen: {
422
+ readonly 100: "#D3EBC7";
423
+ readonly 200: "#C3DDB4";
424
+ readonly 300: "#8AC66C";
425
+ readonly 700: "#48792C";
426
+ readonly 800: "#2C4E22";
427
+ readonly 900: "#1B3317";
428
+ };
429
+ declare const swissRoad: {
430
+ readonly 300: "#FAF39E";
431
+ readonly 400: "#F8C444";
432
+ readonly 500: "#FAB69E";
433
+ readonly 600: "#CB4D4D";
434
+ readonly 800: "#46371E";
435
+ };
436
+ /** Relief inks from the references; neutral shadows avoid colouring every ridge blue. */
437
+ declare const atlasRelief: {
438
+ readonly shadow: "#5B4A3B";
439
+ readonly highlight: "#FFFDF5";
440
+ readonly accent: "#7C6A58";
441
+ readonly contour: "#A8875F";
442
+ };
443
+ declare const swissRelief: {
444
+ readonly shadow: "#4E6276";
445
+ readonly highlight: "#FFF8CD";
446
+ readonly accent: "#606C7A";
447
+ readonly contour: "#B46E0D";
448
+ };
366
449
 
367
450
  /**
368
451
  * The unmap cartography authoring surface, moved here from apps/gateway/src/flavors.ts so the
@@ -382,7 +465,7 @@ declare const amber: {
382
465
  * still decode those slots via `normalizeThemeBase` / the slot table in `preset.ts`. `decimal`
383
466
  * (2026-09-09) maps onto `blueprint`: the two single-hue styles read as the same map.
384
467
  */
385
- declare const THEME_BASES: readonly ["base", "muted", "outdoor", "blueprint", "blush", "orchid", "canopy", "lagoon", "tropic", "sunset", "bold", "pastel"];
468
+ declare const THEME_BASES: readonly ["base", "muted", "outdoor", "blueprint", "blush", "orchid", "canopy", "lagoon", "tropic", "sunset", "bold", "pastel", "atlas", "swiss"];
386
469
  type ThemeBase = (typeof THEME_BASES)[number];
387
470
  /** Map a theme `base` — living or retired — onto a shipped style. `null` if unknown. */
388
471
  declare function normalizeThemeBase(base: string): ThemeBase | null;
@@ -472,6 +555,14 @@ declare function makeFlavor(r: Recipe): Flavor;
472
555
  * hand-tuned originals (they are `buildRecipes(STOCK_RAMPS)`).
473
556
  */
474
557
  interface RampSet {
558
+ atlasPaper: typeof atlasPaper;
559
+ atlasWater: typeof atlasWater;
560
+ atlasGreen: typeof atlasGreen;
561
+ atlasRoad: typeof atlasRoad;
562
+ swissPaper: typeof swissPaper;
563
+ swissWater: typeof swissWater;
564
+ swissGreen: typeof swissGreen;
565
+ swissRoad: typeof swissRoad;
475
566
  ink: typeof ink;
476
567
  paper: typeof paper;
477
568
  wave: typeof wave;
@@ -865,4 +956,4 @@ interface PaletteColor {
865
956
  */
866
957
  declare function seedsFromPalette(colors: PaletteColor[]): Required<ThemeSeeds>;
867
958
 
868
- export { BUILDINGS_3D_LAYER_ID, type BasemapOverrides, type ContrastWarning, DEFAULT_LAYER_VISIBILITY, DEFAULT_MARKER, DEFAULT_ROUTE, DEFAULT_SEEDS, type DeepPartial, LABEL_PAIRS, LAYER_GROUPS, type LabelRamp, type LayerGroup, MAX_CODE_LENGTH, MODES, type MarkerTheme, type Oklch, PRESET_CODE_PREFIX, PRESET_CODE_RE, type PaletteColor, RECIPES, type RampSet, type Recipe, type ResolvedTheme, type RoadRamp, type RouteTheme, STOCK_RAMPS, STYLE_THEMES, THEME_BASES, THEME_CODE_PREFIX, THEME_PRESETS, type ThemeBase, type ThemeDoc, ThemeError, type ThemeHalf, type ThemeLayers, type ThemeMode, type ThemePreset, type ThemeSeeds, amber, applyThemeLayers, aqua, azure, buildRecipes, cobalt, contrast, contrastWarnings, coral, decodePreset, decodeTheme, effectiveLayerOrder, ember, encodePreset, encodeTheme, fern, generateTheme, graphite, hexToOklch, indigo, ink, isLayerVisible, isStockLayerOrder, isStockLayers, isThemePresetCode, jade, layerGroupOf, lilac, linen, makeFlavor, mergeRecipe, mist, normalizeThemeBase, oklchToHex, paper, peach, presetId, registryThemeKey, resolveTheme, rose, sandstone, seedsFromPalette, shuffleSeeds, sky, validateTheme, verdant, wave };
959
+ export { BUILDINGS_3D_LAYER_ID, type BasemapOverrides, type ContrastWarning, DEFAULT_LAYER_VISIBILITY, DEFAULT_MARKER, DEFAULT_ROUTE, DEFAULT_SEEDS, type DeepPartial, LABEL_PAIRS, LAYER_GROUPS, type LabelRamp, type LayerGroup, MAX_CODE_LENGTH, MODES, type MarkerTheme, type Oklch, PRESET_CODE_PREFIX, PRESET_CODE_RE, type PaletteColor, RECIPES, type RampSet, type Recipe, type ResolvedTheme, type RoadRamp, type RouteTheme, STOCK_RAMPS, STYLE_THEMES, THEME_BASES, THEME_CODE_PREFIX, THEME_PRESETS, type ThemeBase, type ThemeDoc, ThemeError, type ThemeHalf, type ThemeLayers, type ThemeMode, type ThemePreset, type ThemeSeeds, amber, applyThemeLayers, aqua, atlasGreen, atlasPaper, atlasRelief, atlasRoad, atlasWater, azure, buildRecipes, cobalt, contrast, contrastWarnings, coral, decodePreset, decodeTheme, effectiveLayerOrder, ember, encodePreset, encodeTheme, fern, generateTheme, graphite, hexToOklch, indigo, ink, isLayerVisible, isStockLayerOrder, isStockLayers, isThemePresetCode, jade, layerGroupOf, lilac, linen, makeFlavor, mergeRecipe, mist, normalizeThemeBase, oklchToHex, paper, peach, presetId, registryThemeKey, resolveTheme, rose, sandstone, seedsFromPalette, shuffleSeeds, sky, swissGreen, swissPaper, swissRelief, swissRoad, swissWater, validateTheme, verdant, wave };
package/dist/index.js CHANGED
@@ -278,6 +278,76 @@ var amber = {
278
278
  600: "#C19100",
279
279
  700: "#916D00"
280
280
  };
281
+ var atlasPaper = {
282
+ 25: "#F6F3EA",
283
+ 100: "#EDE7DC",
284
+ 200: "#DDD4C4",
285
+ 500: "#A08A70",
286
+ 700: "#786B5E",
287
+ 800: "#57504A",
288
+ 900: "#2A2521",
289
+ 950: "#211C17",
290
+ 1e3: "#15110D"
291
+ };
292
+ var atlasWater = {
293
+ 200: "#B3D2E6",
294
+ 400: "#86B3D4",
295
+ 700: "#3E6D96",
296
+ 800: "#274F73",
297
+ 950: "#142B3D",
298
+ 980: "#0B1C29"
299
+ };
300
+ var atlasGreen = {
301
+ 100: "#E9EFD4",
302
+ 200: "#DCE8CB",
303
+ 300: "#CFE4B8",
304
+ 700: "#3B6B2B",
305
+ 800: "#2A4822",
306
+ 900: "#1B3018"
307
+ };
308
+ var atlasRoad = {
309
+ 300: "#F7D686",
310
+ 400: "#E67F50",
311
+ 500: "#C8412A",
312
+ 600: "#A8231C",
313
+ 800: "#5E1712"
314
+ };
315
+ var swissPaper = {
316
+ 25: "#FDFDFE",
317
+ 100: "#F2F2F2",
318
+ 200: "#E6E4E2",
319
+ 500: "#96938E",
320
+ 700: "#686660",
321
+ 800: "#3C3C3C",
322
+ 900: "#252525",
323
+ 950: "#191919",
324
+ 1e3: "#101010"
325
+ };
326
+ var swissWater = {
327
+ 200: "#D2EEFF",
328
+ 400: "#4DA4DA",
329
+ 700: "#2D78A4",
330
+ 800: "#205674",
331
+ 950: "#152F40",
332
+ 980: "#0A1B28"
333
+ };
334
+ var swissGreen = {
335
+ 100: "#D3EBC7",
336
+ 200: "#C3DDB4",
337
+ 300: "#8AC66C",
338
+ 700: "#48792C",
339
+ 800: "#2C4E22",
340
+ 900: "#1B3317"
341
+ };
342
+ var swissRoad = {
343
+ 300: "#FAF39E",
344
+ 400: "#F8C444",
345
+ 500: "#FAB69E",
346
+ 600: "#CB4D4D",
347
+ 800: "#46371E"
348
+ };
349
+ var atlasRelief = { shadow: "#5B4A3B", highlight: "#FFFDF5", accent: "#7C6A58", contour: "#A8875F" };
350
+ var swissRelief = { shadow: "#4E6276", highlight: "#FFF8CD", accent: "#606C7A", contour: "#B46E0D" };
281
351
 
282
352
  // src/recipe.ts
283
353
  var THEME_BASES = [
@@ -292,7 +362,9 @@ var THEME_BASES = [
292
362
  "tropic",
293
363
  "sunset",
294
364
  "bold",
295
- "pastel"
365
+ "pastel",
366
+ "atlas",
367
+ "swiss"
296
368
  ];
297
369
  var RETIRED_BASES = {
298
370
  relief: "outdoor",
@@ -391,6 +463,14 @@ function makeFlavor(r) {
391
463
 
392
464
  // src/recipes.ts
393
465
  var STOCK_RAMPS = {
466
+ atlasPaper,
467
+ atlasWater,
468
+ atlasGreen,
469
+ atlasRoad,
470
+ swissPaper,
471
+ swissWater,
472
+ swissGreen,
473
+ swissRoad,
394
474
  ink,
395
475
  paper,
396
476
  wave,
@@ -2216,6 +2296,76 @@ function buildRecipes(r) {
2216
2296
  forest: r.fern[800]
2217
2297
  }
2218
2298
  };
2299
+ const traditional = (swiss, mode) => {
2300
+ const light = mode === "light";
2301
+ const land = swiss ? r.swissPaper : r.atlasPaper;
2302
+ const water = swiss ? r.swissWater : r.atlasWater;
2303
+ const green = swiss ? r.swissGreen : r.atlasGreen;
2304
+ const stock = light ? BASE_LIGHT : BASE_DARK;
2305
+ const halo = light ? land[25] : land[1e3];
2306
+ const highway = swiss ? r.swissRoad[400] : light ? r.atlasRoad[600] : r.atlasRoad[400];
2307
+ const casing = light ? swiss ? r.swissRoad[800] : r.atlasRoad[800] : land[1e3];
2308
+ return {
2309
+ ...stock,
2310
+ background: light ? land[25] : land[1e3],
2311
+ earth: light ? land[25] : land[900],
2312
+ water: light ? water[200] : water[980],
2313
+ parkA: light ? green[100] : green[800],
2314
+ parkB: light ? green[200] : green[700],
2315
+ woodA: light ? green[200] : green[800],
2316
+ woodB: light ? green[300] : green[700],
2317
+ scrubA: light ? green[100] : green[900],
2318
+ scrubB: light ? green[200] : green[800],
2319
+ institutional: light ? land[100] : land[950],
2320
+ pedestrian: light ? land[25] : land[950],
2321
+ buildings: light ? land[200] : land[700],
2322
+ railway: swiss ? r.swissRoad[600] : land[700],
2323
+ boundaries: light ? r.lilac[600] : r.lilac[400],
2324
+ roads: {
2325
+ other: light ? land[500] : land[700],
2326
+ otherCasing: light ? land[700] : land[1e3],
2327
+ minor: light ? "#FFFFFF" : land[500],
2328
+ minorCasing: light ? land[700] : land[1e3],
2329
+ service: light ? land[25] : land[700],
2330
+ serviceCasing: light ? land[500] : land[1e3],
2331
+ major: swiss ? r.swissRoad[500] : light ? r.atlasRoad[500] : r.atlasRoad[300],
2332
+ majorCasing: casing,
2333
+ highway,
2334
+ highwayCasing: casing,
2335
+ link: highway,
2336
+ linkCasing: casing
2337
+ },
2338
+ tunnelFill: light ? land[100] : land[950],
2339
+ tunnelCasing: light ? land[500] : land[1e3],
2340
+ bridgeCasing: casing,
2341
+ labels: {
2342
+ city: light ? land[900] : land[25],
2343
+ cityHalo: halo,
2344
+ state: light ? land[700] : land[500],
2345
+ stateHalo: halo,
2346
+ country: light ? land[700] : land[500],
2347
+ subplace: light ? land[700] : land[500],
2348
+ subplaceHalo: halo,
2349
+ address: light ? land[700] : land[500],
2350
+ addressHalo: halo,
2351
+ roadMinor: light ? land[800] : land[500],
2352
+ roadMinorHalo: halo,
2353
+ roadMajor: light ? land[900] : land[200],
2354
+ roadMajorHalo: halo,
2355
+ ocean: light ? water[700] : water[400]
2356
+ },
2357
+ pois: { ...stock.pois, green: light ? green[700] : green[300], blue: light ? water[700] : water[400] },
2358
+ landcover: {
2359
+ grassland: light ? green[100] : green[900],
2360
+ barren: light ? land[200] : land[700],
2361
+ urban_area: light ? land[100] : land[950],
2362
+ farmland: light ? land[25] : land[950],
2363
+ glacier: light ? water[200] : water[950],
2364
+ scrub: light ? green[100] : green[900],
2365
+ forest: light ? green[200] : green[800]
2366
+ }
2367
+ };
2368
+ };
2219
2369
  return {
2220
2370
  base: { light: BASE_LIGHT, dark: BASE_DARK },
2221
2371
  muted: { light: MUTED_LIGHT, dark: MUTED_DARK },
@@ -2228,7 +2378,9 @@ function buildRecipes(r) {
2228
2378
  tropic: { light: TROPIC_LIGHT, dark: TROPIC_DARK },
2229
2379
  sunset: { light: SUNSET_LIGHT, dark: SUNSET_DARK },
2230
2380
  bold: { light: BOLD_LIGHT, dark: BOLD_DARK },
2231
- pastel: { light: PASTEL_LIGHT, dark: PASTEL_DARK }
2381
+ pastel: { light: PASTEL_LIGHT, dark: PASTEL_DARK },
2382
+ atlas: { light: traditional(false, "light"), dark: traditional(false, "dark") },
2383
+ swiss: { light: traditional(true, "light"), dark: traditional(true, "dark") }
2232
2384
  };
2233
2385
  }
2234
2386
  var RECIPES = buildRecipes(STOCK_RAMPS);
@@ -2411,7 +2563,9 @@ var STYLE_THEMES = {
2411
2563
  tropic: { v: 1, name: "tropic", base: "tropic" },
2412
2564
  sunset: { v: 1, name: "sunset", base: "sunset" },
2413
2565
  bold: { v: 1, name: "bold", base: "bold" },
2414
- pastel: { v: 1, name: "pastel", base: "pastel" }
2566
+ pastel: { v: 1, name: "pastel", base: "pastel" },
2567
+ atlas: { v: 1, name: "atlas", base: "atlas" },
2568
+ swiss: { v: 1, name: "swiss", base: "swiss" }
2415
2569
  };
2416
2570
 
2417
2571
  // src/oklch.ts
@@ -2654,6 +2808,12 @@ function diffRecipe(stock, generated) {
2654
2808
  function generateTheme(base, seeds) {
2655
2809
  const ramps = {
2656
2810
  ...STOCK_RAMPS,
2811
+ atlasPaper: reRamp(STOCK_RAMPS.atlasPaper, seeds.land, NEUTRAL_CHROMA_CAP),
2812
+ atlasWater: reRamp(STOCK_RAMPS.atlasWater, seeds.water, CHROMA_CAP),
2813
+ atlasGreen: reRamp(STOCK_RAMPS.atlasGreen, seeds.nature, CHROMA_CAP),
2814
+ swissPaper: reRamp(STOCK_RAMPS.swissPaper, seeds.land, NEUTRAL_CHROMA_CAP),
2815
+ swissWater: reRamp(STOCK_RAMPS.swissWater, seeds.water, CHROMA_CAP),
2816
+ swissGreen: reRamp(STOCK_RAMPS.swissGreen, seeds.nature, CHROMA_CAP),
2657
2817
  ink: reRamp(STOCK_RAMPS.ink, seeds.land, NEUTRAL_CHROMA_CAP),
2658
2818
  paper: reRamp(STOCK_RAMPS.paper, seeds.land, NEUTRAL_CHROMA_CAP),
2659
2819
  graphite: reRamp(STOCK_RAMPS.graphite, seeds.land, MUTED_CHROMA_CAP),
@@ -2881,7 +3041,9 @@ var STYLE_SLOTS = [
2881
3041
  "tropic",
2882
3042
  "sunset",
2883
3043
  "bold",
2884
- "pastel"
3044
+ "pastel",
3045
+ "atlas",
3046
+ "swiss"
2885
3047
  ];
2886
3048
  var STYLE_BITS = 5n;
2887
3049
  var KIND_BITS = 2n;
@@ -3268,6 +3430,11 @@ export {
3268
3430
  amber,
3269
3431
  applyThemeLayers,
3270
3432
  aqua,
3433
+ atlasGreen,
3434
+ atlasPaper,
3435
+ atlasRelief,
3436
+ atlasRoad,
3437
+ atlasWater,
3271
3438
  azure,
3272
3439
  buildRecipes,
3273
3440
  cobalt,
@@ -3309,6 +3476,11 @@ export {
3309
3476
  seedsFromPalette,
3310
3477
  shuffleSeeds,
3311
3478
  sky,
3479
+ swissGreen,
3480
+ swissPaper,
3481
+ swissRelief,
3482
+ swissRoad,
3483
+ swissWater,
3312
3484
  validateTheme,
3313
3485
  verdant,
3314
3486
  wave