Map

An interactive geographic viewport with declarative pins, a selection overlay and zoom controls over the basemap engine you supply.

<tp-map>
Map
Default density Compact density Restart from the initial state Show the copyable source
Belém Tower Av. Brasília, 1400-038 Lisboa LX Factory R. Rodrigues de Faria 103, 1300-501 Lisboa São Jorge Castle R. de Santa Cruz do Castelo, 1100-129 Lisboa

Usage

The map owns the camera, pins, selection, overlays and controls; the basemap comes from an engine you assign to engine. In an application that is createMapLibreEngine(maplibregl) or createGoogleMapsEngine(importLibrary) from @tweakpad/ui/map. This site ships no map library, so its examples use the small OpenStreetMap raster engine from the first example, written against the same public MapEngine contract.

<tp-map id="sights" label="Lisbon sights" default-bounds="38.685,-9.235,38.77,-9.085" reveal="always" controls="zoom-in zoom-out reset" > <tp-map-pin value="castle" latitude="38.7139" longitude="-9.1335" label="São Jorge Castle"> <svg viewBox="0 0 32 40" width="32" height="40" aria-hidden="true">…</svg> <tp-map-overlay> <span slot="title">São Jorge Castle</span> <span slot="description">R. de Santa Cruz do Castelo</span> </tp-map-overlay> </tp-map-pin> </tp-map> <script type="module"> import '@tweakpad/ui/register'; import '@tweakpad/ui/styles.css'; import * as maplibregl from 'maplibre-gl'; import maplibreCss from 'maplibre-gl/dist/maplibre-gl.css?raw'; import { createMapLibreEngine } from '@tweakpad/ui/map'; document.getElementById('sights').engine = createMapLibreEngine(maplibregl, { css: maplibreCss }); </script>

The library has no engine dependency. Adapters in @tweakpad/ui/map receive the engine namespace you import, and every adapter produces the same public behavior.

Implement MapEngine (mount returning getCamera, getBounds, jumpTo, project, unproject, attachOverlay and destroy, plus optional animation, fit, zoom-range, interaction, appearance and resize capabilities) to add another engine.

Pins and overlays

A pin’s default slot replaces the marker with your own markup; SVG using currentColor follows the pin’s color. Selecting a pin opens its tp-map-overlay, a non-modal Popover that follows the pin while the camera moves and closes on Escape or an outside press. tp-map-overlay is a non-modal Popover anchored to its pin: every Popover slot (title, description, header, close) and placement property applies (side defaults to block-start, side-offset to 8).

Selected pin
Default density Compact density Restart from the initial state Show the copyable source
Lisbon office Av. da Liberdade 110, 4th floor · Open 9:00–18:00 Get directions

Without authored content a pin renders the default marker. Pins form one tab stop: arrow keys move between them, Enter or Space selects and Escape clears the selection. Each pin is a button with aria-pressed reflecting selection.

Location list

When pins carry essential information, pair the map with a list of the same places bound to the selection. subscribe keeps the list in step with the map, and selectPin moves the map from the list. The viewport is a labelled region. Every gesture has a control or key equivalent, and the map engine keeps its own keyboard pan and zoom.

Map with a list
Default density Compact density Restart from the initial state Show the copyable source
Copenhagen Coffee Lab Fábrica Coffee Roasters Hello, Kristof

Controls

controls floats a joined Button group over the map, in the order you list the actions; controls-position and controls-orientation place it. tp-map-control with map="id" puts the same buttons anywhere on the page. controls-position picks a logical corner (start/end mirror in right-to-left); bottom corners sit above the engine attribution. Any other floating content goes in a corner slot — slot="top-start", top-end, bottom-start or bottom-end — and unslotted children join the controls-position corner after the built-in buttons. Empty corners are not rendered.

The home view is default-bounds, else default-center with default-zoom, else the fitted pins, else the whole world. The initial camera is the home view and reset returns to it.

External controls
Default density Compact density Restart from the initial state Show the copyable source

Controls stay focusable and become aria-disabled while the map is loading or at a zoom limit, so keyboard users always find them in the same place.

Status

Until an engine is ready the map shows a status message. Replace it through the status slot, for example when the basemap waits for consent. Camera requests made before the engine is ready update the pending camera and apply at mount. With reduced motion (motion-policy="reduce" or the user preference), camera moves apply immediately.

