Skip to content

feat(web): retheme every surface on shared design tokens (AGT-4201) - #558

Merged
unohee merged 2 commits into
mainfrom
feat/ui-surface-refactor
Sep 4, 2026
Merged

feat(web): retheme every surface on shared design tokens (AGT-4201)#558
unohee merged 2 commits into
mainfrom
feat/ui-surface-refactor

Conversation

@unohee

@unohee unohee commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

What

The web surface (cockpit, chat, orchestration, threads, warehouse, and the two server-rendered legacy pages / and /issues) now draws from one token vocabulary (web/static/css/tokens.css), ported from the chat-ui agent guideline's §8.1 vocabulary to this vanilla ESM stack.

  • Tokens + theme: semantic tokens only (--bg-app/--bg-surface/…, --fg-*, accent/danger/warning/success, radius, 4pt spacing, type scale, motion, z-index, code + chart + role colours). Light theme is a value swap under [data-theme="light"], stamped by themeBoot.js before first paint; theme.mjs owns the toggle (persisted, follows the OS until a choice is made, aria-pressed + a label naming the mode it switches to). Every page has the toggle.
  • shell.css: shared primitives — sticky blurred topbar with one nav across all pages, .btn family, inputs, chips, badges, cards, status lines, empty state, scroll-to-latest, in-page confirm card, drop overlay, :focus-visible ring (never outline:none alone), prefers-reduced-motion kill, .sr-only.
  • Chat (§2.1/§2.3): composer.mjs — autogrow textarea (≤40vh), Enter sends / Shift+Enter newline / isComposing+229 guard, Send disabled on empty, in-place "Sending…" with aria-busy, draft persistence, labelled chip-remove buttons, drop overlay. scrollFollow.mjs — 80px slack, "↓ Latest" (with unseen count), one live-region announcement per batch. Loading / empty / unreachable states.
  • Destructive actions (§3.2): threads' Resolve now shows a confirm card naming the thread and version (Tauri has no window.confirm); warehouse overwrite arms on first click naming the file.
  • Colours in JS: ROLE_COLORS, KIND_COLORS, statusColor() are token references; SVG paints go through paint() (attribute + inline style) so the graph re-paints with the theme.
  • Legacy pages: dashboardHtml.ts / issueBoardHtml.ts keep their inline stylesheets (their scripts are asserted by name in tests) but their :root blocks are now aliases into tokens.css; all 34 hex literals removed; brand font applied.
  • Font: Inter (latin variable subset, 48 KB) vendored with its OFL licence; Korean falls through to the system stack.

Gate

tests/web/tokens.test.ts enforces the guideline permanently: every var() resolves; no hex literal outside tokens.css; no inline <style> in a shell; every shell links tokens+shell+themeBoot in order and shares one nav; every icon-only button has aria-label; legacy pages link tokens, boot the theme, offer a toggle, and carry no colour literal.

New unit tests: composer.test.ts, scrollFollow.test.ts, theme.test.ts, plus a two-step resolve case in threadBoard.test.ts.

Verification

  • npm run typecheck ✓ · npm run lint ✓ (one pre-existing warning in vega adapter test) · LC_ALL=C npx vitest run → 5524 passed, 10 skipped (398 files)
  • Existing DOM contracts (chatView.test.ts, orchestrationView.test.ts, threadBoard.test.ts) unchanged and green.
  • Not run: axe / screen-reader pass (no browser harness in CI); breakpoints reviewed by CSS only.

Guideline §12 self-check

  • states defined per component (idle/loading/streaming/success/error/empty/disabled) — chat room + composer; others idle/loading/error/empty
  • no hardcoded colour/spacing/font outside tokens.css (test-enforced)
  • focus-visible ring on every interactive element (shell.css global)
  • icon-only buttons named (test-enforced)
  • Enter/Shift+Enter/IME behaviour (tested)
  • destructive → in-page confirm naming target (tested)
  • reduced-motion honoured (global + graph-specific)
  • live region once per batch, not per token (tested)
  • axe run — not available in CI; manual pass pending on vela

One token vocabulary (tokens.css) now paints all seven pages — cockpit,
chat, orchestration, threads, warehouse, and the two server-rendered
legacy pages — with a light theme as a value swap under
[data-theme="light"] stamped by themeBoot.js before first paint, and a
toggle on every page. shell.css carries the shared primitives: topbar,
buttons, inputs, chips, badges, focus ring, empty/status lines, in-page
confirm card, reduced-motion.

Chat gets the guideline's composer (autogrow textarea, Enter sends /
Shift+Enter newline / IME-safe, empty-disabled, in-place sending state,
draft persistence, labelled attachment chips, drop overlay) and stream
following (80px slack, "↓ Latest" button, one live-region announcement
per batch). Threads' resolve and the warehouse's overwrite ask in the
page, naming the target. Role/kind/status colours in the JS colour maps
are token references, so the SVG graph re-paints with the theme.

tests/web/tokens.test.ts is the gate: every var() resolves, no hex
literal outside tokens.css, no inline <style> in a shell, every shell
links tokens+shell+themeBoot in order and shares one nav, every
icon-only button has a name. The brand font (Inter, latin subset) is
vendored with its OFL licence.
…/typecheck 10 minutes

Two consecutive Lint runs on this branch sat silent for the full five
minutes inside `npm ci` — after the reify warnings, before any output —
and were cancelled by the job timeout, while the same lockfile installs
in ~20 s on main. Nothing in package.json or the lockfile changed. The
one network call `npm ci` makes after install is the audit report, and
it has no timeout of its own, so a slow audit endpoint stalls the whole
job. `--no-audit --no-fund` removes that dependency and
`--prefer-offline` uses the restored cache first; the wider timeouts
keep a genuinely slow runner from failing the gate.
@unohee
unohee merged commit 394bdc7 into main Sep 4, 2026
7 checks passed
@unohee
unohee deleted the feat/ui-surface-refactor branch September 4, 2026 08:35
@unohee unohee mentioned this pull request Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant