Backgrounds

Dotted Map

A flat world map traced in dots, with markers that can pulse.

Dotted Map samples the landmass into a cloud of dots and draws each one as a small circle. Because the map is a dot field rather than a coastline path, it scales down cleanly and reads as a diagram instead of a picture.

Rows are offset by half a step by default, so the dots interlock like a weave instead of stacking into visible columns. Markers are placed by latitude and longitude, share that same offset, and can each carry their own size, a pulse override and any extra fields you want to read back in the marker slot.

The landmass comes from the optional svg-dotted-map package. It is loaded on mount and never bundled with this library:

pnpm add svg-dotted-map

Without it the component renders a short install hint instead of breaking the page.

Usage

<template>
  <div class="relative h-64 w-full overflow-hidden rounded-xl border border-default bg-default">
    <MDottedMap
      :markers="markers"
      :pulse="true"
      :dot-radius="0.22"
      class="p-6"
    >
      <template #marker="{ marker, x, y, r }">
        <text
          :x="x + r * 2.2"
          :y="y + r * 0.4"
          :font-size="r * 1.2"
          fill="currentColor"
          class="font-medium"
        >
          {{ String(marker.label) }}
        </text>
      </template>
    </MDottedMap>
  </div>
</template>

<script setup lang="ts">
const markers = [
  { lat: 37.5665, lng: 126.978, size: 2.4, label: 'Seoul' },
  { lat: 40.7128, lng: -74.006, size: 2.4, label: 'New York' },
]
</script>

Examples

Customize

Customize

Aria LabelWorld map
Dot Color
Dot Radius0.22
Height75
Map Samples5000
Marker Color
Pulse
Stagger
Width150

Props

Prop Type Default Description
widthnumber150Width of the map, in viewBox units. The rendered element still fills its parent.
heightnumber75Height of the map, in viewBox units.
mapSamplesnumber5000Points sampled from the landmass. More samples give a denser map and a slower first paint.
markersobject[][]Points to pin, each with lat and lng, plus optional size and pulse. Any other field is passed through to the marker slot.
dotColorstring'currentColor'Colour of the map dots.
markerColorstring'#FF6900'Colour of the markers and their pulse rings.
dotRadiusnumber0.2Radius of a map dot, in viewBox units.
staggerbooleantrueOffset every other row by half a step so the dots interlock.
pulsebooleanfalsePulse every marker. With pulse off, only markers that set pulse: true animate.
ariaLabelstring'World map'Accessible description of the map.
classstring''CSS classes for the wrapper.

Slots

Slot Props Description
marker-Draws over each marker, inside the marker's own <g>. Receives marker, index, x, y and r, all in viewBox units.
fallback-Replaces the install hint shown when svg-dotted-map is not present.

UI

This component's theme exposes the slots below. Override any of them per instance with the ui prop, or app-wide under ui.dottedMap in app.config.ts.

Prop Type Default Description
uiobject-Per-slot classes merged over the theme defaults.
Slot Props Description
root-size-full
map-size-full text-muted
fallback-flex h-full w-full flex-col items-center justify-center gap-2 p-6 text-center text-sm text-muted

Accessibility

The <svg> is exposed as an image with aria-label, so the map is announced as one unit instead of as hundreds of circles. Pulse rings are skipped entirely when the visitor has asked for reduced motion, since SMIL cannot be switched off by a media query the way CSS animations can.

Copyright © 2026