Imported from spro047/Portfolio (
AGENTS.md). Install upstream withnpx skills add spro047/Portfolio. Copyright stays with the author.
Retro macOS Portfolio — Agent Guide
Single-page interactive portfolio built as a macOS 9 simulator inside a CRT monitor. Vanilla TypeScript + CSS, no framework.
Commands
npm run dev # Starts Vite (port 5173) + API dev server (port 3001) concurrently
npm run dev:api # API dev server only (http://localhost:3001)
npm run build # tsc (typecheck) → vite build — run BOTH, never just vite build
npm run preview # vite preview of built dist/
npm run vercel-build # Same as build, used by Vercel
Important: npm run dev runs concurrently. It starts both the Vite dev server and the API server. The API server is a Node HTTP server at api/dev-server.mjs on port 3001. Vite proxies /api/* to it.
Architecture
index.html— single entry point, no routingsrc/main.ts— all application logic (~2066 lines): boot sequence, desktop, window manager, terminal, paint, flappy bird, calculator, contact form, blog, calendar, search, screensaver, dock drag-reordersrc/style.css— all styling (~1700 lines). Design tokens are CSS custom properties in:root. Full design system inDESIGN.mdapi/*.js— Vercel serverless functions. Routes:GET /api/health,GET /api/projects,GET /api/research,POST /api/contactapi/dev-server.mjs— local HTTP server that serves the same API routes (port 3001). Used only innpm run devdata/*.json— JSON seed files loaded by API functionsimg/— static images used at runtime (referenced as./img/...)public/img/— Vite static directory, also has image copiesdist/— build output, gitignored
Key Technical Details
- No test framework — no tests to run or update
- Strict TypeScript —
noUnusedLocals,noUnusedParameters,noImplicitReturnsare all on.tscis typecheck-only (noEmit: true). Fix all TS errors beforevite build - No CSS framework — vanilla CSS3, no Tailwind/Bootstrap. All colors and spacing are CSS custom properties defined in
:root buildruns tsc first — if tsc fails the build fails. Runnpx tsc --noEmitto typecheck without building- State is in
localStorage— keys:dark-mode,desktop-wallpaper,user-paints,flappy-high-score,boot-time,page-views. There is no backend database - No HTTP state — contact form POSTs to
/api/contactwhich logs to console (dev) or Vercel logs (prod). OptionalCONTACT_EMAIL_WEBHOOKenv var forwards to email - Window manager — vanilla drag/drop/resize/z-index. Global window control functions exposed via
(window as any)for inlineonclick:minimizeWindow(id),maximizeWindow(id),closeWindow(id) - Images referenced as
./img/...from JS and HTML — paths resolve through Vite's dev server during development
Vercel Deployment
- Config:
vercel.json—framework: null(custom), build vianpx tsc && npx vite build - Output dir:
dist - API rewrite:
/api/(.*)→/api/$1(Vercel servesapi/*.jsas serverless functions) - Asset caching: 1-year immutable for
/assets/*and/img/*
Style Rules
- Use CSS custom properties from
:rootfor all colors — never hardcode color values - Prefer
transform/opacityanimations over layout-triggering properties - No emoji for UI icons (use HTML entities or images)
- If adding new JS logic, keep in
src/main.ts(single-file app); if it grows large, extract modules intosrc/
Design Reference
DESIGN.md documents the complete design system: colors, typography, spacing, component specs, animation values. Consult it before styling changes.