Route Map
Draw a route on a tile-free map, with delayed stretches, a proposed reroute and cards anchored to stops.
import { RouteMap, RouteMapRoute, RouteMapStop, RouteMapTerrain } from '@youj/react'
Installation & usage
npm i @youj/reactFor an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.
Use a route map where the question is where a load, a courier or a crew is on its way, which stretch is late and what the alternative would be. There are no tiles: land, water, parks and roads are abstract shapes in the theme's tones, so the routes are the loudest thing on it and nothing loads from a map provider. For a world view of where traffic comes from, use a Dot Map; for the same stops as a line of steps, a Stepper with a detour.
import { RouteMap, type RouteMapRoute, type RouteMapStop } from '@youj/react'
const stops: RouteMapStop[] = [
{ id: 'depot', name: 'Harbor depot', kind: 'origin', x: 12, y: 72, detail: 'Departed 09:14' },
{ id: 'truck', name: 'Load 4821', kind: 'current', x: 43, y: 45, card: <p>+47 min</p> },
{ id: 'summit', name: 'Summit DC', kind: 'destination', x: 86, y: 18, detail: 'ETA 15:49' },
]
const routes: RouteMapRoute[] = [
{ id: 'driven', status: 'done', path: ['depot', [24, 60], 'truck'] },
{ id: 'ahead', status: 'delayed', path: ['truck', 'summit'] },
{ id: 'reroute', status: 'proposed', label: 'Proposed via river road', path: ['truck', [66, 57], 'summit'] },
]
<RouteMap stops={stops} routes={routes} />Stops sit at x and y, from 0 to 100 across and down the map. Their kind picks the marker: origin, stop, destination, or current for where the vehicle is now (it pulses). Any stop takes your own icon: the current one wears it on the accent, the others on an ink disc, as a fleet map's other trucks do. A route's path lists what it passes through: stop ids, and [x, y] waypoints to bend it along the roads; the line is smoothed through them. Its status is done (driven, quiet), on-time (ink), delayed (the danger tone) or proposed (a dotted line in the accent), and its label names it in the legend.
A stop's card is anchored beside its marker and always shown: an ETA, a warning, a reroute to approve. It sits toward the middle of the map unless cardSide says left, right, top or bottom. Anything else you lay over the map (a summary card, zoom controls) goes in children. terrain swaps the built-in coast for your own shapes, as path data in a 100 × 100 box; ratio sets the map's width over its height (16 / 10).
Description
A truck's route from the depot to the distribution center, with a delayed leg, a proposed reroute and your own overlay.
Code
route-map-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propsstops, routes, terrain… (9)
Props
| Prop | Type | |
|---|---|---|
stopsRequired | RouteMapStop[] | – |
routesRequired | RouteMapRoute[] | – |
terrain | RouteMapTerrainThe land under the routes. default DEFAULT_TERRAIN | DEFAULT_TERRAIN |
value | string | nullThe picked stop's id (controlled); | – |
defaultValue | string | nullThe stop picked at first. default null | null |
onValueChange | (id: string | null) => void | – |
legend | booleanShows the legend of route states. default true | true |
ratio | numberThe map's width over its height. default 16 / 10 | 16 / 10 |
label | stringThe map's accessible name. Default: the labels' "Route map". | – |
children | ReactNodeYour own overlays on the map: controls, a summary card. | – |
Other props pass through: extends ComponentProps<'div'>. | ||
Stops
| Prop | Type | |
|---|---|---|
idRequired | string | – |
nameRequired | string"Harbor depot". | – |
xRequired | numberAcross the map, 0–100 from its left edge. | – |
yRequired | numberDown the map, 0–100 from its top. | – |
kind | RouteMapStopKind'origin''stop''destination''current'Its marker. default `stop` | `stop` |
detail | stringA line said with its name and shown on hover: "Departed 09:14", "ETA 15:49". | – |
icon | ReactNodeYour own marker face: a truck, a courier. The current position wears it on the accent; any other stop on an ink disc, in place of its dot. | – |
card | ReactNodeA card anchored beside the marker, always shown. | – |
cardSide | 'left' | 'right' | 'top' | 'bottom'Which side of the marker the card sits on. Default: left or right, toward the middle of the map. | – |
Routes
| Prop | Type | |
|---|---|---|
idRequired | string | – |
pathRequired | (string | RouteMapPoint)[]What it passes through, in order: stop ids, and waypoints as [x, y] (0–100) to bend it along the roads. | – |
status | RouteMapStatus'done''on-time''delayed''proposed'default `on-time` | `on-time` |
label | stringIts name in the legend and the text alternative: "AI proposed via ring road". Default: its state's word. | – |
StylingParts and data attributes
Styling
Parts: route-map, route-map-land, route-map-terrain, route-map-routes, route-map-route (with data-status), route-map-stop (with data-kind), route-map-marker, route-map-tag, route-map-card (with data-side) and route-map-legend. Lines keep their width at any size; the map never mirrors in RTL, since geography doesn't.
MotionDraw-in, markers, cards, pulse · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
| Landfades in as the map scrolls into view | duration.slowease.enter340ms · cubic-bezier(0.16, 1, 0.3, 1) | 340mscubic-bezier(0.16, 1, 0.3, 1) | |
| Routeseach draws along its length: driven, then on time, delayed and proposed, one state after another | duration.deliberateease.emphasized560ms · cubic-bezier(0.6, 0, 0.1, 1) | 560mscubic-bezier(0.6, 0, 0.1, 1) | |
| Markersland as their first line starts; the destination settles | spring.snappy0.2s, bounce 0 | 0.2s, bounce 0 | |
| Cardsrise in after their marker | duration.baseease.enter220ms · cubic-bezier(0.16, 1, 0.3, 1) | 220mscubic-bezier(0.16, 1, 0.3, 1) | |
| Current positiona ring opens and fades, only while the map is on screen | duration.deliberateease.exit560ms · cubic-bezier(0.4, 0, 1, 1) | 560mscubic-bezier(0.4, 0, 1, 1) |
Reduced motion
Everything is drawn in place, and the current position doesn't pulse.
Reduced motion
Routes, markers and cards are drawn in place and the current position holds still. The states read the same: color, dots and the legend carry them.
AccessibilityGroup, markers, text alternative
Accessibility
- The map is a group named by
label(default: the labels' "Route map"). - Markers are buttons in stop order, named by place, kind and detail ("Summit DC, Destination, ETA 15:49"); pressing one picks it (
aria-pressed), and hover or focus shows its name. - The routes and stops are written out for screen readers: every stop with its kind and detail, every route with its state and the stops it runs through ("Proposed via river road: Load 4821 → Summit DC").
- Cards are ordinary content, read where they sit after their marker. The land, lines and legend are decorative.
- Its words come from the labels (
routeMap,routeOrigin,routeStop,routeDestination,routeCurrent,routeDone,routeOnTime,routeDelayed,proposed), translated by the language packs.