<geo-map>
A zero-dependency map component using OpenStreetMap tiles. Renders a static tile grid centered on given coordinates with an optional marker pin and address caption.
Overview
The <geo-map> component displays a static map using free OpenStreetMap tiles. It calculates tile coordinates from lat/lng attributes and renders a 3×3 tile grid clipped to the component dimensions.
Key features:
- Zero dependencies — no API keys, no external libraries
- Free tiles from OpenStreetMap or Carto
- Progressive enhancement — slotted
<address>visible before JS - SVG marker pin with customizable color
- CSS custom properties for styling
- Shadow DOM with exposed CSS parts
20 W 34th St, New York, NY 10001
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
lat |
Number | — | Latitude of map center |
lng |
Number | — | Longitude of map center |
zoom |
Number | 15 |
Tile zoom level (1–19) |
marker |
Boolean | true |
Show pin at center. Set to "false" to hide. |
marker-color |
String | #e74c3c |
Pin fill color |
provider |
String | osm |
Tile source: osm, carto-light, carto-dark |
src |
String | — | ID of an external <address data-lat data-lng> or <script type="application/ld+json"> element to resolve coordinates from |
place |
String | — | Name to match against JSON-LD Place scripts on the page (matches data.name) |
Tile Providers
Three free tile providers are supported. All require attribution (included automatically).
Marker
The marker pin is an inline SVG positioned at the center of the map. Customize its color with the marker-color attribute or hide it with marker="false".
Caption
Slot an <address> (or any content) as a caption overlay at the bottom of the map. This content is also the no-JS fallback.
No-JS Fallback
Before the component upgrades, the slotted content is visible as a styled card. Include a link to OpenStreetMap so users can still find the location.
Data Binding
Coordinates can come from 6 sources, checked in priority order. The first match wins:
- Explicit
lat/lngattributes — highest priority src→<address data-lat data-lng>— reference an external address element by IDsrc→<script type="application/ld+json">— reference a JSON-LD script by ID- Slotted
<address data-lat data-lng>— address inside the component placeattribute — scans all JSON-LD scripts for a matchingname<meta name="geo.position">— page-level fallback
src → address element
Point src to the ID of an <address> element with data-lat and data-lng attributes. The address stays visible as semantic content while the map reads its coordinates.
src → JSON-LD script
Point src to the ID of a <script type="application/ld+json"> element. The component reads geo.latitude and geo.longitude from the parsed JSON.
Slotted <address> with coordinates
Slot an <address> with data-lat and data-lng directly inside the component. The address appears as a caption and also provides the coordinates.
place attribute
Set place to a name that matches the name field in a JSON-LD Place script anywhere on the page. Useful when structured data already exists for SEO.
<meta> fallback
As a last resort, the component checks for a <meta name="geo.position"> tag in the document. The content format is "lat;lng".
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--geo-map-height |
300px |
Component height |
--geo-map-border-radius |
0.5rem |
Border radius |
--geo-map-marker-color |
#e74c3c |
Marker pin fill (overridden by marker-color attr) |
--geo-map-marker-size |
32px |
Marker pin dimensions |
--geo-map-caption-bg |
rgba(255,255,255,0.9) |
Caption background |
--geo-map-caption-padding |
0.5rem 0.75rem |
Caption padding |
--geo-map-overlay-bg |
rgba(0,0,0,0.35) |
Overlay background on hover/focus |
--geo-map-overlay-color |
#fff |
Overlay text color |
--geo-map-attribution-font-size |
0.625rem |
Attribution text size |
CSS Parts
| Part | Element | Purpose |
|---|---|---|
container |
Outer wrapper | Overall sizing and border radius |
tiles |
Tile grid area | Map image area |
marker |
SVG pin | Marker styling |
overlay |
Overlay wrapper | Full-surface hover overlay containing the activate button |
activate |
Button | Click-to-interact trigger inside the overlay |
caption |
Slot wrapper | Address/label display |
attribution |
OSM credit | Required attribution link |
Interactive Mode
By default, hovering over the map reveals a "Click to interact" button. Clicking activates pan and zoom — no external libraries loaded. The interactive layer is lazy-loaded on first activation.
Use interactive="eager" to activate immediately, interactive="none" to hide the button, or static-only to fully prevent interaction.
Interactive Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
interactive |
String | click |
Activation mode: click (show button on hover), eager (activate immediately), none (no button) |
static-only |
Boolean | false |
Prevent interactive activation entirely. No activate button rendered. |
Keyboard Navigation
When interactive mode is active, the following keyboard controls are available:
| Key | Action |
|---|---|
| Arrow keys | Pan the map (up, down, left, right) |
| + / = | Zoom in |
| - | Zoom out |
| Escape | Exit interactive mode, return to static view |
Events
| Event | Detail | Description |
|---|---|---|
geo-map:ready |
{ lat, lng, zoom } |
Fired after tiles finish loading |
geo-map:activate |
{ lat, lng, zoom } |
Fired when interactive mode is activated |
geo-map:move |
{ lat, lng, zoom } |
Fired after pan or zoom in interactive mode |
geo-map:error |
{ message } |
Fired on tile load failure or missing coordinates |
Contact Page Example
A common use case: pair <geo-map> with a contact form in a sidebar layout.
Accessibility
- Tile grid has
role="img"with anaria-labeldescribing the coordinates - Marker SVG is
aria-hidden="true"(decorative) - Attribution link opens in a new tab with
rel="noopener" - Slotted
<address>content is accessible before and after JS loads
Related Elements
<icon-wc>— Icon component (similar Shadow DOM pattern)- Contact patterns — Contact form layouts with map integration