This Site
- Year
- 2026
- Role
- Sole developer
- Status
- live
A portfolio you browse as a Linux filesystem — employment history lives in /var/log/career and is searchable with grep.
- React
- TypeScript
- Vite
- Tailwind
- Node.js
Problem
My old portfolio website is outdated and the actual page is built with fundamental basic but not fancy html, javascript and css. Given my time in my new role I needed to update my website, but thought it was time to create a new more appropriate portfolio website to where I am in my career now.
Approach
The site presents itself as an SSH session into a VPS. Career history is written as log files under /var/log/career, skills are executables in /usr/local/bin where file size encodes proficiency and mtime is when it was last used, and projects live in /srv/projects.
The shell is hand-rolled rather than xterm.js, which targets real terminals and would have blocked the requirement that mattered most: every path the shell prints is clickable, so a visitor who will not type can still reach everything. It has a real tokenizer with quoting and pipelines, tab completion, command history and a filesystem walker, so grep -i automated /var/log/career/* genuinely searches every job at once.
The hard constraint was that the metaphor must never cost a visitor the information. There are two modes reading one content source: the terminal, and a conventional site reached with startx. Mobile, reduced-motion and anything without JavaScript get the conventional site by default. Every route is prerendered to static HTML, so crawlers and CV scrapers see full text rather than an empty shell.
Content is authored once in /content and compiled into both a virtual filesystem for the terminal and structured data for the GUI. A validator fails the build on placeholder text, invalid dates and broken image references.
React, TypeScript, Vite and Tailwind, built to static files.
The one before this
The 2024 portfolio is still up, and worth leaving up. It was a conventional single-page site: sections for skills and projects, and a contact form.
Nothing was wrong with it. It just was not up to date and felt like I could build something a lot better with my new skills these days.
Outcome
Around 85 KB of JavaScript gzipped, with the entire shell costing five of them. Boot sequence runs in 2.2 seconds and is skippable from the first frame.