A statically exported portfolio built with Next.js 16 App Router, TypeScript, and MUI v7. The site mirrors the original Flask experience while adding responsive layouts, motion-enhanced cards, and automated testing.
- Framework: Next.js 16 (App Router,
output: 'export') - UI: MUI v7, Framer Motion, Next Font (Inter + Roboto Mono)
- Language & Tooling: TypeScript (strict), ESLint 9 flat config, Prettier 3, Vitest + React Testing Library, Husky + lint-staged
- Static Hosting: GitHub Pages via the
next-static-exportGitHub Actions workflow
npm install
npm run dev # start dev server
npm run lint # eslint --max-warnings=0
npm run typecheck # tsc --noEmit
npm run test # vitest run (jsdom)
npm run test:e2e # playwright test (launches dev server automatically)
npm run lighthouse # run Lighthouse against the static export in /out
npm run build # next build -> static assets in /outCopy .env.example to .env.local and provide:
NEXT_PUBLIC_GTM_ID,NEXT_PUBLIC_ADSENSE_CLIENT– analytics/Adsense IDs.
Because next.config.mjs sets output: 'export', next build creates the deployable out/ directory that the workflow publishes to GitHub Pages.
src/
app/ # App Router routes (Home, About, Contact, My Work, Resumes)
components/ # Reusable UI (SectionCard, HeroSection, EyeSketch, etc.)
data/ # Structured content derived from the original markdown/templates
hooks/ # Custom hooks (e.g., prefers-reduced-motion)
legacy/flask/ # Archived Frozen-Flask implementation + workflows/configs
public/ # Static assets copied from the legacy site
e2e/ # Playwright smoke tests
.github/workflows/next-static-export.yml # Node-based CI/CD pipeline
The entire Frozen-Flask stack (templates, markdown content, static assets, requirements, and the original workflow) now lives under legacy/flask/. This keeps rollback artifacts available without interfering with the new Next.js toolchain.
.github/workflows/next-static-export.yml runs on pushes/PRs to main:
npm cinpm run lintnpm run typechecknpm run testnpm run test:e2enpm run build(emitsout/)- Uploads
out/as an artifact and deploys to GitHub Pages usingpeaceiris/actions-gh-pages@v3when onmain.
-
Color palette and typography honor the legacy monochrome + purple branding
-
All interactive elements keep focus outlines and ARIA labels where needed
-
The legacy p5 eye animation is ported to a client component that automatically disables itself when
prefers-reduced-motion: reduceis detected -
The contact page lists social/email links (no form; direct contact only).
-
Additional implementation notes:
docs/animation-embed-guidelines.md– animation fallbacks, embed rules, cache checklist.docs/badge-strategy.md– reasoning for continuing to use shields.io along with mitigation plans.docs/observability.md+docs/screenshots/README.md– workflow for capturing Lighthouse scores and UI screenshots.
For more detailed migration instructions, see AGENTS.md and docs/migration-plan.md.