# ADR-0002: Next.js + React + TypeScript + Tailwind/shadcn for the Frontend - **Status:** Accepted - **Date:** 2024-06-01 - **Deciders:** FablePool core team - **Related:** ADR-0006 (content format), ADR-0009 (widget sandboxing), ADR-0016 (KaTeX), `docs/architecture/09-accessibility-i18n-bandwidth.md` ## Context The frontend must render rich educational content (Markdown/MDX with math, diagrams, code), host interactive problem widgets, and remain fast on low-bandwidth mobile connections — explicit goals in `docs/architecture/09-accessibility-i18n-bandwidth.md`. It must also be SEO- friendly: published problems and courses are public, shareable open educational resources whose discoverability drives community growth. Forces: - **Server rendering matters.** Content pages must be readable without waiting for a large JS bundle, and crawlable for search engines. - **Per-route code splitting matters.** The problem editor (TipTap, schema validation, diff views) is heavy; learners should never download it. - **Component ecosystem.** We need accessible primitives (dialogs, menus, tabs) without building them from scratch; shadcn/ui (built on Radix) provides WAI-ARIA-correct components we own as source. - **Contributor familiarity.** React + TypeScript is the largest frontend contributor pool. ## Decision Use **Next.js (App Router) + React + TypeScript**, styled with **Tailwind CSS** and **shadcn/ui** components, deployed as a self-hostable Node server (no proprietary-platform-only features). Specific commitments: 1. **Server Components by default.** Content pages (problem view, course pages, lesson reader) are rendered on the server; client components are limited to interaction islands (answer inputs, widgets, editor). 2. **Strict bundle budgets** (enforced in CI): ≤ 80 KB gzipped JS for the problem-solving route excluding widgets; widgets load lazily inside the sandbox frame and are billed to their own budget. 3. **No Vercel-exclusive APIs.** Everything must run via `next build && next start` in Docker so self-hosters are first-class. 4. **shadcn/ui as vendored source**, not a dependency — we copy components into the repo and adapt them for our accessibility and theming needs, which suits an AGPL project (no hidden runtime dependency drift). 5. **MDX rendering is server-side** using a controlled component registry (see ADR-0006); arbitrary JSX from contributors is never executed. ## Alternatives Considered - **SvelteKit** — smaller bundles, excellent DX, but a meaningfully smaller contributor pool and a thinner accessible-component ecosystem. Rejected. - **Plain Django templates + HTMX** — superb for low bandwidth and would collapse the stack to one framework, but interactive problem widgets, the rich editor, and offline-tolerant practice sessions push hard against a server-rendered-HTML-only model. Rejected for the main app; we keep the option open for ultra-light "reader mode" pages later. - **Remix** — comparable to Next.js; Next chosen for ecosystem size, MDX tooling maturity, and contributor familiarity. Rejected on tie-breakers. ## Consequences - ✅ SEO-friendly, fast first paint on content pages; heavy tooling stays off the learner path. - ✅ Accessible primitives we control in-repo. - ⚠️ Two languages in the stack (Python + TypeScript); CI runs two toolchains. Accepted — the API contract (`10-api-design.md`) plus a generated TypeScript client keeps the boundary honest. - ⚠️ App Router churn risk: we pin to a stable major and upgrade deliberately, with the bundle-budget CI catching regressions. - ⚠️ Bundle budgets require ongoing enforcement; a `size-limit` CI job is part of the frontend's definition of done.