53fb4f5f76
The de-archaeology and the ui/docs index landed in the earlier reorg (0 "Phase N" refs in ui/docs/; 24 topic docs linked). This finishes the sync: ui/README.md gains a finalized-web-target summary and links the new topic docs (design-system, a11y, error-state-ux, pwa-strategy). docs/ARCHITECTURE.md and docs/FUNCTIONAL.md need no change — they cover the backend/gateway/cross-service contracts with no stale UI statements; the finalized UI is client-local UX documented under ui/docs/. Marks F7 done. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
209 lines
9.9 KiB
Markdown
209 lines
9.9 KiB
Markdown
# UI Client — Finalization Plan
|
||
|
||
The MVP web client (Phases 1–30, [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) — done
|
||
|
||
Installable, offline-tolerant PWA on SvelteKit's native service worker
|
||
(`src/service-worker.ts`): a version-keyed cache precaches the shell,
|
||
build artefacts (incl. `core.wasm`), and static files; `activate` purges
|
||
old caches; the gateway is never intercepted. Adds
|
||
`static/manifest.webmanifest`, a generated placeholder icon set
|
||
(`scripts/gen-pwa-icons.mjs`, dependency-free), and the manifest /
|
||
theme-color / apple-touch tags in `app.html`. Gated by Playwright against
|
||
a production preview (`pnpm test:pwa`, wired into `ui-test`): manifest +
|
||
installable icons, SW registration + single version-keyed cache, and
|
||
offline shell load. Docs: `ui/docs/pwa-strategy.md`.
|
||
|
||
**Acceptance correction:** "Lighthouse PWA ≥ 90" is dropped — Lighthouse
|
||
removed the PWA category in v12 (current line 13.x), so it is no longer a
|
||
valid gate; the Playwright checks verify the same install/offline
|
||
behaviour directly.
|
||
|
||
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 — done
|
||
|
||
`core.wasm` / `wasm_exec.js` are no longer committed (untracked +
|
||
gitignored). A reusable composite action
|
||
`.gitea/actions/build-wasm` installs TinyGo (`actions/cache`d) and runs
|
||
`make -C ui wasm`; it is invoked by **all three** frontend-building
|
||
workflows — `ui-test` (before Playwright; Vitest needs no build, it uses
|
||
the fake Core), `dev-deploy`, and `prod-build` (which build the bundle on
|
||
the runner via `pnpm build`, then package it). `ui-test` gained a Go
|
||
setup; the deploy workflows already had one. Docs:
|
||
`ui/docs/wasm-toolchain.md`, `ui/README.md`.
|
||
|
||
|
||
|
||
(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 — done
|
||
|
||
The de-archaeology + the `ui/docs/README.md` index landed in the earlier
|
||
docs reorganization (PR #25): `ui/docs/` has **0** stray "Phase N"
|
||
references and the index links all 24 topic docs (each F1–F6 stage also
|
||
added/updated its own topic doc). This stage finished the sync:
|
||
`ui/README.md` gains a finalized-web-target summary and links the new
|
||
topic docs (design-system, a11y, error-state-ux, pwa-strategy).
|
||
`docs/ARCHITECTURE.md` and `docs/FUNCTIONAL.md` need no change — they
|
||
cover the backend / gateway / cross-service contracts and carry no stale
|
||
UI statements; the finalized UI is client-local UX documented under
|
||
`ui/docs/`.
|
||
|
||
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.
|