City placeholder · NL
Address placeholder, 1012 AB City
Open Mon–Fri, 09:00–17:00.
Directions
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.
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.
Install Leaflet alongside Combat UI (add leaflet.markercluster for the
cluster attribute) and your bundler resolves it like any other dependency:
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:
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.
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.
Address placeholder, 1012 AB City
Open Mon–Fri, 09:00–17:00.
DirectionsAddress placeholder, EC1A 1BB City
Address placeholder, 75001 City
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.
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.
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.
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.
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.
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.
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.
| 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. |
| 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>).
|
| 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. |
| 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. |
| 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 |