Protomaps Basemaps
Generate MapLibre GL style specs and cartographic layers for Protomaps PMTiles vector tiles.
Repository Health
Technical Analysis
Protomaps Basemaps is the TypeScript styling layer of the Protomaps basemap project: it turns a PMTiles-backed vector tile source into a ready-to-render MapLibre GL style specification. Rather than hand-writing hundreds of layer definitions, developers call a small set of exported functions to assemble the full basemap layer stack — earth, landcover, water, roads, buildings, boundaries, and multilingual labels — pre-tuned for OpenStreetMap-derived data.
The package ships five built-in color flavors (light, dark, white, grayscale, black) plus a language module that resolves the correct script and font per locale so labels render legibly across writing systems. A companion CLI (generate_style) wraps the same logic to emit a static style.json file for build pipelines that don’t want a runtime dependency.
What You Get
- A
layers(source, flavor, options)function that returns the complete MapLibre GL layer stack (background, earth, landcover, water, buildings, roads, boundaries, labels) for a given vector tile source name - Five built-in
Flavorpresets —LIGHT,DARK,WHITE,GRAYSCALE,BLACK— each a plain object of ~90 named colors that can be spread and partially overridden for custom themes - A language module (
get_multiline_name,get_country_name,language_script_pairs) that selects the correct name field and font per script so labels render correctly across Latin, CJK, Devanagari, Arabic, and other scripts - A
generate_styleCLI andgenerateStyle()function that assemble a fullStyleSpecification(sources, glyphs, sprite, layers) from a TileJSON URL, flavor, and language, for use in static build pipelines - TypeScript types (
Flavor,Pois,Landcover) so custom themes and partial overrides are checked at compile time
Common Use Cases
- Rendering a full-featured, multilingual basemap in a MapLibre GL JS or MapLibre Native app backed by Protomaps PMTiles, without writing raw style JSON
- Generating a static
style.jsonat build time (via thegenerate_styleCLI) for CDN-hosted or offline map deployments - Building a custom-branded map theme by spreading a built-in flavor (e.g.
DARK) and overriding a handful of colors while keeping the full layer stack intact - Swapping label language at runtime (e.g.
en,ja,ar) using the same layer stack, for internationalized map products
Under The Hood
Architecture — The package is organized as four cooperating modules under styles/src/: flavors.ts defines the Flavor interface and the five built-in color presets as flat objects of ~90 named color slots; base_layers.ts (the largest file, ~2,070 lines) exports nolabels_layers() and labels_layers(), which map a Flavor plus a vector-tile source name into an ordered array of MapLibre GL LayerSpecification objects (background through bridges, then labels); language.ts resolves per-script name fields and font stacks for label layers; and index.ts composes these into the single public layers(source, flavor, options) entry point plus namedFlavor(). generate_style.ts and cli.ts sit on top as a thin CLI that reads a TileJSON URL and flavor argument and writes a full StyleSpecification (sources, glyphs, sprite, layers) to disk — no network calls happen inside the library itself; it only assembles JSON describing how a MapLibre renderer should draw tiles it fetches separately.
Tech Stack — Written entirely in TypeScript, built with tsup to dual ESM/CJS output (dist/esm, dist/cjs) with generated .d.ts types, and run/tested via tsx’s native Node test runner rather than a separate test framework. The only runtime-relevant dependency is @maplibre/maplibre-gl-style-spec, used both for its LayerSpecification/StyleSpecification types and, in tests, its validateStyleMin() validator. Linting/formatting is handled by Biome instead of ESLint/Prettier. The package lives inside a larger monorepo (protomaps/basemaps) alongside a Java-based Planetiler tile-generation profile and a SvelteKit-based demo app, but the npm package itself has no dependency on either.
Code Quality — Test coverage is real but narrow: five test files under styles/test/ validate that every built-in flavor produces a spec-valid style (via validateStyleMin), that custom flavor overrides (spreading DARK with one color changed) still validate, and that the CLI’s generateStyle() produces expected output — but there is no per-layer unit testing of the ~2,000-line base_layers.ts beyond end-to-end style validation. Naming is consistent and descriptive throughout (nolabels_layers, get_multiline_name), TypeScript types are used pervasively including for the large Flavor interface, and the code has almost no inline comments, relying on type signatures and small function bodies for readability.
API Design — The public surface is intentionally minimal: two functions (layers, namedFlavor) and five exported constants cover the common path, and the Flavor object’s plain-data shape makes theming a matter of object spreading rather than a fluent builder API, which is low-boilerplate but requires consulting flavors.ts to know all ~90 color keys since documentation is limited to a one-paragraph README plus generated TypeDoc. There is no runtime validation of custom Flavor objects — malformed themes are only caught by TypeScript at compile time or by manually running the style through validateStyleMin.
Used by 2 apps in this directory
PostHog
Analytics · Monitoring · Developer Tools
The all-in-one open source product platform combining analytics, session replay, feature flags, error tracking, AI observability, and a built-in data warehouse in a single self-hostable stack.
World Monitor
Monitoring · Analytics
Real-time global intelligence dashboard that fuses AI-synthesized news, geopolitical risk scoring, and infrastructure tracking into one open-source situational awareness platform.