Skip to main content

how i made this portfolio site

00:07:21:06

first thoughts

here are some reasons why I decided on making a portfolio, as opposed to solely remaining on social media:

  • Brand Identity: establish and reinforce a personal or brand identity. I can customize the design and messaging to reflect my unique style and values.
  • Navigation and Legitimacy: provides a centralized hub for showcasing your work in a cohesive manner. Can enhance credibility and make a strong impression on potential clients or employers.
  • Longevity: Social media platforms may come and go, but my portfolio is a lasting digital asset. It provides a stable online presence that I control, even if trends or platforms change over time.
  • Reach and Engagement: Social media platforms can offer broader reach and potential for engagement due to their large user bases and built-in networking features. However, it can be challenging to stand out among the noise and maintain consistent visibility. I can do more to promote visibility of my work on my website than on theirs.

Prior to making a portfolio website, I had been frustrated by the limitations imposed by the contractural nature of the internet. I held an animosity towards all the sterile, neo-feudal gated communities stifling my individual agency and expression. I yearned for a customizable environment to truly call my own.

My portfolio features two kinds of deliverables: projects (longer-term commitments with social impact) and articles (analytical approaches where I document and reflect). I omit any autodidact inititive which does not fit in the two buckets.

Designing my solution

Starting out, I did not have much webdev knowledge outside of HTML/CSS and some React. I was not at all familiar with the ecosystem of javascript frameworks. Though fortunately, like all problems in life, this proved easy-to-learn with difficulty mainly arising when requirements had greater specificity. I had a strong idea of what I wanted in terms of design and website structure, so I wrote it out:

Giving myself constraints

On the landing page and initial scroll down (~10 seconds), the visitor:

  • is greeted with simple TouchDesigner-esque 2000s-era 3D-graphics. They are the main focal point and should appear as part of the background with a level of depth that is just about to reach the foreground.
  • reads my name which features a simple text animation.
  • does not see any top-bar and knows to scroll down, and then is met with an exciting reveal of the 'About Me' section.
  • later discovers that below the 'About Me' section, there is an initial list of three highlighted projects with sharp/jarring visuals. Below these three is a neatly-configured table with all projects.
  • only sees projects initially. The articles are held in their own page and are not displayed alongside the projects.

When experiencing the portfolio:

  • Each page has the exact same tonal shift, with all media eventually shifting from manic 3D-graphics to in-depth gonzo rabbitholes featuring inline images/videos and heavy hyperlinking and references.
  • You have a better understanding of my work, interests, and approaches.
  • You enjoy the narrative-driven journey and can remember each step along the way.
  • You think that certain words/dichotomies appeared in my brain. We both recognize this site is still a work in progress, but some of the terms which motivated the design might be (in no particular order): forward, energetic, fraternal, mechanical, rhythmic, rebellious, abstract, eclectic, sampling, playful, boastful, futuristic, avant-garde, dense, anthemic, misanthropic/depressive, surreal, longing
A scene from Ghost in the Shell (1995) with the Major cloaking with thermoptic camouflage; the poster for Akira; The Matrix's digital rain effect

The first iteration

I was learning React when I first built this website, and while overkill for a personal portfolio site, it was a great opportunity to learn and experiment with learning it. I've found the best way to learn is by actually making something that you intend to use and ship.

The no-brainer choice at the time was Create React App. It has 102k stars on Github! It served me well in getting things up and running without having to fuss about with config. On top of that, I was using Styled Components, Tween.js, and React Transition Group. I was also playing with some early Three.js effects like the displacement sphere that still resides on the homepage.

Since then I've used this website as a playground for experimenting with new tech and techniques, so over time I've overhauled pretty much everything. A big change along the way was replacing images of my work in static mockups with real-time rendered interactive 3D devices using Blender models I found (Clay Mockups 3D Figma plugin).

Migrating to Next.js

With Create React App I was using a somewhat janky and unmaintained package to prerender the site as static HTML in Puppeteer. This worked okay for the most part, but I wanted a more robust solution for posting articles (like this one you're reading) using MDX. I had a half baked version of this lying dormant in the repo, but it never felt good enough to publish. I looked at a few options like Gatsby, Vite, and Parcel, and Remix, but Next.js stood out as the most suited to my needs.

  • The site is now based on Next.js. Is a much better fit than Create React App. For now I'm just using it to create a static export, but maybe I'll add some server rendered stuff in the future.
  • Styling is now vanilla CSS with postcss to add support for the future native CSS nesting and custom media queries features. I'm using CSS modules instead of BEM syntax to avoid style conflicts.
  • For generating pages from .mdx files, I'm using Kent C Dodds' mdx-bundler. In combination with Next.js it makes generating pages from .mdx files really quick and simple.
  • For animation I've moved from Tween.js and React Transition Group to just Framer Motion.
  • 3D effects are still all using Three.js, but I've added three-stdlib as a better maintained replacement for modules from Three's examples.

Not all smooth sailing

For the most part, the migration was pretty straight-forward. The way I has structured the site with React Router lent itself well to conforming with Next.js's file-based routing, and I was already using postcss for styling. I did, however, encounter a couple of problems:

1. Animated Route transitions

There was a bit of a conflict when it came to animated route transitions. Next.js will immediately yank out all of the styles for the previous page when navigating to a new one. This works great when you're not animating between pages because it cleans up any unused styles form hanging around. When you are animating the page transition though, all of a sudden the previous page becomes jarringly completely unstyled as it transitions out. This problem one of the most commented and reacted to issues on the Next.js repo, so hopefully there's a fix soon, but for now I've dropped in a hack to fix things from the issue's comments.

2. Scroll restoration

Somewhat related to the route transitions, I had to opt out of both Next.js's and the native browser's scroll restoration in order to prevent the browser immediately scrolling to the top when the page started transitioning out. Next.js also doesn't appear to handle shifting focus when linking to the id of an element within the page, so I added that in for accessibility.

Criticism/Optimism towards Remix

I eventually migrated the site to Remix now that they've got good support for CSS modules meaning I didn't need to convert all of my styling. It was mostly a process of deleting all of the hacks mentioned above in this post, and things just work and feel more "web standard". I'm now using the CSS view transitions API to handle smoothly crossfading on route transitions, which is a feature baked into React Router (and as a result Remix). I don't need to do weird javascript hacks to try and set the correct theme (which still inevitably led to a flash of unthemed content) - I'm now storing the preferred theme in a session cookie which Remix makes really easy to do.

Remix poses challenges if you have actions and loaders as the main interface for your application. When you create a route in Remix, it can have one loader for that route and one action. But you must colocate both, otherwise you will have to deal with a crazy dependency chain. So if you have a component that needs some data, and its not in the page route, you better hope that data exists wherever that component is mounted! Components without loaders in the same file will silently fail. And it only took the Remix community fighting tooth and nail to get the Remix team to add a 'typeof' to your loader data.

If you have two actions, you need to define new endpoints or have a switch statement to determine what action to do. You define an action in Remix not based in a component but for the route. You hope the relation between component and route stays tightly knit.

Without a solution like TRPC or an abstraction to guarantee the 1-to-1 relationship, these non-deterministic behaviors in Remix are scary.

But overall, Remix was a great choice for a portfolio site, and I'd would totally recommend it. Recently, Mark Dalgleish has joined the Remix team and I consider the framework to be in the best state it has ever been in.

Future areas for this site's improvement

I would like to eventually replace a lot of animations triggered by Javascript with the upcoming scroll driven animations CSS API, but browser support isn't there yet, so maybe some time later this year.