# ADR-0023: The vanilla auto-mount helper is `@standarx/nav/auto` Status: Accepted Date: 2026-09-22 Deciders: Wesley Cormier ## Context [ADR-0011](0011-package-layout-and-adapters.md) puts a "vanilla auto-mount helper" second in the adapter order (`0011-package-layout-and-adapters.md:132`), after React and before Vue, and left its subpath name the one open detail of that record — working name `@standarx/nav/auto`, not confirmed; the note at `0011-package-layout-and-adapters.md:7-8` is where that rider lived and where its closure is now recorded. The same record is emphatic that the thing is not an adapter: "`vanilla` is not an adapter, because the core is the vanilla API; the only vanilla-specific artefact is an auto-mount helper" (`0011-package-layout-and-adapters.md:90-92`). That sentence sets the bar this record has to clear. The core already mounts in one call — `createInputSystem({ plugins: [spatialPlugin()] })` — and the spatial engine already reads the markup with no configuration: it defaults its root to `document.body` and finds containers by `[data-snav="container"]` alone (`src/spatial/spatial.ts:265-269`, `src/spatial/containers.ts:10`), then reads `data-snav-enter`, `-wrap`, `-block`, `-trap`, `-scroll`, `-ignore` and the four `data-snav-` redirections off the elements as it walks them (`src/spatial/containers.ts:10-18`, `:45-47`). [ROADMAP.md](../../ROADMAP.md) describes the v1 item as "build the containers from the attributes with no framework", and that description is wrong about this engine: the containers are already built from the attributes, by the plugin, with no framework and no helper. A helper that restated that would be a subpath for a convenience alias. So the question this record answers is not "what shall the helper be called" but "what is actually left for it to do". Two things were, and only two. ## Decision The subpath is **`@standarx/nav/auto`**, built from `src/auto/auto.ts`. The rider of ADR-0011 is closed on the working name it proposed. `/mount` reads as a verb waiting for an object, and `/vanilla` would re-assert in a subpath name exactly what ADR-0011:89-90 denies. **1. `autoMount(options)`, not an import with a side effect.** `package.json` declares `"sideEffects": false` (`:26`), so a bare `import "@standarx/nav/auto"` is a module a bundler is entitled to delete. The entry is a factory like every other one in this package — `spatialPlugin`, `gamepadPlugin`, `focusRingPlugin` (ADR-0011:169-172) — and nothing runs until it is called. The signature is `autoMount(options?: AutoMountOptions): AutoMount` (`src/auto/auto.ts:69`), returning a handle carrying `system`, `null` until there is one, and an idempotent `destroy` (`AutoMount` at `:58-63`). **2. It waits for the document.** When `doc.readyState` is `"loading"` the helper defers the whole construction to `DOMContentLoaded` and returns a usable handle immediately (`src/auto/auto.ts:97-103`). A module script is deferred by the platform, so most callers never take that branch; a classic `