Status slot
Default density Compact density Restart from the initial state Show the copyable source

Accept map cookies to see the store location.

Theming

The basemap follows the map’s computed color-scheme (dark styles in a dark scope). theme assigns colors to ten roles per scheme — background, land, water, park, road, roadMajor, building, boundary, label, labelHalo — using any CSS color, including tokens; tokenMapTheme maps them to the library tokens. Call refreshAppearance() after changing a scoped theme that the map cannot observe.

API Reference

Every element also accepts motion-policy.

Attributes

AttributePropertyTypeDefaultDescriptiondefault-centerdefaultCenterMapPosition | nullnullHome center (latitude,longitude).default-zoomdefaultZoomnumber1Home zoom on the 256-pixel scale.default-boundsdefaultBoundsMapBounds | nullnullHome region (south,west,north,east), fitted with fitPadding.min-zoomminZoomnumber0The farthest the camera can zoom out.max-zoommaxZoomnumber20The closest the camera can zoom in.fit-paddingfitPaddingnumber48Padding in CSS pixels for fitting and reveal visibility.selected-pinselectedPinstring | null—Controlled selection; undefined makes the lane uncontrolled.default-selected-pindefaultSelectedPinstring | nullnullUncontrolled initial selection.revealrevealMapRevealPolicy'if-hidden'Reveal after a selection not made on the pin itself.reveal-zoomrevealZoomnumber | nullnullZoom used when revealing; null keeps the current zoom.interactiveinteractivebooleantrueEngine gestures and keyboard.cooperative-gesturescooperativeGesturesbooleanfalseModifier-gated wheel zoom and two-finger touch pan.labellabelstring | nullnullViewport accessible name; falls back to the map message.controlscontrolsstring | readonly MapControlAction[]''Built-in floating controls, in order (zoom-in zoom-out reset fit-pins); empty renders none.controls-positioncontrolsPositionMapCorner'top-end'The corner that holds the built-in controls and default-slot content.controls-orientationcontrolsOrientationMapControlsOrientation'vertical'Stacking of the built-in controls.

Properties

PropertyTypeDefaultDescriptionengineMapEngine | nullnullThe engine adapter that renders the basemap (property only).themeMapTheme | nullnullBasemap theme roles (property only).messagesMapMessages{}Strings with English fallbacks, inherited by constituents.onSelectedPinChange((event: TpValueChangeEvent<string | null>) => void) | undefined—Observes selection proposals before dispatch.stateread-onlyMapState—The latest frozen state snapshot.nativeread-onlyunknown—The engine's native map object, or null.engineInstanceread-onlyMapEngineInstance | null—The mounted engine instance while ready (advanced use).mapMessagesread-onlyRequired<MapMessages>—The text in use: messages over the defaults.resolvedControlsPositionread-onlyMapCorner—The resolved corner for built-in controls and default-slot content.

Events

EventTypeDescriptiontp-map-readyCustomEvent<{ native: unknown; }>The engine is ready; detail.native is the engine's map object.tp-map-errorCustomEvent<{ error: unknown; }>The engine failed; detail.error.tp-map-requestCustomEvent<MapRequestDetail>Cancelable; every camera and selection request.tp-map-request-failedCustomEvent<MapRequestFailedDetail>A request rejected.tp-map-camera-changeCustomEvent<MapCameraDetail>The camera moved (at most once per frame).tp-map-camera-commitCustomEvent<MapCameraDetail>The camera settled.tp-map-pressCustomEvent<MapPressDetail>A press on the basemap outside pins and overlays.tp-value-changeTpValueChangeEvent<string | null>Cancelable selection proposal (selectedPin).

Methods

