KQ-1693 · WAYFINDING/DOTS

Wayfinding Dots

Non-blocking hint dots that sit on the corners of the features nobody has opened yet, each breathing slowly from one shared clock with its own phase. Pressing a dot flicks it up on springs.flick, frames the feature on springs.snap and unfolds the tip out of the dot as a clip circle growing from its centre; closing folds it back and the dot flies on springs.glide along an arc into the corner counter, whose digit rolls on springs.snap and pill lands on springs.recoil. Dots are buttons in reading order: Enter opens, Escape closes and moves focus to the next dot, H shows or hides them all, and under 420px the tip is a bottom sheet you can swipe away.

KQ-1693 · WAYFINDING/DOTS
Fieldline

Scheduled3

  • Replace valve

    Dock 4

    09:30DueMR
  • Hedge trim

    Pier 9 offices

    11:00TF

In progress3

  • Boiler service

    Basin Yard

    08:00DueKA
  • Fence repair

    Fernworks depot

    10:30PN
0of 5 found

0 of 5 found · nothing found yet

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/wayfinding-dots

Props

children
ReactNode
default —
The interface the hints sit on. Mark each feature with data-wayfinding="<target>".
hints
WayfindingHint[]
default defaultWayfindingHints
The hints, { id, target, title, body, placement?, keys? }, in the order they are numbered.
found / defaultFound
string[]
default — / []
Ids of the hints already found, controlled or initial.
onFoundChange
(ids: string[]) => void
default —
Fires from the close, the use or the Dismiss all that found a hint, with every found id.
onOpen
(hint: WayfindingHint) => void
default —
A dot was opened.
onDismiss
(hint: WayfindingHint, via: WayfindingDismissVia) => void
default —
A hint was closed and counted as found: via its button, Escape, a press outside, a swipe of the sheet or using the feature.
onDismissAll
(ids: string[]) => void
default —
Dismiss all was pressed, with the ids it found.
shown / defaultShown
boolean
default — / true
Whether the dots are showing, controlled or initial.
onShownChange
(shown: boolean) => void
default —
Fires from the hotkey or the eye button that showed or hid the dots.
hotkey
string | null
default "h"
The key that shows and hides every dot while focus is inside the component and not in a text field; null turns it off.
dotStyle
"beacon" | "halo" | "ring" | "pip"
default "beacon"
The mark: a ring that leaves the dot, a swelling glow, a breathing hollow ring, or a numbered badge.
breathSpeed
number
default 1
How fast the dots breathe, 0.25 to 2; 1 is one breath every 3.2 s.
accent
string
default "var(--accent-bright)"
The dots, the highlight frame and the counter ring. Any CSS colour; dots take its hue at a fixed lightness so they read in both themes.
discoverOnUse
boolean
default true
Using a feature yourself counts as finding it: its dot leaves for the counter without a tip.
counter
"top-start" | "top-end" | "bottom-start" | "bottom-end"
default "bottom-end"
The corner the discovered counter sits in.
sheetBelow
number
default 420
Below this container width, in px, tips dock as a bottom sheet.
label
string
default "Feature hints"
The hint layer's accessible name; the found count is added to it.
disabled
boolean
default false
The dots stay but cannot be opened, the hotkey is off and the counter's buttons are inert.
className
string
default —
Classes for the wrapper around the interface.