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
Empire State Building
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).

OSM (default)
Carto Light
Carto Dark

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:

  1. Explicit lat/lng attributes — highest priority
  2. src<address data-lat data-lng> — reference an external address element by ID
  3. src<script type="application/ld+json"> — reference a JSON-LD script by ID
  4. Slotted <address data-lat data-lng> — address inside the component
  5. place attribute — scans all JSON-LD scripts for a matching name
  6. <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.

Click to activate (default)
Hover, then click to interact. Drag to pan, scroll to zoom, Escape to exit.
Static only
Tokyo — no 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 an aria-label describing 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