Portfolio
All 5 versions of this site.
Next.js • Tailwind CSS • TypeScript • CMS • tRPC
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:
-
Markdown files are written inside
content
(subdirectories are ok). -
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 atfragments/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 clearspublic/
and replicates the structure incontent/
, 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 infragments/
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 (inbase.html
)$ {markdown} $
. This template has navigation, a favicon, a title, etc. Our page is now complete and available in its corresponding location withinpublic/
.
-
-
-
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
insidepublic
. 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 astemplates/404.html
) unlesspublic/foo/bar/_root.html
exists, in which case, it will be returned as the default for that path.
Here's what it looked like: