# TECH_SPEC.md Phase 2 — technology stack evaluation and recommendation. No implementation code; decisions + justification. --- ## 1. Constraints that drive every choice 1. **The site is a work sample.** Core Web Vitals, a11y, and view-source cleanliness are product requirements (Phase 1, P2 persona). 2. **Content-heavy, interaction-light.** ~10 pages × 2 languages, one form, no user accounts, no server state. Motion is CSS-grade (Phase 1 motion system: transform/opacity only, one signature animation). 3. **Self-hosted.** Deploys to Connor's own infrastructure (Docker, nginx reverse proxy, Gitea at git.cesnimda.uk). No vendor lock to Vercel/Netlify. 4. **Bilingual first-class.** EN root + `/no/` localised slugs, hreflang, per-language OG (Phase 1 IA §5). 5. **Maintainable by one person** who works daily in C#/.NET + React/TypeScript. ## 2. Frontend framework ### Recommendation: **Astro 5.x (TypeScript, strict)** | Criterion | Astro | Next.js (App Router) | Plain Vite + React SPA | |---|---|---|---| | Output for this content shape | Static HTML, zero JS by default — ideal | Static export possible but carries React runtime (~85 kB+) for no interactive need | SPA: worst SEO/first-paint profile — rejected outright | | i18n routing (path prefix + localised slugs) | First-class built-in (`defaultLocale` root, per-locale routes), content collections pair cleanly | Requires middleware/next-intl wiring; static export + i18n routing is the App Router's weakest combination | Manual everything | | Performance ceiling | LCP = HTML+CSS only; 100/100 realistic | Good, but hydration cost is structural | Poor | | Islands for the 3 interactive spots (lightbox, form, nav overlay) | Astro islands — ship JS only where needed | Everything is React | Everything is React | | Docker/static hosting | Builds to `dist/` static files → nginx serves them; trivial, rock-solid | Static export OK; SSR mode would need a Node runtime container for no benefit | Static, but SPA routing needs nginx fallbacks | | Skill-signalling for Connor | Shows breadth; both project case studies already prove React | Matches CV React keyword | Matches React | | Longevity/maintenance | Content-site sweet spot; low upgrade churn | Heavier upgrade treadmill | Low-level maintenance burden | **Decision: Astro.** The two case studies already prove React competence; the portfolio itself should prove *judgement* — choosing the right tool for a content site is exactly the "key decisions & trade-offs" thinking the site preaches. This decision itself becomes a line in the site's "how this site works" meta page. **Interactivity model:** zero-framework islands. The three interactive components (mobile nav overlay, screenshot lightbox, contact form) are small enough for vanilla TypeScript modules/custom elements inside Astro components — no React island needed. If any grows past ~150 lines of imperative DOM code, promote it to a Preact island (3 kB) rather than React (Phase 3 escape hatch, pre-authorised here). ## 3. Styling system ### Recommendation: **Tailwind CSS 4.x + CSS custom properties for the token layer** - The Phase 1 design system is token-complete (colour, spacing, type, radius, motion). Tokens live as **CSS custom properties on `:root`/`[data-theme]`** — the single source of truth; Tailwind consumes them via its `@theme` mechanism. This gives: theme switching by attribute swap (no JS re-render), tokens inspectable in dev tools (P2 will look), and Tailwind's utility ergonomics without hardcoded values. - Component-scoped styles (Astro's scoped `