<script setup lang="ts">
import * as maplibre from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import { MapView } from "@dicehub/kappa/components/map-view";
import type { MapOptions } from "maplibre-gl";
const options = {
style: "https://tiles.openfreemap.org/styles/liberty",
center: [13.405, 52.52],
zoom: 10,
} satisfies Omit<MapOptions, "container">;
</script>
<template>
<MapView
:engine="maplibre"
:options="options"
aria-label="Map of Berlin"
/>
</template>Installation
MapView uses MapLibre GL JS as an optional peer dependency. Import the component from its isolated path and load MapLibre's required stylesheet once in the application.
pnpm add @dicehub/kappa maplibre-glUsage
Pass the imported MapLibre namespace and standard MapLibre options. Kappa supplies the container, resize and disposal lifecycle, controls, markers, states, and accessibility boundary.
<script setup lang="ts">
import * as maplibre from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import { MapView } from "@dicehub/kappa/components/map-view";
import type { MapOptions } from "maplibre-gl";
const options = {
style: "https://tiles.openfreemap.org/styles/liberty",
center: [13.405, 52.52],
zoom: 10,
} satisfies Omit<MapOptions, "container">;
</script>
<template>
<MapView
:engine="maplibre"
:options="options"
aria-label="Map of Berlin"
/>
</template>Engine Boundary
Kappa does not bundle a tile service, style, or access token. Select a provider that meets the application's licensing, privacy, attribution, and availability requirements. The examples use the public OpenFreeMap Liberty style; production applications should supply their own style URL or style object.
- Use
markersfor normal labeled locations and application selection. - Use
readyorgetMap()for GeoJSON, vector sources, routes, polygons, terrain, and custom layers. - Replace the options object or change
revisiononly when the map must be recreated. - Keep attribution enabled when the selected data or tile provider requires it.
Examples
Find an Address
Type at least three characters to get address suggestions, then select one to move the map. This example uses the public Photon service.
<script setup lang="ts">
import { computed, ref } from "vue";
import * as maplibre from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import {
Autocomplete,
type AutocompleteInputValueChangeDetails,
type AutocompleteValueChangeDetails,
} from "@dicehub/kappa/components/autocomplete";
import { MapView, type MapViewMarker } from "@dicehub/kappa/components/map-view";
import type { Map as MapLibreMap, MapOptions } from "maplibre-gl";
interface AddressResult extends MapViewMarker {
id: string;
}
const query = ref("");
const results = ref<AddressResult[]>([]);
const selected = ref<AddressResult>();
const searchOpen = ref(false);
const markers = computed(() => selected.value ? [selected.value] : []);
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
let searchController: AbortController | undefined;
const options = {
style: "https://tiles.openfreemap.org/styles/liberty",
center: [12.1, 52.45],
zoom: 5.6,
} satisfies Omit<MapOptions, "container">;
async function searchAddresses(value: string) {
searchController?.abort();
const controller = new AbortController();
searchController = controller;
try {
const url = new URL("https://photon.komoot.io/api/");
url.searchParams.set("q", value);
url.searchParams.set("limit", "5");
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error("Address search returned " + response.status);
const data = await response.json() as {
features: Array<{
geometry: { coordinates: [number, number] };
properties: { name?: string; city?: string; osm_id?: number };
}>;
};
results.value = data.features.map((feature, index) => ({
id: String(feature.properties.osm_id ?? index),
label: feature.properties.name ?? feature.properties.city ?? "Unnamed location",
coordinates: feature.geometry.coordinates,
}));
} catch {
if (!controller.signal.aborted) results.value = [];
}
}
function suggestAddresses(details: AutocompleteInputValueChangeDetails) {
query.value = details.inputValue;
if (details.reason === "item-select") return;
searchOpen.value = details.inputValue.trim().length > 0;
if (debounceTimer) clearTimeout(debounceTimer);
searchController?.abort();
results.value = [];
if (details.inputValue.trim().length < 3) return;
debounceTimer = setTimeout(() => searchAddresses(details.inputValue), 320);
}
function selectAddress(
map: MapLibreMap,
details: AutocompleteValueChangeDetails<AddressResult>,
) {
selected.value = details.items[0];
if (selected.value) {
searchOpen.value = false;
map.flyTo({ center: selected.value.coordinates, zoom: 13 });
}
}
</script>
<template>
<MapView
:engine="maplibre"
:options="options"
:markers="markers"
aria-label="Address search map"
>
<template #overlay="{ map }">
<Autocomplete
v-model:input-value="query"
v-model:open="searchOpen"
:filter="false"
:input-attrs="{ 'aria-label': 'Find an address' }"
:items="results"
:item-to-string="(item: AddressResult) => item.label"
:item-to-value="(item: AddressResult) => item.id"
input-behavior="autohighlight"
placeholder="Find an address..."
@input-value-change="suggestAddresses"
@value-change="selectAddress(map, $event)"
/>
</template>
</MapView>
</template>Locations
Keep location data declarative while Kappa owns marker focus, selection, safe text popups, and visual tones.
<script setup lang="ts">
import { ref } from "vue";
import * as maplibre from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import {
MapView,
type MapViewMarker,
} from "@dicehub/kappa/components/map-view";
import type { MapOptions } from "maplibre-gl";
const selectedId = ref<string | number>();
const options = {
style: "https://tiles.openfreemap.org/styles/liberty",
center: [12.1, 52.45],
zoom: 5.6,
} satisfies Omit<MapOptions, "container">;
const markers: MapViewMarker[] = [
{
id: "berlin",
coordinates: [13.405, 52.52],
label: "Berlin",
description: "Primary engineering office",
tone: "accent",
},
{
id: "hamburg",
coordinates: [9.9937, 53.5511],
label: "Hamburg",
description: "Test facility",
tone: "success",
},
];
</script>
<template>
<MapView
:engine="maplibre"
:options="options"
:markers="markers"
:selected-marker-id="selectedId"
aria-label="Engineering locations"
@marker-click="selectedId = $event.id"
/>
</template>Routes and Areas
Use the ready event for advanced MapLibre sources and layers. Kappa keeps the frame and controls stable.
<script setup lang="ts">
import * as maplibre from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import { MapView } from "@dicehub/kappa/components/map-view";
import type { Map as MapLibreMap, MapOptions } from "maplibre-gl";
const options = {
style: "https://tiles.openfreemap.org/styles/liberty",
center: [11.85, 52.35],
zoom: 5.35,
} satisfies Omit<MapOptions, "container">;
const operationsGeoJson = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
type: "Polygon",
coordinates: [[
[8.35, 54.05], [10.45, 54.55],
[14.55, 52.25], [12.7, 50.65],
[9.15, 51.2], [8.35, 54.05],
]],
},
},
],
} satisfies Parameters<MapLibreMap["addSource"]>[1];
function addOperationsLayer(map: MapLibreMap) {
map.addSource("operations", {
type: "geojson",
data: operationsGeoJson,
});
map.addLayer({
id: "operations-area",
type: "fill",
source: "operations",
paint: {
"fill-color": "#247ab7",
"fill-opacity": 0.12,
},
});
}
</script>
<template>
<MapView
:engine="maplibre"
:options="options"
aria-label="Operations area and route"
@ready="addOperationsLayer"
/>
</template>Data States
Reserve one stable map region while location data or the map service changes state.
<script setup lang="ts">
import * as maplibre from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import { MapView } from "@dicehub/kappa/components/map-view";
import type { MapOptions } from "maplibre-gl";
const options = {
style: "https://tiles.openfreemap.org/styles/liberty",
center: [13.405, 52.52],
zoom: 10,
} satisfies Omit<MapOptions, "container">;
</script>
<template>
<MapView :engine="maplibre" :options="options" aria-label="Map" loading />
<MapView :engine="maplibre" :options="options" aria-label="Map" empty />
<MapView
:engine="maplibre"
:options="options"
aria-label="Map"
error="The location service is unavailable."
/>
</template>Accessibility
- Give every map a specific
ariaLabeland a concise text summary inariaDescription. - Each Kappa marker is a keyboard-focusable button. Marker labels become accessible names.
- Address suggestions use Kappa Autocomplete. Arrow keys move through results, Enter selects one, and Escape closes the list.
- Do not make the map the only way to reach essential locations or values. Provide a linked list or table for critical data.
- Translate the control and state labels when the application supports more than one language.
Security and Data
- Kappa marker labels and descriptions use DOM text nodes. They do not accept HTML.
- Treat style URLs, tile endpoints, GeoJSON, and custom MapLibre callbacks as application-controlled input.
- Do not expose private coordinates to a third-party tile service without an approved data policy.
- Address queries may contain personal data. Review the selected geocoding provider's privacy policy, limits, and production terms.
- Restrict remote map hosts with the application's Content Security Policy.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
ariaLabel | string | — | Required accessible name for the interactive map canvas. |
ariaDescription | string | Keyboard instructions | Instructions or a short data summary linked to the canvas. |
engine | MapViewEngine | — | Imported MapLibre namespace. Keeps the engine outside the Kappa bundle. |
options | MapViewOptions | — | MapLibre initialization options except container. |
markers | readonly MapViewMarker[] | [] | Safe text markers with optional descriptions, tones, and popups. |
selectedMarkerId | string | number | — | Marks one location as selected without owning application state. |
height | string | number | "28rem" | Map height. Numbers are pixels. |
showControls | boolean | true | Shows Kappa zoom and reset controls. |
loading / empty / error | boolean / boolean / string | false / false / — | Explicit asynchronous and data states. |
revision | number | string | 0 | Recreates the map after in-place option changes. |
Events
| Event | Payload | Description |
|---|---|---|
ready | MapLibreMap | Fires after the map style loads. Add custom sources and layers here. |
markerClick | MapViewMarker | Fires when a Kappa marker is selected. |
moveEnd | MapViewState | Returns center, zoom, bearing, and pitch after movement. |
error | unknown | Reports construction and MapLibre runtime errors. |
Exposed Methods
| Method | Description |
|---|---|
getMap() | Returns the MapLibre map instance for advanced engine operations. |
resize() | Recalculates the canvas size. ResizeObserver calls this automatically. |
reset() | Returns to the initial camera position. |
zoomIn() / zoomOut() | Changes zoom with the engine animation. |
Import the component and public types from @dicehub/kappa/components/map-view. See the MapLibre GL JS documentation for engine options, sources, and layers.