Skip to content

Dotted Map

A flat world map drawn as dots on a canvas, with optional markers and arcs that pulse between marker pairs. Still when off-screen, paused or for reduced motion.

Motionv0.1.0experimentalWCAG 2.2 AAView spec

Optional, and kept apart.

Nothing here is included in a project unless you add it, and the plain component it builds on is unchanged. This effect is decoration: it is hidden from screen readers, stands still for visitors who prefer reduced motion, and can be paused. Use Still on a preview to see that version.

A map of the world drawn in small dots.

import { DottedMap } from "@rdloom/react";

export default function DottedMapBasicExample() {
  return (
    <div className="flex w-full justify-center">
      <div className="w-full max-w-xl">
        <DottedMap label="World map" description="A map of the world drawn in small dots." />
      </div>
    </div>
  );
}

Installation

npx rdloom add dotted-map

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.

It also adds rdloom-motion.css next to your tokens and imports it from the tokens file, once: no setup, and no inline styles. The plain components you already use are untouched.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/dotted-map.json

Works, but without upgrade tracking, and you import the motion CSS yourself.

Usage

import { DottedMap } from "@rdloom/react";

<div className="flex w-full justify-center">
  <div className="w-full max-w-xl">
    <DottedMap label="World map" description="A map of the world drawn in small dots." />
  </div>
</div>

Connections

Four cities linked in a chain by pulsing arcs.

  • San Francisco
  • London
  • Mumbai
  • Tokyo
  • San Francisco to London
  • London to Mumbai
  • Mumbai to Tokyo
import { DottedMap, type WorldConnection, type WorldMarker } from "@rdloom/react";

const places: WorldMarker[] = [
  { lat: 37.8, lng: -122.4, label: "San Francisco" },
  { lat: 51.5, lng: -0.1, label: "London" },
  { lat: 19.1, lng: 72.9, label: "Mumbai" },
  { lat: 35.7, lng: 139.7, label: "Tokyo" },
];

const routes: WorldConnection[] = [
  { from: "San Francisco", to: "London" },
  { from: "London", to: "Mumbai" },
  { from: "Mumbai", to: "Tokyo" },
];

export default function DottedMapConnectionsExample() {
  return (
    <div className="flex w-full justify-center">
      <div className="w-full max-w-xl">
        <DottedMap
          label="Data centre links"
          description="Four cities linked in a chain by pulsing arcs."
          markers={places}
          connections={routes}
        />
      </div>
    </div>
  );
}

Still

Two cities joined by an arc.

  • Sao Paulo
  • Paris
  • Sao Paulo to Paris
import { useState } from "react";
import { Button, DottedMap } from "@rdloom/react";

// Defined once, outside the component, so a re-render does not restart the map.
const places = [
  { lat: -23.5, lng: -46.6, label: "Sao Paulo" },
  { lat: 48.9, lng: 2.4, label: "Paris" },
];
const routes = [{ from: "Sao Paulo", to: "Paris" }];

export default function DottedMapStillExample() {
  const [paused, setPaused] = useState(false);
  return (
    <div className="flex w-full flex-col items-center gap-3">
      <div className="w-full max-w-xl">
        <DottedMap
          label="Routes"
          description="Two cities joined by an arc."
          markers={places}
          connections={routes}
          dotSpacing={10}
          isPaused={paused}
        />
      </div>
      <Button variant="secondary" onPress={() => setPaused((p) => !p)}>
        {paused ? "Play the arcs" : "Hold the map still"}
      </Button>
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
label

Accessible name of the whole graphic.

string"World map"
description

A sentence for screen readers that says what the graphic shows. It is visually hidden.

stringnone
markers

Places to mark as { lat, lng, label }. Each one is also listed, with its label, for screen readers.

WorldMarker[]none
dotColor

Colour of the land dots, any CSS colour. Defaults to the muted text colour.

stringnone
markerColor

Colour of the markers, any CSS colour. Defaults to the primary action colour.

stringnone
isPaused

Stops the movement and shows one still frame.

booleanfalse
connections

Arcs drawn between two markers as { from, to }, each the label of a marker. A pulse travels along each arc. Pairs whose labels match no marker are skipped. They are also listed for screen readers.

WorldConnection[]none
dotSpacing

Distance between dots in pixels. Larger is coarser and cheaper.

number8

Accessibility

Role group, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

Not interactive.

Screen readers announce

  • The canvas is hidden from assistive technology
  • The label, description, a list of the places and a list of the connections are read as text

What your code must do

  • The canvas is decoration and aria-hidden; the root is a labelled group with a visually hidden description and a real list of the places
  • Stands still for visitors who ask for less motion (prefers-reduced-motion), inside an rdm-still container and with isPaused; one still frame is drawn
  • Moving for more than five seconds, so it can be paused with isPaused or the rdm-still container (WCAG 2.2.2)
  • Does not flash: nothing changes more than three times in a second (WCAG 2.3.1)
  • Paused while off-screen, and its animation frame is cancelled when it unmounts
  • Hidden in forced-colors (high contrast) mode, where the place list is still there for assistive technology
  • Connections are listed as text, so the routes are not shown by the animation alone

Guidelines

Use it when

  • Showing where offices, regions or routes are in a hero or section
  • A quiet decorative map with a few named places

Avoid it when

  • Precise geography or street level detail
  • Many dozens of arcs at once

Don't

  • Leaving out label and description
  • Arcs whose meaning is only in the animation

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-text-muted
  • --rd-color-action-primary