Every portfolio site is a chance to eat your own cooking, so this one is built with the same tools I like using on real projects — with one constraint that shaped everything: no backend, ever. And since machine learning and security are what I do, the design leans into both: a living neural network in the hero, ciphertext that decrypts as you scroll, and deterministic hash art everywhere else.
The stack
- Next.js (App Router) with
output: "export"— the whole site compiles to plain HTML, CSS and JavaScript - Tailwind CSS v4 for styling, with light/dark themes defined as design
tokens in
globals.css - An interactive neural-network canvas on the hero — plain Canvas API, zero libraries. Signals run forward passes, the pointer excites nearby neurons, and adversarial (red) pulses get killed mid-network
- Markdown posts parsed at build time with
gray-matterandmarked - GitHub Actions builds and deploys to GitHub Pages on every push
Why static export
A personal site doesn't need a server, and not running one means there's nothing to patch, nothing to pay for, and nothing that can fall over. The blog is just markdown files rendered at build time — writing a new post means adding a file, not maintaining a CMS.
The one wrinkle with GitHub Pages is that project sites live under a sub-path
(/<repo-name>/). The build handles that with a BASE_PATH environment
variable, so the same code runs at the root locally and under the sub-path in
production.
Editing content
All the site content lives in the content/ directory:
| File | What it controls |
|---|---|
content/site.ts |
Name, bio, email, social links |
content/projects.ts |
The project cards grid |
content/experience.ts |
The work-history timeline |
content/blog/*.md |
Blog posts |
Change a file, push to main, and GitHub Actions ships it a minute later.