mirror of
https://github.com/tabler/tabler.git
synced 2026-10-12 21:27:06 +04:00
- ready() and onStyle() hooks, as the map is now created after the library loads - popup fade-in and .map-marker-pulse
433 lines
21 KiB
Plaintext
433 lines
21 KiB
Plaintext
---
|
|
title: Map
|
|
summary: A map shows places and markers on an interactive world map. Tabler turns a single element into a themed map, and the map itself is rendered by MapLibre GL JS.
|
|
description: Show interactive maps with markers, popups and a matching dark mode from plain HTML, using MapLibre GL JS and free map tiles. No account or access token is needed.
|
|
related: [/ui/plugins/vector-map]
|
|
docs-libs: [maplibre-gl]
|
|
source: core/scss/vendor/_maplibre.scss
|
|
css-vars: map-marker
|
|
classnames:
|
|
component:
|
|
- class: ratio
|
|
desc: Wrapper that keeps the map at a fixed aspect ratio
|
|
- class: map-marker
|
|
desc: Marker drawn in HTML, a circle that can hold an icon, a count or an avatar
|
|
part:
|
|
- class: map-marker-label
|
|
desc: Short text under a marker
|
|
- class: map-controls
|
|
desc: Column of buttons in the top end corner of the map
|
|
modifier:
|
|
- class: map-marker-dot
|
|
desc: Small dot with no content
|
|
- class: map-marker-pulse
|
|
desc: Ring that grows out of the marker and fades, for a live position
|
|
- class: map-marker-pin
|
|
desc: Teardrop pin that points down, used with the bottom anchor
|
|
- class: map-popup-card
|
|
desc: Popup with no padding and a fixed width, for an image above the text
|
|
- class: map-tooltip
|
|
desc: Popup shown as a small dark label, for text on hover
|
|
color:
|
|
- class: map-marker-{color}
|
|
desc: Any theme color, for example map-marker-red or map-marker-green
|
|
size:
|
|
- class: ratio-{ratio}
|
|
desc: The aspect to hold, for example ratio-16x9 or ratio-21x9
|
|
---
|
|
import Example from '@components/Example.astro';
|
|
import Map from '@shared/components/demo/Map.astro';
|
|
import Icon from '@ui/Icon.astro';
|
|
import CodeDocs from '@components/CodeDocs.astro';
|
|
import { Code } from 'astro:components';
|
|
import { site } from '@shared/lib/site.ts';
|
|
import { cdnJsTag, cdnMapJsTag, cdnPluginSnippet } from '@lib/cdn-snippets.ts';
|
|
|
|
## Overview
|
|
|
|
The map component turns an element into an interactive map. Add `data-bs-toggle="map"` to it, set the center and the zoom with data attributes, and write the markers as child elements. No script is needed.
|
|
|
|
The map is drawn by [MapLibre GL JS](https://maplibre.org/maplibre-gl-js/docs/), an open-source map library, which you load next to Tabler. The map tiles come from [OpenFreeMap](https://openfreemap.org/). It needs no account and no access token, and it can be used in commercial projects.
|
|
|
|
If you only need a simple map of countries or regions, use the [vector map](/ui/plugins/vector-map) instead. It makes no network calls at all.
|
|
|
|
## Installation
|
|
|
|
The map component is not part of `tabler.js`. It ships as a small add-on, `tabler-map.js`, so pages without a map do not carry it. Load the MapLibre stylesheet, and the add-on after `tabler.js`; it adds `MapView` to the `tabler` object:
|
|
|
|
<Code
|
|
lang="html"
|
|
code={`<link rel="stylesheet" href="${site.cdnUrl}/dist/libs/maplibre-gl/dist/maplibre-gl.css" />
|
|
${cdnJsTag()}
|
|
${cdnMapJsTag()}`}
|
|
/>
|
|
|
|
The add-on loads MapLibre GL JS by itself, from `dist/libs/maplibre-gl` next to the Tabler scripts. Tabler uses version 6, which is published as an ES module. When your files are somewhere else, point the `library` option at the module: `data-bs-library="/vendor/maplibre-gl.mjs"`. The library loads two more files from its own folder, `maplibre-gl-shared.mjs` and `maplibre-gl-worker.mjs`, so keep the three together.
|
|
|
|
If the page sets `window.maplibregl` before the add-on runs, the component uses that and loads nothing. This is the way to go with a bundler:
|
|
|
|
```bash
|
|
npm install maplibre-gl
|
|
```
|
|
|
|
```js
|
|
import * as maplibregl from 'maplibre-gl'
|
|
import 'maplibre-gl/dist/maplibre-gl.css'
|
|
|
|
window.maplibregl = maplibregl
|
|
|
|
const { MapView } = await import('@tabler/core/dist/js/tabler-map.esm.js')
|
|
```
|
|
|
|
<Danger title="Use version 6.4.1 or newer">
|
|
Older versions, including all of version 5, have a known security issue in the code that cleans the attribution text.
|
|
</Danger>
|
|
|
|
Browsers do not load ES modules from a page opened straight from the disk. Use a local server to see the maps while you work.
|
|
|
|
Tabler styles the map controls, popups and attribution to match the interface, in light and dark mode. These styles are in `tabler-vendors.css`, so load it after the MapLibre stylesheet:
|
|
|
|
<Code lang="html" code={cdnPluginSnippet(['vendors'])} />
|
|
|
|
## Usage
|
|
|
|
### Basic map
|
|
|
|
Put the map element inside a `ratio` element, so the map keeps its shape on every screen size. `data-bs-center` takes the longitude first and the latitude second. This is the opposite of what most map services show.
|
|
|
|
<Example flush>
|
|
<Map rounded={false} mapId="simple" />
|
|
</Example>
|
|
|
|
Change the shape with the ratio classes, for example `ratio-21x9` for a wide map or `ratio-1x1` for a square one.
|
|
|
|
### Map styles
|
|
|
|
The map uses a light gray style in light mode and a dark one in dark mode. Tabler softens their colors, so markers and data stand out. Set another style with `data-bs-map-style`: the name of an OpenFreeMap style, or the URL of any MapLibre style.
|
|
|
|
| Style | Result |
|
|
| --- | --- |
|
|
| `positron` | Light gray map. The default in light mode. |
|
|
| `dark` | Dark gray map. The default in dark mode. |
|
|
| `liberty` | Street map in color. |
|
|
| `bright` | Street map with stronger colors. |
|
|
| `fiord` | Dark blue map. |
|
|
|
|
<Example flush>
|
|
<Map rounded={false} mapId="light" />
|
|
</Example>
|
|
|
|
`data-bs-map-style-dark` sets the style for dark mode. Styles from other providers work the same way, as long as they follow the MapLibre style format. Check the terms of each provider, as many of them need an API key or a paid plan.
|
|
|
|
### Dark mode
|
|
|
|
The map follows the nearest `data-bs-theme` and changes its style when the theme changes. Markers and popups stay on the map. To keep one look in both modes, set `data-bs-map-theme` to `light` or `dark`.
|
|
|
|
### Markers
|
|
|
|
Each child element with `data-bs-lng` and `data-bs-lat` becomes a marker. Use the `.map-marker` class to draw it: a circle in the primary color that can hold an icon, a short count or an avatar. Put a `.map-marker-label` element inside the marker for a short text under it.
|
|
|
|
Add `.map-marker-dot` for a small dot, or `.map-marker-pin` for a pin that points down. A pin needs `data-bs-anchor="bottom"`, so its tip sits on the place. Change the color with `.map-marker-{color}`.
|
|
|
|
<Example flush>
|
|
<Map rounded={false} mapId="marker-styles" title="Marker styles" center={[52.522, 13.405]} zoom={11}>
|
|
<span class="map-marker map-marker-dot" data-bs-lng="13.345" data-bs-lat="52.54"></span>
|
|
<span class="map-marker map-marker-dot map-marker-red" data-bs-lng="13.39" data-bs-lat="52.545"></span>
|
|
<span class="map-marker" data-bs-lng="13.445" data-bs-lat="52.538"><Icon name="building-store" /><span class="map-marker-label">Store</span></span>
|
|
<span class="map-marker map-marker-green" data-bs-lng="13.35" data-bs-lat="52.512">12</span>
|
|
<span class="map-marker map-marker-pin" data-bs-lng="13.405" data-bs-lat="52.522" data-bs-anchor="bottom"><Icon name="home" /></span>
|
|
<span class="map-marker map-marker-pin map-marker-red" data-bs-lng="13.47" data-bs-lat="52.518" data-bs-anchor="bottom"><Icon name="star" /></span>
|
|
<span class="map-marker" data-bs-lng="13.43" data-bs-lat="52.508"><span class="avatar avatar-sm" style="background-image: url(/static/avatars/000m.jpg)"></span></span>
|
|
</Map>
|
|
</Example>
|
|
|
|
Add `data-bs-draggable="true"` to let people move a marker, for example to pick an address. Add `.map-marker-pulse` for a ring that grows out of the marker, for a live position or a place that needs attention.
|
|
|
|
HTML markers work well for up to a few hundred places. For more, use [clusters](#clusters).
|
|
|
|
### Popups
|
|
|
|
Add `data-bs-popup` to a marker for a short text that opens when the marker is clicked. Tabler draws the popup as a small bordered panel with no arrow, and fades it in.
|
|
|
|
<Example flush>
|
|
<Map rounded={false} mapId="markers" />
|
|
</Example>
|
|
|
|
For richer content, put a `<template>` inside the marker. Its content becomes the popup. Add the `map-popup-card` class to the template for a popup with an image: it removes the padding and sets a fixed width, so the image reaches the edges. `data-bs-close-button="true"` on the template adds a close button, and `data-bs-popup-open="true"` on the marker opens the popup from the start.
|
|
|
|
<Example flush>
|
|
<Map rounded={false} mapId="popup-card" title="Popup card" center={[52.546, 13.4]} zoom={11} ratio="4x3">
|
|
<span class="map-marker map-marker-pin" data-bs-lng="13.4019" data-bs-lat="52.5169" data-bs-anchor="bottom" data-bs-popup-open="true">
|
|
<Icon name="map-pin" />
|
|
<template class="map-popup-card" data-bs-close-button="true">
|
|
<div class="img-responsive img-responsive-21x9" style="background-image: url(/static/photos/a-visit-to-the-bookstore.jpg)"></div>
|
|
<div class="p-3">
|
|
<div class="fw-medium">Museum Island</div>
|
|
<div class="text-secondary mb-3">Five museums on the Spree, open daily.</div>
|
|
<a href="#" class="btn btn-primary btn-sm">View details</a>
|
|
</div>
|
|
</template>
|
|
</span>
|
|
</Map>
|
|
</Example>
|
|
|
|
### Controls
|
|
|
|
Add `data-bs-controls="true"` for zoom buttons in the top corner of the map. They are regular Tabler buttons, so they follow the theme. For more buttons, pass a list of names:
|
|
|
|
| Name | Buttons |
|
|
| --- | --- |
|
|
| `zoom` | Zoom in and zoom out. |
|
|
| `north` | Turns the map back to north up. |
|
|
| `locate` | Moves the map to the position of the device. The browser asks for permission first. |
|
|
| `fullscreen` | Shows the map on the whole screen, and back. |
|
|
|
|
```html
|
|
<div class="w-100 h-100" data-bs-toggle="map" data-bs-center="13.405,52.52" data-bs-zoom="12" data-bs-controls="zoom,north,locate,fullscreen"></div>
|
|
```
|
|
|
|
The buttons have English names for screen readers. Change them with the `labels` option, by action:
|
|
|
|
```html
|
|
<div class="w-100 h-100" data-bs-toggle="map" data-bs-controls="true" data-bs-labels='{"zoom-in": "Przybliż", "zoom-out": "Oddal"}'></div>
|
|
```
|
|
|
|
You can also write the buttons yourself. Any button with `data-bs-map-action` works on the map it is inside, or on the map its `data-bs-target` points at. The actions are `zoom-in`, `zoom-out`, `north`, `locate` and `fullscreen`. A button with no content gets a matching icon; put the buttons in a `.map-controls` element to place them in the top corner.
|
|
|
|
```html
|
|
<div class="w-100 h-100" id="map-office" data-bs-toggle="map" data-bs-center="13.405,52.52" data-bs-zoom="12">
|
|
<div class="map-controls">
|
|
<button type="button" class="btn btn-sm btn-icon" data-bs-map-action="zoom-in" aria-label="Zoom in"></button>
|
|
<button type="button" class="btn btn-sm btn-icon" data-bs-map-action="zoom-out" aria-label="Zoom out"></button>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="button" class="btn" data-bs-map-action="locate" data-bs-target="#map-office">Find me</button>
|
|
```
|
|
|
|
### Route
|
|
|
|
Draw a path with `data-bs-route`: a list of `[longitude, latitude]` points. The line takes the primary color and is drawn again after a theme change. Mark its ends with markers.
|
|
|
|
```html
|
|
<div class="w-100 h-100" data-bs-toggle="map" data-bs-center="13.4046,52.513" data-bs-zoom="11.6" data-bs-route="[[13.3695,52.5251],[13.4132,52.5219],[13.4397,52.505]]">
|
|
<span class="map-marker map-marker-dot" data-bs-lng="13.3695" data-bs-lat="52.5251"></span>
|
|
<span class="map-marker map-marker-pin" data-bs-lng="13.4397" data-bs-lat="52.505" data-bs-anchor="bottom"></span>
|
|
</div>
|
|
```
|
|
|
|
To show how far along a path something is, add `data-bs-route-progress` with the index of the point reached so far, counted from 0. The path up to that point stays solid and the rest is dashed.
|
|
|
|
```html
|
|
<div class="w-100 h-100" data-bs-toggle="map" data-bs-route="[[13.3695,52.5251],[13.4132,52.5219],[13.4397,52.505]]" data-bs-route-progress="1"></div>
|
|
```
|
|
|
|
### Areas and other shapes
|
|
|
|
`data-bs-geojson` draws [GeoJSON](https://geojson.org/) over the map: areas with a light fill and an outline, lines, and points as small circles. Pass the GeoJSON itself, or the URL of a file.
|
|
|
|
```html
|
|
<div class="w-100 h-100" data-bs-toggle="map" data-bs-center="13.405,52.519" data-bs-zoom="11" data-bs-geojson="/data/delivery-zone.geojson"></div>
|
|
```
|
|
|
|
Both use the primary color. Set `data-bs-layer-color` to another color, for example `--tblr-red` or `#d63939`.
|
|
|
|
### Globe
|
|
|
|
Add `data-bs-globe="true"` to draw the map as a 3D globe. Use a low zoom, between 1 and 2, to see the whole globe. People can turn it by dragging.
|
|
|
|
```html
|
|
<div class="w-100 h-100" data-bs-toggle="map" data-bs-center="62,32" data-bs-zoom="1.3" data-bs-globe="true"></div>
|
|
```
|
|
|
|
### Content over the map
|
|
|
|
To put buttons or a small card over the map, place them next to the map element and position them with the utility classes:
|
|
|
|
```html
|
|
<div class="ratio ratio-16x9">
|
|
<div>
|
|
<div class="w-100 h-100 rounded" data-bs-toggle="map" data-bs-center="13.405,52.52" data-bs-zoom="10"></div>
|
|
<div class="btn-list position-absolute top-0 start-0 m-2">
|
|
<button type="button" class="btn btn-sm btn-pill btn-primary">Berlin</button>
|
|
<button type="button" class="btn btn-sm btn-pill">Tokyo</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### Attribution
|
|
|
|
MapLibre adds the credit for the map data in the corner of the map. OpenFreeMap requires it, so do not hide it.
|
|
|
|
## Layers
|
|
|
|
The route and GeoJSON options cover simple shapes. For anything more, such as clusters, dashed lines or circles sized by a value, take the MapLibre map from the component and use its own API. The `map` property is the [MapLibre map](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/).
|
|
|
|
A layer reads its data from a GeoJSON source. Layers are part of the map style, so add them in `onStyle()`. It runs when the style has loaded, and again each time the style changes, for example on a theme change.
|
|
|
|
```js
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
const view = tabler.MapView.getInstance('#map-stores')
|
|
|
|
view.onStyle(function (map) {
|
|
map.addSource('stores', { type: 'geojson', data: stores })
|
|
map.addLayer({
|
|
id: 'stores',
|
|
type: 'circle',
|
|
source: 'stores',
|
|
paint: { 'circle-color': tabler.MapView.color('--tblr-primary'), 'circle-radius': 6 },
|
|
})
|
|
})
|
|
|
|
// Runs once the map exists: the library may load a moment after the page
|
|
view.ready(function (map) {
|
|
map.on('click', 'stores', function (event) {
|
|
console.log(event.features[0].properties)
|
|
})
|
|
})
|
|
})
|
|
```
|
|
|
|
For an area, use a `Polygon` geometry with a `fill` layer, and a `line` layer for its outline.
|
|
|
|
### Theme colors in layers
|
|
|
|
MapLibre draws layers on a canvas, so a CSS variable does not work in `paint`, and neither do the `oklch()` values Tabler colors are written in. `MapView.color()` turns a variable, or any CSS color, into a plain `rgb()` color. Call it inside `onStyle()`, so the colors are read again after a theme change.
|
|
|
|
```js
|
|
tabler.MapView.color('--tblr-primary') // 'rgb(6, 111, 209)'
|
|
```
|
|
|
|
### Clusters
|
|
|
|
Put many points in a GeoJSON source with `cluster: true`. MapLibre then groups nearby points and splits the groups as you zoom in.
|
|
|
|
```js
|
|
map.addSource('stores', { type: 'geojson', data: stores, cluster: true, clusterRadius: 40 })
|
|
|
|
// Circle for each cluster, larger for more points
|
|
map.addLayer({
|
|
id: 'clusters',
|
|
type: 'circle',
|
|
source: 'stores',
|
|
filter: ['has', 'point_count'],
|
|
paint: { 'circle-color': tabler.MapView.color('--tblr-primary'), 'circle-radius': ['step', ['get', 'point_count'], 14, 10, 18, 25, 24] },
|
|
})
|
|
|
|
// Number of points in the cluster
|
|
map.addLayer({
|
|
id: 'cluster-count',
|
|
type: 'symbol',
|
|
source: 'stores',
|
|
filter: ['has', 'point_count'],
|
|
layout: { 'text-field': ['get', 'point_count_abbreviated'], 'text-font': ['Noto Sans Regular'], 'text-size': 12 },
|
|
paint: { 'text-color': '#ffffff' },
|
|
})
|
|
```
|
|
|
|
`text-font` has to be a font the map style provides. All OpenFreeMap styles have `Noto Sans Regular`.
|
|
|
|
### Tooltips
|
|
|
|
For a short label on hover, add the `map-tooltip` class to a MapLibre popup. It turns the popup into a small dark label.
|
|
|
|
```js
|
|
const tooltip = new maplibregl.Popup({ closeButton: false, offset: 12, className: 'map-tooltip' })
|
|
|
|
map.on('mousemove', 'cities', function (event) {
|
|
tooltip.setLngLat(event.lngLat).setText(event.features[0].properties.name).addTo(map)
|
|
})
|
|
map.on('mouseleave', 'cities', function () {
|
|
tooltip.remove()
|
|
})
|
|
```
|
|
|
|
Live versions of a route, an area, clusters, a bubble map, a globe with arcs and a city switcher are on the [maps preview page](https://preview.tabler.io/maps.html). The [map blocks page](https://preview.tabler.io/map-blocks.html) has larger examples: a store locator, a list of homes with prices on the map, a delivery tracker, an address form with a draggable pin, a server status map, a map of live users and a vector map of users by country.
|
|
|
|
## JavaScript
|
|
|
|
### Via data attributes
|
|
|
|
An element with `data-bs-toggle="map"` becomes a map when the `tabler-map.js` script loads:
|
|
|
|
<CodeDocs name="map-init" file="core/js/src/map.ts" />
|
|
|
|
### Via JavaScript
|
|
|
|
Create a map from your own code with the same options:
|
|
|
|
```js
|
|
const view = new tabler.MapView('#map', {
|
|
center: [13.405, 52.52],
|
|
zoom: 12,
|
|
controls: true,
|
|
})
|
|
```
|
|
|
|
### Options
|
|
|
|
Pass options as data attributes, with the name written in kebab case after `data-bs-`, or in the config object.
|
|
|
|
| Name | Type | Default | Description |
|
|
| --- | --- | --- | --- |
|
|
| `library` | string | MapLibre in `dist/libs` | URL of the MapLibre GL JS module, loaded when `window.maplibregl` is not set. A relative URL starts at the Tabler script. `null` turns the loading off. |
|
|
| `center` | array, string | `[0, 0]` | Center of the map: `[longitude, latitude]`, or the two numbers in a string such as `"13.405,52.52"`. |
|
|
| `zoom` | number | `1` | Zoom level, from 0 for the whole world to about 20 for a building. |
|
|
| `mapStyle` | string | `'positron'` | Name of an OpenFreeMap style, or the URL of a MapLibre style. |
|
|
| `mapStyleDark` | string | `'dark'` | Style used in dark mode. Set `null` to keep `mapStyle` in both modes. |
|
|
| `mapTheme` | string | `'auto'` | `'auto'` follows the nearest `[data-bs-theme]`; `'light'` or `'dark'` keeps one look. |
|
|
| `palette` | boolean | `true` | Softer colors for the `positron` and `dark` styles. Other styles are left as they are. |
|
|
| `controls` | boolean, string | `false` | Buttons over the map: `true` for zoom, or a list of `zoom`, `north`, `locate` and `fullscreen`. |
|
|
| `labels` | object | `{}` | Names of the buttons for screen readers, by action: `zoom-in`, `zoom-out`, `north`, `locate`, `fullscreen`. |
|
|
| `route` | array | `null` | A path drawn as a line: a list of `[longitude, latitude]` points. |
|
|
| `routeProgress` | number | `null` | Index of the route point reached so far. The path up to it is solid, the rest is dashed. |
|
|
| `geojson` | object, string | `null` | GeoJSON drawn over the map: an object, or the URL of a file. |
|
|
| `layerColor` | string | `'--tblr-primary'` | Color of the route and the GeoJSON shapes: a CSS color or a custom property. |
|
|
| `globe` | boolean | `false` | Draw the map as a 3D globe. |
|
|
| `mapOptions` | object | `{}` | Any [MapLibre map option](https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MapOptions/), passed straight through. |
|
|
|
|
A marker element takes these attributes:
|
|
|
|
| Attribute | Description |
|
|
| --- | --- |
|
|
| `data-bs-lng`, `data-bs-lat` | Position of the marker. Both are needed. |
|
|
| `data-bs-anchor` | Part of the marker that sits on the position: `center` by default, `bottom` for a pin. |
|
|
| `data-bs-draggable` | Set `true` to let people move the marker. |
|
|
| `data-bs-popup` | Text of a popup that opens when the marker is clicked. |
|
|
| `data-bs-popup-open` | Set `true` to open the popup from the start. |
|
|
|
|
### Methods
|
|
|
|
| Name | Description |
|
|
| --- | --- |
|
|
| `map` | The MapLibre map, for layers, events and everything else the options do not cover. `null` until the library has loaded. |
|
|
| `ready(callback)` | Runs the callback with the MapLibre map once it exists. |
|
|
| `onStyle(callback)` | Runs the callback with the map each time a style loads. The place to add layers. |
|
|
| `markers` | The MapLibre markers made from the child elements, in document order. |
|
|
| `zoomIn()`, `zoomOut()` | Zooms the map by one level. |
|
|
| `resetNorth()` | Turns the map back to north up, with no tilt. |
|
|
| `locate()` | Moves the map to the position of the device. |
|
|
| `toggleFullscreen()` | Shows the map on the whole screen, and back. |
|
|
| `dispose()` | Removes the map, its markers and the instance. |
|
|
| `MapView.color(value)` | Turns a CSS variable or color into a plain `rgb()` color for a map layer. |
|
|
| `MapView.getInstance(element)` | Returns the instance for an element, or `null`. |
|
|
| `MapView.getOrCreateInstance(element)` | Returns the instance, or creates one. |
|
|
|
|
### Events
|
|
|
|
| Event | Description |
|
|
| --- | --- |
|
|
| `load.bs.map` | Fires on the map element once the map has loaded its style and first tiles. |
|
|
|
|
## Using Mapbox GL JS
|
|
|
|
MapLibre GL JS started as a fork of Mapbox GL JS, so the two share most of their API. The map component works with MapLibre only. To use Mapbox, write the map script yourself: load its script and stylesheet, set `mapboxgl.accessToken` to your public token, and use a `mapbox://` style URL.
|
|
|
|
## Accessibility
|
|
|
|
- The map is a picture for most users. Give the same information as text near it, for example the address or a list of places.
|
|
- Add `role="region"` and an `aria-label` to the map element, so screen reader users know what the map shows.
|
|
- Do not put an action only on a marker. Repeat it in a normal link or button below the map.
|
|
- Keep a visible fallback when the script fails to load, for example the address in text.
|