ADR-0023 — Sidebar stations open one at a time

At most one sidebar station is open at each level, so opening one closes the one beside it and the sidebar stays short.

Context

ADR-0018 made every sidebar folder an orbit station. The station that holds the current page opens, the others start closed, and each station's chevron opens and closes it on its own. A reader who opens three stations has three open: this site's Maintain track has twelve sections, so a few clicks turn the sidebar back into the long list that stations were meant to shorten, and the open station you came for scrolls out of view.

What the code does today (src/components/docs/orbit-station.tsx):

  • Each station is Fumadocs' SidebarFolder, which keeps its own open state (fumadocs-ui/components/sidebar/base). It opens itself when it holds the current page (active) and never closes itself.
  • useFolder() gives a station's content its own open and setOpen; nothing links one station's state to another's.

The owner asked on 2026-11-01 for one open section at a time, opening one closing the other, and chose that this applies at every level, nested stations included.

Decision

  1. At most one open station per level. Stations that share a parent (the track's top level, or the inside of one station) form a group, and at most one station in a group is open. Opening a station, with its chevron or its name, closes the open station beside it. The same holds inside an open station for its nested stations, and below that.
  2. Closing leaves the group closed. Closing the open station opens no other; a track can show every station closed.
  3. The current page sets the open path. On every page change, each station on the current page's path opens and every other station closes, at every level. A reader who opened a station and then follows a link elsewhere sees the new page's stations open, not the one they had opened.
  4. Everything else in ADR-0018 decision 2 stands: the card, header, moons on an arc, the current station's border in the ecosystem colour, nested stations, top-level pages and separators. The ADR constellation (ADR-0019) is a station like any other here.
  5. Built on Fumadocs' folder state. A small client context per group records which station is open; each station reports its own opening through useFolder() and closes when another in its group opens. No fork of the sidebar and no new dependency.

Options considered

ConcernOptionProsConsVerdict
ScopeA — every levelOne rule everywhere; the shortest sidebarA nested station closes when its sibling opens, even inside a long sectionChosen (owner)
ScopeB — top-level stations onlyNested stations stay freeTwo behaviours in one sidebarRejected (owner)
On a page changeA — the current path opens, the rest closeThe sidebar always shows where the reader isA station the reader opened by hand closes when they navigate away from itChosen
On a page changeB — keep what the reader openedRespects a manual choiceTwo stations open again: the current one and the manual one, which breaks decision 1Rejected
BuildA — a group context over Fumadocs' useFolder()Small; stays on Fumadocs' primitives (ADR-0018)Depends on SidebarFolder keeping useFolder() and opening on activeChosen
BuildB — a controlled folder of our ownFull controlRe-implements Fumadocs' collapsible, its animation and keyboard handlingRejected

Consequences

Positive

  • A long track stays a short list of station names with one open, wherever the reader clicks.
  • The open station is always the one the reader chose last, or the current page's.

Negative / accepted trade-offs

  • Comparing two sections side by side in the sidebar is no longer possible; the reader opens one after the other.
  • A station opened by hand closes on the next page change unless it holds that page.
  • The behaviour needs a client context; the server render still shows the current path open and the rest closed, as today.

Content invariants

None touched: the sidebar's behaviour changes, not any page, and per-page Markdown and llms.txt carry no sidebar.

Security considerations

None — this change touches no data, no endpoint and no secret. It changes client-side open and closed state in the sidebar only.

Implementation notes

  • src/components/docs/orbit-station.tsx: a group context per level (the root's, and one inside each station) holding the open station's id; a station that opens claims the group, and a station closes when another claims it. The current page's path claims each group on every page change.
  • Keyboard and screen readers unchanged (by construction, not tested with a screen reader): the chevron stays Fumadocs' collapsible trigger with aria-expanded; a station closed by another reports aria-expanded="false".
  • Reduced motion: stations open and close without animation, as today (the animation is untouched).
  • Tests (ADR-0007, ADR-0021): the group's rule as a pure function (open one, open a sibling, close the open one, a page change to another station, a nested group, a page change into a nested station). The rendered station (client) is not unit-tested until a DOM is chosen (ADR-0021); it is checked in Chrome on localhost at phone and desktop width. Checked in Chrome at desktop width only: opening a station closes the open one, closing leaves all closed, a page change opens that page's station (also when the reader had opened another), and the ADR constellation follows the rule. The automated Chrome window did not resize, so phone width (the drawer) was not seen.
  • bun run test, bun run types:check and bunx next build pass; bun run lint is reported while it stays blocked (AGENTS.md §5).

Docs to update on implementation

  • ../app-flow/one-open-station.mdx: Current, folded into ../app-flow/docs-sidebars.mdx screen 1 and its states. Kept as its own page, which docs-sidebars links from screen 1, so its diagrams stay with it.
  • ADR-0018: Superseded by: this ADR for decision 2's Open and closed point.
  • AGENTS.md §1 (the sidebar bullet) and PROJECT_AGENTS.md, where the sidebar is described (it describes only the ADR constellation, so nothing changed there); README.md.
  • DESIGN.md and .impeccable/design.json, where they describe stations opening.
  • ./index.mdx: status.

On this page

6 min read