Skip to the content
Scale

Props

NameTypeDefaultDescription
metersPerPixel*number—The ground distance one CSS pixel spans at the map's centre, in metres, as the map gives it at its current zoom and latitude. Required. ScaleBar picks a round distance from it and draws the bar that long. A value that is not a positive finite number shows "—" and says "Unknown".
scaleLabelstringThe locale'sOverrides "Scale", heard before the distance
metersUnitLabelstringThe locale'sOverrides "m", the unit after a distance under a kilometre
kilometersUnitLabelstringThe locale'sOverrides "km", the unit after a distance of a kilometre or more
unknownLabelstringThe locale'sOverrides "Unknown", heard in place of "—" when metersPerPixel gives no distance
classNamestring—Joined after pn-ScaleBar on the root
styleCSSProperties—Reaches the root
refRef<HTMLDivElement>—Reaches the root element
slotProps{ label?: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> | undefined; bar?: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> | undefined; }—Props for each part: label, bar.
metersPerPixel (required)number, default —
The ground distance one CSS pixel spans at the map's centre, in metres, as the map gives it at its current zoom and latitude. Required. ScaleBar picks a round distance from it and draws the bar that long. A value that is not a positive finite number shows "—" and says "Unknown".
scaleLabelstring, default The locale's
Overrides "Scale", heard before the distance
metersUnitLabelstring, default The locale's
Overrides "m", the unit after a distance under a kilometre
kilometersUnitLabelstring, default The locale's
Overrides "km", the unit after a distance of a kilometre or more
unknownLabelstring, default The locale's
Overrides "Unknown", heard in place of "—" when metersPerPixel gives no distance
classNamestring, default —
Joined after pn-ScaleBar on the root
styleCSSProperties, default —
Reaches the root
refRef<HTMLDivElement>, default —
Reaches the root element
slotProps{ label?: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> | undefined; bar?: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> | undefined; }, default —
Props for each part: label, bar.

Use it

import { ScaleBar } from '@mylder/perron/patterns'

Ask your agent

search_docs({ query: "ScaleBar" })

Keyboard and ARIA

ScaleBar has no keys of its own and nothing in it is focusable: a scale is read, not operated. Tab passes it by.

KeyDoesFrom
NoneThe scale takes no focus and no keys; Tab passes it byPerron
ElementRoleAccessible name fromStates announced
rootNone (generic div)NoneNone
labelNone (generic span)Its text: "Scale" from VisuallyHidden, then "300 m"; or "Scale" and "Unknown"None
barNone: aria-hiddenNone; the label says the distance it drawsNone

The root is a plain div, not a landmark, an image or a named region: the scale is one line of text to a screen reader, "Scale 300 m", read in place, and a role="img" with an aria-label would hide the same words in an attribute that expectOwnedStrings and the reader both read less reliably than text.

The label is not a live region: the distance changes with every zoom step and would flood a screen reader, and Accessibility asks that only changes of state be announced, in the screen's own polite live region.

What axe won't catch, from the contract, each with how this component meets it:

  • Everything the eye is told, the ear is told, for every shape each prop accepts: the eye gets the distance and, from the bar's shape, that it is a scale; the ear gets "Scale" from VisuallyHidden and the same distance from the label's text. With no resolution, the eye gets "—" and the ear "Unknown". No colour carries meaning.
  • Every landmark and named region has an accessible name: none here; the root is not a landmark.
  • Every default label can be overridden: scaleLabel, metersUnitLabel, kilometersUnitLabel and unknownLabel.
  • Every shape the type accepts renders, in every variant: metersPerPixel as a fraction of a metre, as metres and as kilometres, at each of the four round steps, and as 0, a negative number, NaN and Infinity, which show "—". ScaleBar.test.tsx runs the rounding over each, and the stories render metres, kilometres, the shortest bar and the unknown.
  • A hidden copy kept for animation is aria-hidden, and a collapsed region is inert: none here; nothing animates or collapses. The bar and the "—" are hidden because the words beside them say them, not for animation.
  • Native semantics first: plain div and span, text read in place, aria-hidden on the drawn bar and the "—", and no ARIA of the pattern's own beyond that.
  • Focus moves in reading order and returns on close: nothing takes focus or opens; the DOM order, label then bar, is the order on screen.
  • Every target is at least size.target-min square: no target; nothing in the scale is pressed.
  • Loops stop when the system asks for less motion: none here; nothing moves on its own, and the bar's length changes without a transition.

Owned strings

Every word ScaleBar shows or announces is its own: the caller gives a number, never a word. unknown is reused from packages/perron/src/locale/en.ts; the other three are new keys, which the build adds to all four locale files, with the Danish, Swedish and Norwegian below.

The new keys read scale "Scale", metersUnit "m" and kilometersUnit "km", with the units written as the SI symbols in every locale. Content design writes a space before units, "4 min", "45 km/h", so the distance reads "300 m" and "2 km", the symbols every Nordic locale writes the same; the keys follow minutesUnit, Delay's unit after a figure; "Scale" names what the distance measures for a reader who can't see the bar, and is "Målestok", "Skala" and "Målestokk" in the others; symbols, unlike "metres" and "kilometres", need no singular and plural for "1 km" and "2 km".

KeyEnglishOverrideSeen or heard where
scale"Scale"scaleLabelThe label part, heard first, through VisuallyHidden; never seen
metersUnit"m"metersUnitLabelThe label part, seen and heard after a figure under 1000, "300 m"
kilometersUnit"km"kilometersUnitLabelThe label part, seen and heard after a figure in kilometres, "2 km"
unknown"Unknown"unknownLabelThe label part when metersPerPixel gives no distance: heard in place of the "—"

Do and don't

For a caller using the pattern, with the reason. Each row is a rule: a must row's Don't is never right, a should row's needs a reason, and Checked by says what catches the Don't in a caller's code.

RuleLevelDoDon'tChecked by
DD-1mustGive metersPerPixel for the map's centre, and give it again on every zoom and on every move that changes the latitude muchGive it once and leave it: the scale then claims a distance the map no longer showsreview
DD-2mustLeave metersPerPixel as NaN until the map has loaded, so the scale shows "—" and says "Unknown"Pass a guess or 0 before the map is ready: the brand never claims more than the data knowsreview
DD-3shouldPlace the scale from the screen, at the bottom right beside the map controls, size.panel-gap from the edges at z-index.map-controlsGive it a position, a margin or a z-index through className: the pattern sizes to its content and places nothingreview
DD-4shouldKeep one scale on the mapPut a scale in a panel or a sheet, away from the map it measuresreview
DD-5mustLeave the scale out of the screen's live region; announce changes of state thereMake the label a live region: it changes on every zoom step and would flood a screen readerreview