- lib/error/: classify any caught error into a stable ErrorKind from the transport signal (HTTP status / Connect Code / fetch TypeError / navigator.onLine); map to translated error.* messages via reportError (sticky Retry toast for retryable kinds) or errorMessageKey (inline). Mail compose now surfaces the translated 403/error inline. - lib/ui/view-state.svelte: shared loading/empty/error placeholder with the right live-region role + optional action; entity tables (races/sciences/ship-classes) migrated, rest adopt incrementally. - map/selection-ring.ts: accent ring around the selected planet, fed into the map buildExtras alongside the reach circles. - lib/ui/sheet-dismiss.ts: tap-outside + drag-handle swipe-down dismissal for the planet/ship-group bottom-sheets (hand-rolled pointer events). Tests: error, view-state, selection-ring, sheet-dismiss (761 total). Docs: ui/docs/error-state-ux.md (+ index); F4 marked done. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.8 KiB
UI Client — Finalization Plan
The MVP web client (Phases 1–30, PLAN.md) is functionally complete. This plan finalizes the web experience — visual consistency, accessibility, localisation, error UX, installability, and documentation — before the native platform wrappers in ROADMAP.md. It absorbs the original Phases 33 (PWA) and 35 (Polish), which were pulled forward to here.
This is a finalization pass, not the final word. After it ships, the owner exercises the whole UI by hand and brings small-nuance fixes; the last stage (F8) is an explicit, open-ended owner-driven refinement loop. The earlier stages are "done" when their acceptance holds; the feature set is "final" only when the owner signs off.
Each stage ends with a runnable, reviewable artifact and is gated on the
per-stage CI rule (push, watch go-unit / ui-test to green) before
being marked done.
F1 — Visual design system — done
Merged to development via PR #26 (2026-05-22): a shared design-token
system (ui/frontend/src/lib/theme/), light/dark theming with a picker
and pre-paint guard, and the whole UI migrated onto the tokens.
Documented literal exceptions: the battle-scene data-viz palette, overlay
scrims, and directional/deliberate drop shadows. The default theme
follows the OS (system); the account-menu picker pins light or dark.
Tokens and conventions live in ui/docs/design-system.md.
Goal: replace the ad-hoc per-component styling (inline hex colors like
#0a0e1a, one-off spacing) with a shared design language so every view
looks like one product.
ui/frontend/src/lib/theme/(orapp.css:root) design tokens: color palette (surface / border / text / accent / danger), spacing scale, radii, typography, focus-ring style — as CSS custom properties.- Migrate components to the tokens (calculator, inspectors, sidebar,
tables, lobby, auth) — replace literal hex/px with
var(--…). - Topic doc
ui/docs/design-system.mddocumenting the tokens and usage.
Acceptance: no literal theme colors left in component <style> blocks
(spot-checked); a single token change restyles the app coherently.
Consider the frontend-design skill for the palette/spacing pass.
F2 — Accessibility (WCAG 2.2 AA) — done
Shared a11y primitives (.sr-only, .skip-link, trapFocus,
restoreFocus, the --color-focus ring), keyboard paths and ARIA across
login / lobby / in-game shell (skip links + landmarks, WAI-ARIA sidebar
tabs with roving + arrow keys, menu Escape + focus restore, the mail
compose modal dialog with a focus trap), and the map canvas handled as a
labelled accessible-alternative surface (data via the sidebar/tables).
Gated by tests/e2e/a11y-axe.spec.ts (axe WCAG 2.2 AA scan of every
top-level view, chromium-desktop, zero violations) and
tests/e2e/a11y-keyboard.spec.ts. Documented in ui/docs/a11y.md.
(From Phase 35.) Goal: the whole client is usable by keyboard and assistive tech.
- Keyboard-only paths for login, lobby, and the in-game shell; visible focus rings (from the F1 token); ARIA labels/roles; screen-reader-only text where needed.
ui/docs/a11y.mdaudit results.
Acceptance: WCAG 2.2 AA on lobby, login, and the in-game shell per an axe-core scan; full keyboard reachability with visible focus. Tests: axe-core integration tests on every top-level view; Playwright keyboard-only navigation.
F3 — Localisation completeness — done
An audit found the client already i18n-first (the single hard-coded UI
string, the battle-scene aria-label, is now keyed); en/ru already have
identical key sets (692 keys). Added locale persistence to the i18n
store (localStorage galaxy-locale: stored choice > browser detection
default) so a switch survives reloads, and
tests/i18n-completeness.test.tsenforcing en/ru key-set parity, non-empty values, and persistence. A noisy literal-text lint was deliberately skipped — the structural parity test plus the i18n-first convention cover drift. Docs:ui/docs/i18n.md.
(From Phase 35.) Goal: every visible string is translated (en + ru).
- Audit all components for hard-coded strings; move them into the i18n bundles; missing-key detection test; locale-switch persistence across reloads.
Acceptance: no untranslated visible strings; missing-translation test is green; locale persists. Tests: Vitest i18n bundle-structure + missing-key detection.
F4 — Error & state UX — done
Central error surface src/lib/error/ (classify any error into a stable
ErrorKind from the transport signal; map to translated error.*
messages via reportError toast — sticky + Retry for retryable kinds —
or errorMessageKey for inline). Shared ViewState placeholder
(loading/empty/error with a11y roles), adopted by the entity tables.
Selected-planet ring on the map (map/selection-ring.ts, fed into
buildExtras). Bottom-sheet tap-outside + swipe-down dismissal
(lib/ui/sheet-dismiss.ts, hand-rolled pointer events). Error-surface
and ViewState adoption continue incrementally across the remaining
views. Docs: ui/docs/error-state-ux.md.
(From Phase 35, plus the deferred Phase 13/35 items.) Goal: consistent, actionable feedback everywhere.
ui/frontend/src/lib/error/central error surface with stable codes and retry/escalation guidance; every server-side error mapped to a translated, actionable message (en/ru).- Consistent empty / loading / error states across views.
- Selected-planet visual on the map (ring/halo) off the
SelectionStore(deferred from Phase 35). - Mobile bottom-sheet swipe-down + tap-outside dismissal (deferred from Phase 13/35).
Acceptance: every server error → a translated actionable message; consistent empty/loading/error states; the selected planet is visually marked.
F5 — PWA (was Phase 33)
Goal: the web build is installable and offline-tolerant. Depends on F6 (wasm in CI) so the service worker caches a freshly-built artefact.
ui/frontend/src/service-worker.tscache-first assets with stale-invalidation on app update;manifest.webmanifest;ui/frontend/static/icons/web icon set;ui/docs/pwa-strategy.md.
Acceptance: installs as a PWA on Chrome, Edge, and iOS Safari; the SW survives an app update without serving stale code. Tests: Lighthouse PWA ≥ 90; Playwright install→offline→cached-login; version-bump invalidation.
F6 — Build hygiene: build core.wasm in CI
(From the PLAN.md TODO; timely — the binary is currently committed and must be rebuilt by hand on every Go-bridge change, which has already bitten us.) Goal: stop shipping the binary in git.
- Install TinyGo on the gitea Actions runner (Linux tarball /
curl … | tar -xz, sincebrewis macOS-only); addmake -C ui wasmahead of the Vitest step in.gitea/workflows/ui-test.yaml. - Remove
ui/frontend/static/core.wasmandwasm_exec.jsfrom the repo and re-tightenui/.gitignore.
Acceptance: CI builds the wasm and Vitest/Playwright pass against the freshly built artefact; the binaries are no longer tracked.
F7 — Documentation finalization
Goal: living docs read as current state, not build archaeology.
- De-archaeologize the 20
ui/docs/topic docs: strip "Phase N adds…" history, rewrite as present-tense descriptions of how things work. ui/docs/README.mdindex (added during this reorganization).- Sync
ui/README.md,docs/ARCHITECTURE.md, anddocs/FUNCTIONAL.md(+_rumirror) with the finalized UI.
Acceptance: no stray "Phase N" references in ui/docs/; the index links
every topic doc; READMEs/ARCHITECTURE describe current state.
F8 — Owner manual-QA refinement loop
Open-ended, owner-driven. The owner exercises the whole UI by hand and files small-nuance fixes; each is folded in as it surfaces. There is no fixed acceptance gate here — finalization is "done" when the owner signs off and the client moves on to the ROADMAP platform wrappers.