The style CLI
To see a conversion rather than read it, the style preview runs this same converter in the browser and renders the result with the SDK.
npx @massif-maps/style-tools mapbox2css style.json out/ --validate
massif-style is one command carrying every style conversion the project ships. It is an npm
package, so a style author needs no C++ toolchain, and the conversions that must agree with the
SDK run the SDK's own code compiled to WebAssembly — there is no second implementation to drift.
The package is built and tested in CI but has not been published to npm. Until it is, take
massif-style.mjs and massif-style.wasm from a style-tools-v*
release, or build it from
tools/style-cli/.
| Subcommand | What it does |
|---|---|
css2xml <project.json> <out.xml> | compiles a CartoCSS style project to the mapnik XML the decoder reads |
mapbox2css <style.json> <out-dir> | translates a MapBox / MapLibre style JSON into a CartoCSS project |
Node 20 or newer. The wasm build uses NODERAWFS, so it runs under Node — not in a browser.
Converting a MapBox or MapLibre style
massif-style mapbox2css style.json out/ --validate
Writes a style project — project.json plus the .mss files — which is exactly what a
"project" style set loads:
{"type": "vector",
"source": "osm",
"style": {"type": "mbvt",
"project": {"type": "project",
"assets": {"type": "dir", "path": "/sdcard/my-style"},
"name": "style"}}}
Flags:
--validate | compiles the result with css2xml in the same process, so CartoCSS the compiler would reject fails here rather than on device |
--strict | exit non-zero if any MapBox property was dropped |
--shield-anchors [sides] | let a POI name take the first free side of its icon, and draw the icon alone when none is (shield-anchors + shield-text-optional). Sides in preference order, default right,left,top,bottom; a layer stating its own text-variable-anchor keeps it |
--icon-font FACE--icon-font-map FILE | draw every shield icon as a glyph of an icon font instead of a sprite, so the style ships one font rather than a sheet of PNGs. FILE maps icon-image names onto that face's characters ({"mountain": ""}, "U+E90A" and 59658 all read). A name it has no glyph for draws no icon, so country artwork is lost; a marker — a oneway arrow, a crossing — keeps its sprite |
massif-style mapbox2css --help lists the rest.
Every conversion prints a coverage report naming each property it could not carry and how often it appeared. That is the number to read: a translation is not "done" because it produced files.
What it cannot carry
These are CartoCSS gaps, not converter bugs — the report names them one by one:
- Layer types with no symbolizer —
heatmap, andsymbollayers that have only an icon. - Properties —
line-blur,line-gap-width,line-gradient,fill-extrusion-pattern, every*-translate, mostraster-*adjustments. - Expressions with no CartoCSS form —
feature-state,within,let/var,number-format,image,%,abs/floor/ceil, andinterpolateover anything other than zoom. - Draw order across source layers. One entry in the project's
layersarray pulls every attachment of that name, so two MapBox layers on different source layers cannot be interleaved. The converter reports how many end up out of order rather than reordering silently. - The sprite.
icon-imageis dropped, so a symbol layer with an icon and no text produces nothing.
Compiling a CartoCSS project
massif-style css2xml project.json style.xml
massif-style css2xml --roundtrip project.json style.xml # parse the XML back and diff it
This is the SDK's own compiler — CartoCSSMapLoader and MapGenerator, the same code the map runs
at runtime — so a project that compiles here renders there. Use it in CI to fail a style change
before it reaches a device.
Writing the CartoCSS by hand
Every property the decoder understands, with its live/baked flag, is in the CartoCSS property reference. Properties marked live can be changed through a style parameter with a repaint instead of a re-decode.
Also planned
carto2css— mapnik XML back to CartoCSS. Not written;MapGeneratoronly goes one way.- A browser build, for a playground that compiles a style in the page.
NODERAWFSrules the current artifact out.
How the package is built, why the split between wasm and TypeScript, and how it is released: style tools internals.