Files
galaxy-game/ui/PLAN-finalize.md
T
Ilia Denisov 8dcaf1c6c6
Tests · UI / test (push) Waiting to run
Tests · UI / test (pull_request) Failing after 7m13s
feat(ui): error & state UX — error surface, view states, map selection, sheet gestures (F4)
- 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>
2026-05-22 13:29:11 +02:00

170 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# UI Client — Finalization Plan
The MVP web client (Phases 130, [PLAN.md](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](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/` (or `app.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.md` documenting 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.md` audit 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.ts` enforcing 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.ts` cache-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`, since `brew` is macOS-only); add `make -C ui wasm`
ahead of the Vitest step in `.gitea/workflows/ui-test.yaml`.
- Remove `ui/frontend/static/core.wasm` and `wasm_exec.js` from the repo
and re-tighten `ui/.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.md` index (added during this reorganization).
- Sync `ui/README.md`, `docs/ARCHITECTURE.md`, and `docs/FUNCTIONAL.md`
(+ `_ru` mirror) 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.