Files
antidrift/docs/superpowers/specs/2026-05-31-m4-look-good-design.md
T
felixm c807a72623 Spec M4: cockpit-style web UI design pass
State-driven accent HUD, stacked bands, CSS/JS split out of the inline
HTML, polished presentational review recap. No behavior changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 20:57:32 -04:00

6.1 KiB

M4 — "Look good" Design

Goal: A real design pass on the web UI: a cockpit-style, state-aware HUD that reads at a glance, with CSS/JS split out of the inline HTML for maintainability, and a polished review recap. No behavior changes.

Status: Design approved 2026-05-31. Supersedes the utilitarian inline UI shipped through M3.5.


1. Direction & Visual System

The UI is an instrument panel you glance at — a cockpit, not a document. Dark, near-black cool-neutral base. State is carried by a single state-driven accent: a CSS custom property --accent switched by a data-state attribute on the <main> element. The accent colors the status band's top border and the state pill, so the frame itself communicates where you are without reading text.

State → accent mapping

State data-state Accent
locked locked dim gray
planning planning blue
active · on-task active calm green / cyan
active · nudge nudge amber
active · drifting drift red
review review violet-neutral

data-state is derived in the client render from runtime_state plus, when active, the drift sub-status (driftingdrift; a present nudgenudge; otherwise active). This mirrors the precedence already used by the status-file renderer (drift outranks nudge).

Design tokens (CSS custom properties)

  • Surfaces / text: --bg, --panel, --line, --ink, --ink-dim
  • State accent: --accent (the only variable that changes with data-state)
  • Fixed semantic colors: --ok, --warn, --danger

Typography

  • Timer: heavy weight, font-variant-numeric: tabular-nums.
  • Evidence times: ui-monospace so the bucket columns align.
  • Band headers / pills: small, uppercase, letter-spaced (keeps the existing pill idiom from the current UI).
  • Prose: system-ui.

2. Layout — Stacked HUD Bands

Every state composes the same band primitive: a row with a top divider and consistent horizontal/vertical padding. Stacking bands produces the layered HUD look. The active session follows the approved sketch:

ACTIVE · on task · 7 switches      ← status band (accent border-top + pill)
24:18  write the spec section      ← timer band
done when: draft saved             ← task band
now code·spec ●  |  code 18:02 …   ← evidence band
[ Complete ]                       ← action band

Drift and nudge are not a separate floating box. When the session drifts or is nudged, the status band itself changes copy and data-state flips, so the whole frame goes amber/red. The same controls render inside that band:

  • Drift: Back to task (/refocus), This is on task (/ontask), End session (/complete).
  • Nudge: Dismiss (client-only, current behavior).
  • Pending: a quiet "checking focus…" line.

3. Per-State Treatment

  • Locked: one dim band, large Start planning button (/planning).
  • Planning: an intent + Sharpen band, then field bands — Next action, Success condition, Minutes, Allowed apps — with the blue accent. All existing input ids (#intent, #na, #sc, #mins, #apps, #start, #coachStatus) and the coach pre-fill behavior are untouched.
  • Active: the HUD described in §2.
  • Review (polished, presentational only): summary bands built from data the state already carries — next_action, success_condition, the context-switch count, and the per-window bucket recap (reusing the existing evidence fields). No new backend data is introduced; richer session reflection is M7's job. The End button (/end) remains.

4. Structure

Split the single inline file into three files under internal/web/static/:

  • index.html — markup shell only (<head> links the stylesheet and script).
  • app.css — the full visual system (tokens, bands, per-state rules).
  • app.js — the render logic, moved verbatim: same render() function, same partial-update paths (updateActiveDrift, updatePlanningCoach), same element ids, same EventSource('/events') and POST endpoints. The only additions are the band markup in the template strings and setting main.dataset.state per render.

web.go currently serves only / via c.FileFromFS. Add routes so the two new assets are served from the embedded staticFS:

  • GET /app.cssstatic/app.css
  • GET /app.jsstatic/app.js

No new Go dependencies, no JavaScript build step, no framework. The embedded static directory and the conciseness/token-efficiency ethos of the Go rewrite are preserved.

5. Behavior & Data Flow — Unchanged

Same SSE stream, same partial-update render() logic, same element ids, same POST endpoints, same server-authoritative expiry timer. The redesign is markup + CSS + asset routing only. This is precisely what keeps the existing web_test.go (endpoint and state-JSON assertions, markup-agnostic) green.

6. Testing

  • Existing web_test.go stays green. It asserts on endpoint status codes and the state JSON, not on HTML markup, so the visual rework does not touch it.
  • New Go test: GET /app.css and GET /app.js each return 200 with the correct Content-Type (text/css, text/javascript / application/javascript). Asserted against the router via httptest, stdlib testing only.
  • Manual visual checklist across the six data-state values: locked, planning, active (on-task), active (nudge), active (drift), review. There is no JavaScript test harness in this Go project; the rendering is presentational and verified by eye, consistent with the existing approach.

7. Out of Scope

  • Micro-interactions / motion (timer easing, accent transitions, panel slide-in) — explicitly excluded for M4; can be a later pass.
  • Any new backend data or fields on the state payload.
  • M7 reflection content (real session summary, time-on-task analytics). The M4 review screen is presentational recap of already-available data only.