A fast, prerendered personal portfolio β introduce Β· showcase Β· share
A small, fast personal site that introduces who I am, walks through my experience and toolbox, and lets you read or download my CV. Every route is prerendered to static HTML, so it loads instantly and shares cleanly on LinkedIn and in search results β tuned for performance, accessibility, and SEO, with the numbers gated in CI by Lighthouse.
Live at profile.mitya.dev.
- For developers β What it is Β· Quick start Β· Architecture Β· Tech stack Β· Scripts Β· Testing Β· CI Β· Deployment Β· Docs
A personal portfolio and CV site. The landing page opens with an animated grid background and a
typing intro; from there, About, Experience, and Toolbox sections introduce my
background, and a dedicated /cv page renders an inline PDF preview with a download link and a
branded share card. It's deliberately small and fast β single-page React, prerendered to static
HTML, with per-route SEO metadata so each page shares cleanly.
npm install
npm run dev # Vite dev server on http://localhost:5173| Command | URL | Notes |
|---|---|---|
npm run dev |
http://localhost:5173 | hot-reloading dev server |
npm run build |
β | type-check, build, then prerender routes to static HTML |
npm run preview |
http://localhost:4173 | preview the production build locally |
Requirements: Node 22 (matches the Dockerfile and CI). No runtime configuration β the site is fully static once built.
A single-page React app built by Vite, then prerendered to static HTML per route by
react-snap (the postbuild step). The static dist/ is served by Nginx in a Docker image; no
server runtime, no API. Per-route <head> metadata is set with react-helmet-async from a
single SEO config (src/seo/config.ts).
flowchart LR
Dev["Vite dev :5173"]
Build["vite build"]
Snap["react-snap<br/>prerender per route"]
Dist[("dist/ Β· static HTML")]
Nginx["Nginx (Docker)<br/>profile.mitya.dev"]
Build --> Snap --> Dist --> Nginx
.
βββ src/
β βββ pages/ # intro Β· theBuzz (about Β· experience Β· toolbox) Β· cv Β· notFound
β βββ components/ # UI Β· animated grid backgrounds Β· navbar Β· prompter Β· SEO head
β βββ seo/ # single-source SEO config + structured data
β βββ routes.tsx # route table (lazy-loaded for bundle splitting)
β βββ main.tsx # app entry
βββ scripts/ # prerender.cjs (postbuild) Β· og-image.cjs
βββ tests/ # Playwright e2e: flows Β· content Β· accessibility Β· layout
βββ public/ # CV PDF, share cards, icons, robots.txt, sitemap.xml
βββ docs/ # performance baseline + working specs/plans
βββ nginx.conf # production serving (caching, inline CV)
βββ Dockerfile # build β static β Nginx image
| Layer | Tech |
|---|---|
| UI | React 19 Β· React Router 7 Β· react-helmet-async (per-route meta) |
| Styling | Tailwind CSS 4 Β· @fontsource/poppins |
| Build | Vite 6 Β· TypeScript 5 Β· react-snap (static prerender) |
| Tests | Vitest 4 (unit) Β· Playwright (e2e) Β· Testing Library |
| Quality | ESLint Β· Prettier Β· Husky Β· Lighthouse CI |
| Serving | Nginx Β· Docker Β· Node 22 |
| Command | What it does |
|---|---|
npm run dev |
Start the Vite dev server |
npm run build |
Type-check, build, and prerender routes to static HTML |
npm run preview |
Preview the production build locally |
npm run lint |
Run ESLint |
npm run format |
Format with Prettier (format:check to verify) |
npm run test:unit |
Run unit tests (Vitest) |
npm run test:e2e |
Run end-to-end tests (Playwright) |
npm run test |
Run unit and e2e tests |
npm run lhci |
Build and run Lighthouse CI |
npm run test:unit # Vitest unit tests (jsdom + Testing Library)
npm run test:e2e # Playwright e2e against the dev server (auto-started)
npm run test # bothPlaywright drives the real app on http://localhost:5173 (it boots npm run dev itself) and
covers the intro flow, navigation and routing, content/SEO presence, keyboard accessibility, and
responsive layout β see tests/.
GitHub Actions (.github/workflows/deploy.yml) runs on every push
to master and on pull requests, in parallel jobs:
- quality β
npm ci, ESLint, and Prettierformat:check. - build β full
npm run build(type-check + Vite + prerender). - unit-tests / e2e-tests β Vitest and Playwright, each uploading JUnit results (and the Playwright HTML report) as artifacts.
- test-report β aggregates the JUnit artifacts into a single test summary.
Lighthouse CI gates performance, accessibility, best-practices, and SEO scores β see
lighthouserc.json and the recorded baseline in
docs/performance-baseline.md.
npm run build outputs prerendered static HTML to dist/. The Dockerfile builds
that in a node:22-alpine stage and serves it from nginx:alpine using
nginx.conf (gzip, long-lived asset caching, the CV served inline). Deployed to
profile.mitya.dev.
- Performance baseline:
docs/performance-baseline.mdβ Lighthouse scores + the CI gates that lock them in. - Design specs & plans:
docs/superpowers/β working notes for the responsive UI, intro overlay, theming, Lighthouse setup, and testing layer.
Released under the MIT License β see LICENSE.