Spatial Navigation
Headless Spatial Navigation; covers CSS Spatial Navigation, ARIA grid navigation.
| Package | @vizejs/ui/spatial-navigation |
| Maturity | stable |
| Own the source | vize lib pull spatial-navigation |
| Requires | collection |
| Aliases | spatial navigation, grid navigation, geometry navigation |
| Covers | CSS Spatial Navigation, ARIA grid navigation |
API
createSpatialNavigation
Create a geometry-driven, SSR-safe arrow navigation controller.
function createSpatialNavigation<Key extends CollectionKey, Value>( options: SpatialNavigationOptions<Key, Value>, ): SpatialNavigationController<Key>
useSpatialNavigation
Create a spatial controller disposed with the current Vue effect scope.
function useSpatialNavigation<Key extends CollectionKey, Value>( options: SpatialNavigationOptions<Key, Value>, ): SpatialNavigationController<Key>
capture
function capture(errors: unknown[], callback: () => void): void
surfaceErrors
function surfaceErrors(errors: readonly unknown[], message: string): void
readBoolean
function readBoolean(source: unknown, name: string): boolean
readAlgorithm
const readAlgorithm
readBoundary
const readBoundary
readFocus
const readFocus
readDirection
function readDirection(value: unknown): SpatialNavigationDirection
validateOptions
function validateOptions<Key extends CollectionKey, Value>( options: SpatialNavigationOptions<Key, Value>, ): void
normalizeRect
function normalizeRect(value: unknown): SpatialNavigationRect
rankItem
function rankItem<Key extends CollectionKey, Value>( item: CollectionItem<Key, Value>, rect: SpatialNavigationRect, index: number, origin: SpatialNavigationRect, direction: SpatialNavigationDirection, ): RankedSpatialItem<Key, Value> | null
selectCandidate
function selectCandidate<Key extends CollectionKey, Value>( candidates: readonly RankedSpatialItem<Key, Value>[], algorithm: SpatialNavigationAlgorithm, ): RankedSpatialItem<Key, Value> | undefined
selectWrappedCandidate
function selectWrappedCandidate<Key extends CollectionKey, Value>( items: readonly { item: CollectionItem<Key, Value>; rect: SpatialNavigationRect; index: number; }[], origin: SpatialNavigationRect, direction: SpatialNavigationDirection, ): RankedSpatialItem<Key, Value> | undefined
keyDirection
function keyDirection(event: KeyboardEvent): SpatialNavigationDirection | null
isEditableDescendant
function isEditableDescendant(event: KeyboardEvent): boolean
Behavior
createSpatialNavigation turns collection geometry into predictable physical arrow movement. It
implements library-owned behavior because CSS Spatial Navigation remains a Working Draft and is not
a portable browser primitive. Roles, selection, activation, and styling remain consumer-owned.
| Concern | Contract |
|---|---|
| Geometry | Default measurement uses transformed viewport rectangles from getBoundingClientRect; custom geometry supports virtualized and server-described items. |
| Normal ranking | Candidates use the CSS Spatial Navigation distance terms: Euclidean distance, orthogonal displacement and bias, and projected alignment. |
| Grid ranking | Aligned candidates are preferred, then primary-axis distance, overlap, orthogonal distance, and registry order. |
| Direction | Up, down, left, and right are physical directions and do not reverse under RTL. |
| Candidate set | Only registry-navigable items with finite, non-negative, ordered geometry participate. |
| Initial state | A missing active key uses the first navigable item as the search origin without mutating it during findTarget. |
| Boundaries | contain consumes an owned boundary arrow; exit publishes the boundary but preserves native scroll or ancestor behavior. |
| Looping | Optional looping selects the opposite aligned spatial edge and remains deterministic by registry order. |
| Editing | Modified, composing, already-handled, form-control, contenteditable, and shadow-retargeted editor events are preserved. |
| Focus | focus moves DOM focus; logical only changes active state for active-descendant or externally managed focus. |
| Scrolling | preventScroll is forwarded with a legacy focus fallback; logical focus and prevented focus use nearest-block reveal by default. |
| Virtualization | Null DOM elements are valid with custom rectangles and logical focus; custom reveal receives the resolved collection item. |
| Reactivity | Algorithm, boundaries, looping, focus, prevent-scroll, and disablement are read at operation time. |
| Callback timing | State and DOM effects commit before immutable onNavigate; the originating native event and numeric score are retained. |
| Failure atomicity | Registry, focus, reveal, and consumer failures are aggregated after committed logical state remains observable. |
| Server rendering | Construction and prop reads do not measure layout; deterministic markup hydrates in place before first navigation. |
| Vapor | A public composable fixture must compile without diagnostics in native DOM, SSR, and Vapor lanes. |
| Styling | The module emits no CSS and owns no visual state. |
| Tree shaking | Root and subpath consumers emit identical JavaScript, retain no unrelated families, and emit zero CSS. |