Stack
- Astro for static generation, TypeScript strict
- Notion as the content layer (two databases: Case Studies, Resumes)
- Vercel for deployment and SSL
- Geist Variable, self-hosted via Fontsource
- CSS custom properties for the four chromatic themes
Content
Case studies and resumes live as Notion database rows. The build
queries them at compile time and renders Notion's block tree as
editorial prose. Image galleries pull from /public/case-studies/[slug]/.
Drop in a file, it appears.
Resume system
Master plus per-role variants. Each row has Slug,
Status, Tailored For, and Is Default,
plus full Notion content. Master renders at /resume.
Variants at /resume/[slug] for direct sharing.
A new variant for a new role is one new database row. Site picks it
up on next deploy. No code change. Route auto-generates from
getStaticPaths().
Themes
Four palettes scoped to [data-theme] on <html>.
The selector mutates one attribute; the site reflows in one repaint.
Press T to cycle. The cardstock speckle, cursor accent,
tagline rule, and every typographic surface inherit from the same
token set. No theme hardcodes a color.
Resume routes carry a print stylesheet: white bg, black text, letter size, one-page typography. Cmd+P produces a clean PDF.
Source
github.com/ai-tolo/jt-portfolio — public. Built May 2026 with Claude Code on an M3 Air, replacing an aging Framer portfolio.