← showcases · case 01 · Greenfield · surfaces: TUI + Web

orbit — one vocabulary, two surfaces

An agent console with a terminal TUI and a web dashboard. The terminal owns its palette, so TUI tokens are ANSI roles and contrast is advisory; a web theme change must never move a TUI pixel. This page is the showcase half of the system; the rules it obeys live in the page.

host-owned palette shared vocabulary, declared sharing boundary monochrome high-contrast path advisory measurement

Context

Who: developers running an agent in a terminal, plus the same sessions reviewed in a browser. Jobs: start a task, watch tool calls, approve or deny actions, resume work later. Constraints: the terminal emulator owns background, foreground, and the ANSI palette; the app controls only roles, weight, and layout. The TUI and the web app share one vocabulary, not one token set.

Shared (declared)

Term table — session, turn, tool call, permission, plan, checkpoint. One action keeps one word across both surfaces.

Not shared

Tokens, components, and layout. The TUI never references a web token; the drift check fails on an undeclared cross-surface reference.

Principles

RuleRationale
Roles, not valuesThe host palette cannot be read or set; naming an RGB value would be a lie the app cannot keep.
Color is never the only carrierEvery colored state also carries a word or glyph, so a failed or overridden ANSI role cannot hide meaning.
High contrast is the default foreground plus weightNo ANSI role is legible on both light and dark hosts; “brighter color” cannot be the escape hatch.
The cell is the layout unitGaps, indents, truncation, and status collapse are counted in cells, never in pixels.
Surfaces stay decoupled at the token layerA web theme choice that reached the TUI once produced dark text on a dark terminal; the boundary is now a checked rule.

Roles and advisory contrast

The app emits roles; the numbers below are measured against one named reference palette (xterm-style defaults) and are advisory only. The guarantee is structural: a word or glyph always accompanies color, and a monochrome path always exists.

RoleANSIUsed foron dark hoston light host

Verdicts: ok ≥ 4.5 · large only ≥ 3.0 · fails. No role passes on both hosts — that is the reason the fallback is structural, not a color.

TUI scene

A whole session, not a sampler: transcript, inline permission panel, status line. Status content collapses from the outside in as width drops.

Host background:
› refactor the token checker to enumerate rendered pairs
● plan 4 steps · read → draft → apply → verify
✓ read docs/design/DESIGN_SYSTEM.md
✓ read docs/design/check-tokens.mjs
⠿ run node check-tokens.mjs --strict
✗ exit 1 · 3 failures
dawn standard: text/subtle 4.41:1 (< 4.5)
dawn standard: focus-ring/surface 2.71:1 (< 3.0)
◌ write docs/design/check-tokens.mjs +12 −4
permission · apply edit to check-tokens.mjs?
+12 −4 · no secrets in diff
[a] allow once [s] allow session [d] deny
… 3 earlier events · ↑ scroll for history
orbit·main·sonnet ·12.4k/200k·◆ plan ·⏸ 2 pending·04:12

Status line collapse (40 / 26 / 18 cells)

orbit·main·sonnet ·12.4k/200k·◆ plan ·⏸ 2 pending·04:12
orbit·◆ plan ·⏸ 2·04:12
orbit·◆ plan·⏸ 2

Monochrome fallback

Switch the scene above to Monochrome HC to see the system as a user with NO_COLOR or a forced monochrome terminal sees it. Meaning survives through words, glyphs, and weight — never through a brighter color.

● plan running
✓ read done
✗ run failed
◌ write pending
■ plan running
ok read done
x run failed
.. write pending

Left: color roles. Right: ASCII glyphs with weight as the only emphasis axis. The right column is the guaranteed path; the left is an enhancement when the host allows it.

Surface isolation

The web dashboard below is a different surface with its own tokens. Change the web theme: the dashboard must move, the TUI must not. The check asserts both directions, so a no-op cannot pass.

Web theme:

Web dashboard

session orbit-18 · 4 succeeded · 1 failed · 2 pending

⠿ run node check-tokens.mjs --strict
orbit·◆ plan·⏸ 2
isolation check: idle — press “Run isolation check”.

Glyphs

MeaningPrimaryASCII fallbackNotes
running⠿*Braille spinner; frozen under reduced motion.
done✓okWord form survives fonts without the glyph.
failed✗xAlways paired with the failing check.
pending◌..Distinct from dim text alone.
needs attention◆!Used for permission and plan state.

Emoji are excluded: width varies by host and font, which breaks cell alignment and truncation.

Typography

The host owns the font; the TUI owns weight, dim, and alignment. There is exactly one size, so hierarchy is built from emphasis and position, never from a type scale.

default tool call completed in 1.42s
bold attention and values use weight, not size
dim timestamps, paths, secondary metadata
italic sparingly: quoted user text
aligned 12.4k / 200k tokens
ro·◆ plan·04:12
  • Columns are counted in cells; truncation uses a visible ellipsis so “there is more” is never silent.
  • Emoji are excluded from the glyph set — variable width breaks every alignment guarantee.
  • Numeric columns stay right-aligned and tabular, so scanning stays vertical.

Components and states

TUI components are line types and panels. Every state below also survives the monochrome host — shape and word carry it when color cannot.

Transcript line types

› user prompt
● plan · 4 steps
⠿ tool running
✓ tool done
✗ tool failed

Panel · focused action

⚠ permission apply edit?
[a] allow once [s] allow session
[d] deny (esc)

Focus is inverse video: visible with color disabled.

Status line segments

orbit·main· ◆ plan·⏸ 2
— collapsed from the outside in as width drops

Disabled and unavailable

[d] deny (no pending request)
⠿ run queued · waiting for permission

Unavailable actions stay visible with their reason; they are not hidden.

Motion and accessibility

Motion budget

  • One spinner cadence while work is running; it stops the moment the result lands.
  • prefers-reduced-motion and dumb terminals freeze the spinner at a static glyph.
  • No transitions, no scroll effects — terminal motion is expensive and distracting.

Accessibility

  • Focus is inverse video, visible without color; meaning never rides on an ANSI role.
  • The screen is a linear text stream — screen readers get the same order as the eye.
  • Every keybinding is discoverable from a status line or help overlay.
  • The monochrome high-contrast path is a shipped mode, not a fallback users must invent.

Verification

  • Pairs are enumerated from rendered components, per host background — not sampled by hand.
  • Advisory numbers are labeled with the reference palette they assume.
  • Structural fallbacks are asserted: a no-color rendering, a monochrome rendering, and an ASCII glyph set for every role.
  • The drift check rejects any TUI token that resolves to a web token unless the sharing is declared; the check itself is proven by mutating an artifact and confirming failure.