Skip to content

Map

cui-map is a Leaflet-backed map that reads place data from its light-DOM children. Search engines and screen readers see the addresses, regions, and links before any JavaScript loads. Leaflet itself is dynamically imported when the map scrolls into view, keeping it off the first-paint budget. Bring your own tile provider via tile-url and tile-attribution.

Install

cui-map uses Leaflet as an optional peer dependency, dynamically imported the first time a map appears — so pages without a map never load it. Leaflet's CSS is bundled into the element's shadow DOM, so you never need an extra <link> tag.

With a bundler

Install Leaflet alongside Combat UI (add leaflet.markercluster for the cluster attribute) and your bundler resolves it like any other dependency:

Without a build step

cui-map loads Leaflet through the bare "leaflet" specifier, so in a no-bundler setup you can resolve it straight from a CDN with an import map — no npm install required:

<script type="importmap"> { "imports": { "leaflet": "https://esm.sh/leaflet@1.9.4", "leaflet.markercluster": "https://esm.sh/leaflet.markercluster@1.5.3?external=leaflet" } } </script> <script type="module"> import "combat-ui/auto"; </script>

The leaflet.markercluster entry is only needed for the cluster attribute. The ?external=leaflet query keeps the plugin on the same Leaflet instance the map uses, so clustering attaches correctly instead of patching a second copy.

Points

Each location is an <article class="cui-map-point"> with data-lat / data-lng. Its inner heading, paragraphs, and links survive into the marker popup — the popup content is a clone of the article, so anything in light DOM renders both in the fallback list and in the popup.

Office locations

City placeholder · NL

Address placeholder, 1012 AB City

Open Mon–Fri, 09:00–17:00.

Directions

City placeholder · UK

Address placeholder, EC1A 1BB City

City placeholder · FR

Address placeholder, 75001 City

Regions

Regions are authored as inline GeoJSON inside <script type="application/geo+json" class="cui-map-region">. The script tag is inert without JS but parseable to search engines. Add data-label for hover tooltips and data-color for a per-region class hook.

Coverage

GeoJSON content can be a bare Geometry or a wrapped Feature. Clicking a region fires cui-map-region-click with the region's script element and the parsed feature.

Clustering

Add the cluster attribute to group nearby markers when zoomed out. The leaflet.markercluster plugin is dynamic-imported only when the attribute is present, so non-clustered maps don't pay for it.

Stores

Store 01

Store 02

Store 03

Store 04

Store 05

Store 06

Store 07

Store 08

Custom icons

Slot one or more <template slot="marker-icon"> elements with data-category to map them to points. Points with a matching data-category use that template's HTML as their marker. SVG paths that use fill="currentColor" inherit --cui-map-marker-color automatically.

Office placeholder

Event placeholder

A template without data-category becomes the default for any point without a matching category. Points without a slotted icon fall back to the built-in teardrop pin.

Tile providers

cui-map is provider-agnostic. The default URL points at the raw OpenStreetMap server, which is fine for low-traffic docs and demos but not suitable for production. Pick one of the patterns below — or anything else with a Leaflet-compatible XYZ URL — and always preserve the provider's required attribution.

Free, no key, strict usage policy. Fine for documentation, prototypes, and low-traffic marketing sites. Switch providers before you ship a production app.

Free for non-commercial use. Swap light_all for dark_all, voyager, etc. See the basemap list for variants.

Free tier up to a generous quota. API key required for production traffic — append ?api_key=… to the URL template. See themes for variants.

Paid past the free tier. Replace YOUR_TOKEN with your public access token; raster styles include light-v11, dark-v11, streets-v12, outdoors-v12, satellite-v9.

SEO & JSON-LD

The map block keeps content in semantic HTML, so search engines crawl it without executing JavaScript. Pair it with structured data per page for richer results. The component does not emit JSON-LD itself — author it once from the same source data that renders the cards.

For event locations use Event with location.@type = "Place"; for stores use LocalBusiness. See schema.org/Place for the full vocabulary.

API reference

Attributes
Attribute Default · Purpose
center "0,0". Comma-separated lat,lng for the initial view.
zoom 4. Initial zoom level.
min-zoom · max-zoom Optional bounds on user-controlled zoom.
tile-url Default: OpenStreetMap. Leaflet-compatible XYZ URL template.
tile-attribution HTML-safe attribution string. Always render the provider's required credit.
tile-subdomains "abc". Used by the {s} placeholder in tile URLs.
cluster Boolean. Loads leaflet.markercluster and groups nearby markers.
fit-bounds Boolean — true by default when > 1 point. Set "false" to keep the configured center / zoom; set "true" to force fit even with one marker.
interactive "true". Setting "false" disables drag and the zoom control.
scroll-wheel-zoom "true". Setting "false" keeps the page scrollable when the cursor is over the map.
data-theme="dark" Forces dark tile filtering on a per-instance basis. Otherwise the global theme token --cui-map-tile-filter drives it.
Slots and child markup
Slot / element Purpose
slot="title" Heading rendered above the canvas. Stays in light DOM for SEO.
slot="legend" Optional legend rendered between the canvas and the fallback list.
slot="marker-icon" One or more <template> elements. Each may carry data-category. Used as marker HTML for any point with a matching category.
.cui-map-point An <article> with required data-lat / data-lng, optional data-category. Cloned into the marker popup.
.cui-map-region <script type="application/geo+json"> containing a Feature or Geometry. Optional data-label for hover tooltip and data-color for a per-region class hook (.cui-map-region--<color>).
Properties and methods
API Purpose
map.leafletMap Reference to the underlying Leaflet L.Map. null until the element enters the viewport and mounts.
map.isReady Boolean — has Leaflet finished mounting?
map.clustered · map.interactive · map.scrollWheelZoom Boolean getters reflecting their attributes.
map.refresh() Re-read children and rebuild markers + regions. Call after dynamic content updates.
Events
Event Detail · Behavior
cui-map-ready { map }. Fired after Leaflet mounts. Use this to attach custom layers, controls, or panes.
cui-map-point-click { point, lat, lng }. Fired when a marker is clicked. point is the original light-DOM <article>.
cui-map-region-click { region, feature }. Fired when a region polygon is clicked. region is the original <script> element.
cui-map-bounds-change { bounds, zoom }. Fires on the trailing edge of pan and zoom.
CSS custom properties
Variable Default
--cui-map-block-size 28rem
--cui-map-radius --cui-radius-2
--cui-map-border-color --cui-color-border
--cui-map-surface --cui-color-surface-muted
--cui-map-tile-filter none · inherits dark value from tokens.css
--cui-map-marker-color --cui-color-accent
--cui-map-region-fill Accent at 18% alpha
--cui-map-region-fill-hover Accent at 28% alpha
--cui-map-region-stroke Accent at 80% alpha
--cui-map-region-stroke-width 2
--cui-map-popup-bg --cui-color-surface-raised
--cui-map-popup-color --cui-color
--cui-map-popup-radius --cui-radius-2
--cui-map-popup-shadow --cui-shadow-2
--cui-map-popup-padding --cui-space-4
--cui-map-control-bg --cui-color-surface-raised
--cui-map-control-border --cui-color-border
--cui-map-attribution-bg --cui-color-surface