MethodReturnsDescriptionrequest(action, value, options?)Promise<MapRequestOutcome>Proposes a camera or selection request by its action name, through the cancelable tp-map-request; resolves with the outcome.zoomIn(step?, options?)Promise<MapRequestOutcome>Zooms in by step levels, one by default.zoomOut(step?, options?)Promise<MapRequestOutcome>Zooms out by step levels, one by default.resetView(options?)Promise<MapRequestOutcome>Returns the camera to the home view of default-center, default-zoom or default-bounds.flyTo(target, options?)Promise<MapRequestOutcome>Moves the camera to a center, zoom or both.fitBounds(bounds, fit, options?)Promise<MapRequestOutcome>Fits a region (south,west,north,east) in the viewport, with optional padding and a zoom limit.fitPins(fit?, options?)Promise<MapRequestOutcome>Fits every selectable pin in the viewport.selectPin(value, options?)Promise<MapRequestOutcome>Proposes a selection (null clears it); the reveal policy applies after commit.revealPin(value, zoom?, options?)Promise<MapRequestOutcome>Centers a pin, at zoom, else reveal-zoom, else the current zoom.project(position)MapPointThe viewport point, in CSS pixels, of a geographic position.unproject(point)MapPositionThe geographic position under a viewport point in CSS pixels.subscribe(selector, callback, options?)() => voidCalls callback whenever the part of the map state that selector picks changes; returns the unsubscribe function.refreshAppearance()voidRe-reads the color scheme and theme tokens (for example after a scoped theme change).

Slots

SlotDescriptiondefaultFloating content in the controls-position corner, after the built-in controls.top-startFloating content in the top inline-start corner.top-endFloating content in the top inline-end corner.bottom-startFloating content in the bottom inline-start corner.bottom-endFloating content in the bottom inline-end corner.statusReplaces the empty, loading and error message.

CSS parts

PartDescription::part(viewport)The labelled region that contains the engine surface and pins.::part(status)The empty, loading or error status.::part(controls)Each floating corner container (also controls-<corner>).::part(control-group)The Button group of built-in controls.

<tp-map-pin>

Attributes

AttributePropertyTypeDefaultDescriptionvaluevaluestring''Identity within the map (required, unique).latitudelatitudenumber—Decimal degrees (required).longitudelongitudenumber—Decimal degrees (required).labellabelstring''Accessible name (required).anchoranchorMapPinAnchor'bottom'Which point of the visual sits on the position.

Properties

PropertyTypeDefaultDescriptionpositionread-onlyMapPosition | null—The pin's position, or null while its coordinates are not valid numbers.pinDisabledread-onlyboolean—Whether the pin is disabled, itself or through its map.accessibleLabelread-onlystring—The name the pin is announced by: its label, else the map's pin message.selectedread-onlyboolean—Whether this pin is the map's effective selection.controlread-onlyHTMLElement | null—The focusable button (overlay anchor).overlayread-onlyTpMapOverlay | null—The child overlay, if any.

Methods

MethodReturnsDescriptionfocus(options?)voidFocuses the pin's button.

Slots

SlotDescriptiondefaultThe pin visual. Defaults to the marker graphic.

CSS parts

PartDescription::part(pin)The focusable button placed at the position.::part(pin-visual)The default marker graphic.

<tp-map-overlay>

Events

EventTypeDescriptiontp-open-changeTpSurfaceOpenChangeEventCancelable proposal to open or close, before the surface changes; detail.value is the proposed state, detail.reason what asked (a trigger press, hover or focus, Escape, an outside press, a close action or code) and detail.trigger the trigger. On a close, detail.preventUnmountOnClose() keeps the content mounted.tp-open-change-completeCustomEvent<{ open: boolean; }>After the surface finishes opening or closing; detail.open.

Slots

SlotDescriptiondefaultThe content of the popup.triggerThe control that opens the popup, usually a Button.anchorAn element to position the popup against instead of the trigger.titleThe heading that names the popup.descriptionText that describes the popup.headerReplaces the header that holds the title and the description.closeAn action that closes the popup when pressed, after its own handlers.

<tp-map-control>

Attributes

AttributePropertyTypeDefaultDescriptionactionactionMapControlAction'zoom-in'The bound action.labellabelstring''Overrides the action message as the accessible name.variantvariantTpButton['variant']'outline'Forwarded to the composed Button.sizesizeTpButton['size']'icon'Forwarded to the composed Button.

Properties

PropertyTypeDefaultDescriptionaccessibleLabelread-onlystring—The control's accessible name: its label, else the action's message.availableread-onlyboolean—Whether activation would act now.groupBoundaryread-onlyElement | null—The composed Button, so a Button group joins this control's seams.

Methods

MethodReturnsDescriptionfocus(options?)voidFocuses the control's button.

Slots

SlotDescriptiondefaultReplaces the default icon.

CSS parts

PartDescription::part(button)The composed Button's native control.::part(icon)The default icon.