Historical snapshot before consolidation on 2026-10-03. This records inherited choices, not the current Quiz implementation. Read the current record first. Retained for rationale and provenance.
ADR-008 — React + Vite SPA front end, accessible headless components, no SSR¶
Status¶
Accepted (2026-07-03, phase 3).
Context¶
The portal is an authenticated application (teacher, student) with no SEO need whatsoever. Table views must render in under 2 s at 100 rows (NFR-11). The interface ships in French with English addable without a rewrite (NFR-14), dates in Europe/Zurich (C-02), and nine WCAG 2.1 AA criteria are required on the main journeys (NFR-15).
Decision¶
- A React 19 + Vite 7 SPA, served as static files by the monolith: no front-end server to operate, trivial redeployment.
- TanStack Router + Query + Table: cache and invalidation driven by the SSE events (ADR-005), 100-row tables without on-the-fly aggregation.
- Radix UI (headless) as the component foundation: keyboard, focus and ARIA covered by construction — NFR-15 compliance does not rest on a continuous effort.
- i18next with externalized strings (NFR-14); Luxon for Europe/Zurich display (C-02).
- Types shared with the backend and the CLI through the Zod schemas of
packages/contracts.
Consequences¶
- The front end is a folder of static files versioned with the backend: the portal API does not need to be versioned (they are deployed together).
- The acceptance accessibility audit (axe-core, NFR-15) checks a foundation that is already accessible instead of catching up on home-made components.
- SSE reconnection is resolved by a TanStack Query refetch: no duplicated real-time state.
Rejected alternatives¶
- Next.js or SSR: no server rendering is needed (the portal sits behind a login, SEO is irrelevant); it would add a front-end server to operate and a deployment coupling.
- Home-made UI components: a recurring accessibility cost and a permanent risk on NFR-15; all three proposals converged on a headless foundation.
- A Turborepo + pnpm multi-pipeline monorepo setup (productivity proposal): six packages and build pipelines for a one-maintainer project; plain pnpm workspaces are enough for the three shared packages.