npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@topolyne/style-engine

v0.5.2

Published

Translates a Topolyne semantic map config into a real MapLibre GL style — the design engine behind every Topolyne map.

Readme

@topolyne/style-engine

Translates a Topolyne MapConfig into a real MapLibre GL style, plus the presets, provider config, geocoding, directions, and color helpers behind the Topolyne editor and @topolyne/react.

Most apps just embedding a published map never need this package — @topolyne/react calls into it for you. Reach for @topolyne/style-engine directly if you're building your own editor or renderer against the Topolyne config format, self-hosting the tile/terrain/geocoding/routing providers, or want the standalone color utilities.

npm install @topolyne/style-engine

Building a style

import { buildMapStyle, defaultProvider } from "@topolyne/style-engine";
import type { MapConfig } from "@topolyne/config-schema";

const style = buildMapStyle(config as MapConfig, defaultProvider);
// style is a real maplibre-gl StyleSpecification — hand it straight to `new maplibregl.Map({ style, ... })`

buildMapStyle(config, provider, options?) is the single function that turns semantic config ("roads should look like this", "show hillshade at 0.6 intensity") into concrete MapLibre sources and layers — every OpenMapTiles source-layer name and zoom-interpolated width lives here. Pass a contourSource (see below) in options to include contour lines when config.terrain.contours is on; omitted, terrain/contour features degrade gracefully instead of throwing. config.markers (up to MAX_MARKERS, see @topolyne/config-schema) are rendered as their own GeoJSON source and pin/label layers automatically — nothing extra to call for those. Setting config.terrain.satellite swaps the vector-tile land styling for real aerial imagery from provider.satelliteTiles as the base layer; roads, borders, labels, and buildings still render on top.

Presets

import { presets, getPreset, configFromPreset } from "@topolyne/style-engine";

presets;                 // Preset[] — all 8 built-in presets, for a theme picker
getPreset("terrain");    // Preset — throws if the id is unknown
configFromPreset("terrain", location, "My trip");  // MapConfig

Eight built-in presets ship today — terrain, midnight-terrain, paper-atlas, slate, sandstone, arctic, neon-grid, forest-canopy — each a distinct design decision across palette, road treatment, terrain relief, and border/label density, not one template with the background color swapped. A Preset carries id, name, description, three swatches for a thumbnail, and the same colors/roads/terrain/layers/borders/labels shape a MapConfig uses. configFromPreset(presetId, location, name?) builds a full MapConfig by applying a preset to a searched-to location.

Providers

import { defaultProvider, withProviderOverrides } from "@topolyne/style-engine";

const provider = withProviderOverrides({
  vectorTiles: { url: "https://tiles.example.com/planet" },
});

MapProviderConfig is the one place that names outside tile/data vendors — presets and the rest of the style engine only ever talk about semantic config. defaultProvider points at OpenFreeMap for vector tiles, MapTiler terrain-RGB for elevation and MapTiler satellite tiles for aerial imagery (both only when NEXT_PUBLIC_MAPTILER_API_KEY is set in the environment — otherwise demTiles/satelliteTiles are left undefined and those layers are skipped), Nominatim for geocoding, and OpenRouteService for directions. Note the NEXT_PUBLIC_ prefix: buildMapStyle() runs client-side wherever defaultProvider is used, so the key needs to reach the browser bundle — this is the same trust model MapTiler (and Mapbox) already design their keys for, restricting abuse by domain rather than by secrecy. withProviderOverrides shallow-merges your overrides onto the default, so swapping one provider (say, a self-hosted OpenMapTiles server) means passing one field, not reconstructing the whole object. Routing and geocoding API keys are never part of this object since it ships into the browser bundle — see Directions and Geocoding below.

Terrain contours

import { setupContourSource, DEFAULT_CONTOUR_THRESHOLDS } from "@topolyne/style-engine";
import maplibregl from "maplibre-gl";

const contourSource = await setupContourSource(provider, maplibregl);
const style = buildMapStyle(config, provider, { contourSource });

setupContourSource wraps maplibre-contour: it registers a mlcontour:// protocol and spins up a Web Worker, so it's browser-only and must run once, client-side, against a live maplibre-gl module — never during server-side style generation. Returns undefined when provider.demTiles isn't configured. DEFAULT_CONTOUR_THRESHOLDS is the zoom-to-interval map ({ 9: 500, 11: 200, 12: 100, 14: 50, 16: 20 }) buildMapStyle uses when drawing contour lines.

Geocoding

import { searchLocation, zoomForBoundingBox } from "@topolyne/style-engine";

const results = await searchLocation("Belfast", provider, { limit: 5 });
// [{ label, latitude, longitude, boundingBox? }, ...]

zoomForBoundingBox(results[0].boundingBox!); // rough zoom for "fly to this result"

searchLocation calls the Nominatim-compatible endpoint in provider.geocode. Nominatim's usage policy requires a descriptive User-Agent and forbids heavy client-side polling, so call this from a server route, not directly from the browser (mirrored by the hosted app's /api/geocode).

Directions

import { fetchRoute, defaultProvider } from "@topolyne/style-engine";

const route = await fetchRoute(
  [-5.9301, 54.5964],
  [-5.9081, 54.6031],
  defaultProvider,
  { apiKey: process.env.ORS_API_KEY!, profile: "driving-car" },
);
// { coordinates: [number, number][], distanceMeters, durationSeconds }

fetchRoute(origin, destination, provider, opts) calls OpenRouteService's directions API and returns a route ready to draw as a GeoJSON line. opts.profile defaults to "driving-car" — also accepts "cycling-regular" and "foot-walking". opts.waypoints adds stops in between. OpenRouteService needs a secret API key, so — exactly like geocoding — this must be called from a server route, never from the browser (mirrored by the hosted app's /api/directions, which is what @topolyne/react's <MapRoute> actually calls).

Color utilities

Small, dependency-free hex color helpers used to derive secondary tones (hillshade shadow/highlight, border ink, label color) from the handful of colors a config exposes:

import { mix, lighten, darken, luminance, withAlpha, readableInk } from "@topolyne/style-engine";

mix("#ffffff", "#000000", 0.5);   // "#808080"
lighten("#336699", 0.2);          // blend toward white
darken("#336699", 0.2);           // blend toward black
luminance("#336699");             // 0 (black) - 1 (white)
withAlpha("#336699", 0.5);        // "rgba(51, 102, 153, 0.5)"
readableInk("#336699");           // { text, halo } - readable label ink for this background

Requirements

  • maplibre-gl ^4.7.1 (peer dependency)
  • @topolyne/config-schema (installed automatically)

License

MIT