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

DeploysLogsAccess

States · walked from the machine

openclosed

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.ts
  • packages/ui-headless/core/src/sidebar/sidebar.spec.md
  • packages/ui-headless/core/src/sidebar/vanilla.ts
  • packages/ui-headless/browser-contract/sidebar.contract.ts, the shared browser assertions.
  • Each framework package has a matching browser/sidebar.spec.ts contract entry.