All 5 versions of this site.

Next.js • Tailwind CSS • TypeScript • CMS • tRPC

All 5 versions of this site.

What's This Site For?

This is my personal portfolio site. I use it to document my progress as a developer, host novel APIs for quick projects, and provide information on who I am and how to contact me.

I've updated this site several times over the past few years (5 major revisions, to be exact). Each time, the UX gets dramatically better and, in my opinion, the site looks a lot better. The first two iterations were pretty rudimentary, but since then the high-level feature set has been the same.

Version 5 (2024, Latest)

This is the site you're on! Here's the tech stack used:

  • Next.js (App Router)
  • Tailwind CSS
  • Sanity CMS
  • TypeScript

You can view the source code on GitHub: portfolio-5.0 .

I upgraded from the previous version because I wanted an intuitive interface that allowed me to publish content and update the site without necessarily pushing GitHub commits. And, using my own components with Tailwind enabled meant I could personalize the site with my design style more easily.

Version 4 (2022)

This version of the site was created because I wanted more stability in my serverside framework. I leveraged Next to use UI frameworks (Geist UI) to standardize my design systems and implement SSR easier. Here's what it looked like:

Version 3 (2021)

This is the first version of the site that stuck around for more than a year.

I wanted a site that made it simple to write content (like this). I wanted to write in Markdown, and I found out that I could use something like Hugo, but I felt that it was too overcomplicated. Instead, I made this site. This'll make more sense if you check out the source code on GitHub at @http-samc/portfolio-3.0. Here's how it works:

  1. Markdown files are written inside content (subdirectories are ok).

  2. node build is called, which does the following:

    • Run the custom project hook. This looks reads the files in the content/projects directory and extracts a projectName and projectDescription from them. Then, it creates a html fragment at fragments/projects.html that is prestyled and contains a list of all my projects (this is what you see here or here)).

    • Next, the standard render() function is called. This clears public/ and replicates the structure in content/, but changes {name}.md to {name}.html when replicating. The actual conversion is done by marked.js.

      • Before the html is fully converted, the function scans for the special placeholder operator $ {...} $. The ... represents a path to an html fragment, which gets opened and replaces the special operator. The reason this exists is to keep the Markdown simple to read and enjoyable to type. Any complex html can be left in fragments/ and will make it to the static page at runtime.

      • The resulting html is still a fragment - it needs a title, resource files, etc. It is added to templates/base.html by replacing the special placeholder operator (in base.html) $ {markdown} $. This template has navigation, a favicon, a title, etc. Our page is now complete and available in its corresponding location within public/.

  3. Use node app to start the Express.js server, which handles all routing dynamically. Pages can be accessed based on their relative path once already inside public. Your homepage can be accessed with _root.html inside public. This works for branches too: public/foo/.../bar can have a bunch of files that can be acceesed at /foo/.../bar/{name}, but accessing /foo/.../bar directly will result in a 404 (sent as templates/404.html) unless public/foo/bar/_root.html exists, in which case, it will be returned as the default for that path.

Here's what it looked like: