| .github/workflows | ||
| app-profiles | ||
| capacitor/android | ||
| contributing | ||
| docs | ||
| internal | ||
| layouts | ||
| library | ||
| public | ||
| .env.example | ||
| .gitattributes | ||
| .gitignore | ||
| bun.lock | ||
| capacitor.config.json | ||
| CONTRIBUTING.md | ||
| index.html | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
| vite.config.js | ||
| vite.doxa-maps-build-config.json | ||
doxa-maps
A bundler that turns your maps into small embeddable pieces you can drop onto any website.
✨ New here, and you don't write code?
You can build real, embeddable maps by describing them to an AI coding agent — no programming required. Start with the friendly, zero-jargon guide: docs/tutorial.md.
In plain words
What a bundler is. It's a tool that takes your map code and packages it into a small, ready-to-use file. You write the map once; the bundler produces the shippable piece.
What it outputs. Plain .js files in the public/ folder — one per bundle. That's it.
No server, no build step on anyone else's side. Serve the folder from anywhere and the maps work.
What a bundle is. A self-contained web component — a custom HTML tag like <my-map>. You add
one <script> and drop the tag on a page. It carries everything it needs, so it runs on any site,
in any framework, or in none.
One bundle can hold several maps. A single bundle can define more than one element
(<my-map>, <coverage-map>, …). One file, many maps — fewer things to load and manage.
The bundles are lean. The build is tree-shaken: only the code a map actually uses gets included, unused code is dropped. Small files, fast loads — which matters when a map embeds on someone else's page.
Embed on other people's websites. Because each map is just a tag, you can put your maps on a partner's or customer's site — or in any tool that accepts an embed, like Start.me, Notion, a CMS, a landing page. Same map, many places. Write it once, embed it everywhere.
Build a whole app (SPA), not just single maps. Bundle several elements into one layout and you get a single-page app made of your maps — lazy-loaded so it stays fast. So the same pieces are both standalone embeds and the building blocks of a full site.
They're JAMstack, so the API is hot-swappable. The map is static; the data/service it talks to is a runtime setting, not baked in. You can point a map at a different API — or swap the map engine itself (Mapbox ↔ MapLibre) — by changing config, no rebuild. Portable and future-proof.
Why it's fast (one self-contained file)
Everything a map needs is built into one file — the Vue runtime, the components, and the CSS are all inlined. The browser makes one request and the map runs. There's no waterfall of extra downloads and no dependency on a separate Vue CDN being online or version-matched.
- One output file. The JS carries its own CSS (
cssCodeSplit: false) and its own Vue, built as a single IIFE. Nothing to wire up, nothing that can 404 on its own. - Faster than loading Vue from a CDN. A CDN copy of Vue is an extra request plus a version you have to keep in sync; inlining removes both. Fewer moving parts, less technical debt.
- Lean. Tree-shaking drops unused code, so the single file stays small even though it's complete.
(The one deliberate exception is the heavy PDF-export library — loaded from a CDN only if someone actually exports — so the everyday map load never pays for it.)
Quickstart
Bun is required (bun.lock is the committed lockfile — install from https://bun.sh).
bun install
bun run build # builds every app-profiles/<bundle>/ → public/doxa-maps/<bundle>/<bundle>.js
bun run dev # HMR staging at http://localhost:5173/ (does NOT update a built public/)
Built output lands in public/, the committed, served web-root. Serve that folder from
anywhere and the bundles are embeddable from it.
Embed a map
<link href="https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css" rel="stylesheet" />
<script src="https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js"></script>
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js"></script>
<script src="/doxa-maps/my-map/my-map.js"></script>
<my-map profile-config='{"profile":"my-map","tk":"pk.eyJ..."}'></my-map>
The bundle is a plain <script> web component — the host page needs no build step and no
framework. profile-config is read at runtime, so the same bundle re-points at different data,
colors, or API with no rebuild.
Docs
- Why doxa-maps — the problem, the "map islands" idea, one diagram.
- Tutorial — build your first map, step by step.
- How-to — task recipes: embed, add a profile, choose a CDN, set tokens.
- Reference — file map, config fields, contracts, the
@maplibrary index. - Explanation — the short "what it is".
- Architecture — the deep end: contracts, catalogs, wiring.
Try it live: bun run dev opens the staging playground — every map, clickable, with a
paste-ready embed snippet for each.
Contributors: see CONTRIBUTING.md.
License
Built on the doxa-maps template (Apache-2.0) — the shared library/ and build tooling are a
permissive base everyone shares. Your maps under app-profiles/<bundle>/ are yours: your color
strategies, API connections, and i18n. Full text: LICENSE.