DocsPlatform
Sidebar
One open binding for a navigation rail. How the collapsed state actually looks is left to you, because that is a layout decision rather than a behavioural one. All four layers share the one behaviour contract.
Live machine
The shipped sidebarMachine — open or closed, with the reason (trigger, escape, outside click) carried on every transition.
sidebar · live via createService
States · walked from the machine
Transitions from here · click to send
Context
{
"mode": "uncontrolled"
}Log · newest first
No events yet — use the component above or send a transition.
The layers
Machine is pure state with no DOM. Headless maps that state to framework-native anatomy and ARIA. Styled keeps the existing Grassroot visual recipe while delegating behaviour. Vanilla mounts the same headless contract without a framework.
Behaviour contract
The machine owns open requests, controlled/uncontrolled synchronisation, trigger semantics, and presentational collapse. Adapters translate DOM events and render machine snapshots. Not one of them keeps a second reducer or re-filters the collection behind your back.
- Controlled bindings emit serialisable change requests, and only adopt the parent's state once it comes back in through input.
- Uncontrolled bindings commit straight to machine state, and still emit the same request object, so a consumer can watch either way.
- Keyboard, pointer, focus, dismissal, and accessibility behaviour are shared across vanilla, React, Vue, Solid, Svelte, and Angular.
Evidence
The authoritative source files are:
packages/ui-machines/src/sidebar/index.tspackages/ui-headless/core/src/sidebar/sidebar.spec.mdpackages/ui-headless/core/src/sidebar/vanilla.tspackages/ui-headless/browser-contract/sidebar.contract.ts, the shared browser assertions.- Each framework package has a matching
browser/sidebar.spec.tscontract entry.