Rewriting My Personal Site in a Weekend

Why Rewrite?

The last time I created a personal site was back in 2018. I had just graduated from college and decided I did not want to be an Electrical Engineer. My first site was a simple static site built with GatsbyJs and self-hosted on a Raspberry Pi. My goal then was to create something completely from scratch. I had done some web development in my internships, but I wanted to build something to gain a better understanding of how everything worked from the ground up. This let me explore a few different avenues of web-based software engineering and learn what areas I enjoyed and disliked.

I maintain a blog for Forsea and have been enjoying writing posts for the app. There are a lot of topics that would not be appropriate for that forum, so I wanted to add that functionality to my own site. Some of my friends enjoy reading the Forsea posts, but blogging has been a great way to document my thoughts and learnings over time, and I wanted to continue that for my own personal development. As I set out to implement it on the old site, I realized the look had become dated and it could really benefit from some housekeeping.

I decided to set out on a little challenge. For this project, I wanted to develop a platform for myself that I could continue to build and experiment on. The development of Forsea takes up most of my free time these days, so I needed to complete this project in as little time as possible. My weekend seemed relatively free, so I decided to try and complete the whole thing as a weekend project to test my ability to prioritize tasks, work decisively, and deliver a project in a short time.

The Plan

I committed to this project on Thursday night and spent some time planning everything out before I went to sleep. I wanted to keep the scope of the project small but aimed to add some new features and refresh some of the old content.

Tasks to be Done

  • Design: I wanted to give the site a more modern look while keeping it simple and clean.
  • Theming: I aimed to add the ability to switch between light and dark mode, defaulting to a user's system preference.
  • Blog: Part of the inspiration for this project. I wanted to create a blog section for me to document my thoughts and learnings.
  • Refresh the Old: I wanted to retain some of the content from the old site but give it a fresh look and feel. Two areas I wanted to refresh were the Resume and Film sections.
  • Containerization: Containerize the site with Docker for easy deployment and maintenance.

After spending some time thinking about the tasks I needed to complete, I moved on to planning out my tech stack.

Frontend

For my frontend, I decided to use NextJs. Using Next was a no-brainer for me as I have been using it exclusively for the past year with Forsea, and I love developing with it. Are there other frameworks that could have been better for this project? Most definitely, but I am most familiar with Next, and speed was the name of the game with this project. While I may not be getting the most out of Next right now, there may be some features I implement in the future that the framework would readily support.

For styling, I went with Tailwind. Styling is one of my least favorite parts of development, but Tailwind makes it easy to quickly style sites without having to wrestle with CSS. I am not sponsored by them, but the TailwindUI library is well worth the money for the amount of time you will save developing with it. It even provides some templates that are a great starting point for a project; I used the Spotlight template for this project.

Using a template felt a little bit like cheating, and I wasn't particularly thrilled about using one, but I really didn't have the time to design something from scratch, and it was close enough to the design I had envisioned. There were a few things I changed with the way they structured the project, some other things behind the scenes, as well as adding my own personal theming, but it gave me a good starting point for a basic look and feel.

Backend

My first site was self-hosted. With self-hosting, you don't have to pay monthly fees for hosting, and you have full control over the server. That is also one of the drawbacks of self-hosting. You must do everything yourself from setup to security, to maintenance. If your network has problems, then your site will have problems. If the power is out or a tree breaks a network cable, your site is down. I looked into using a cloud-based solution for hosting, but I wasn't looking to spend any money on this project.

I use AWS for Forsea, but it would be unbelievably overkill for this project. I spent a fair amount of time looking into different cloud providers that specialize in hobby projects like this and their free tiers. There have been some horror stories lately of massive cloud bills due to DDoS attacks or misconfigurations. I want to kind of set and forget the deployment of this site and not have to worry about my bank account being deleted by a botnet.

A major disadvantage of the free tier VMs is that they hibernate after a certain amount of time of inactivity. This leads to a problem known as cold starts, where the first user to return will have a much longer than usual load time as the server wakes up.

As it currently stands, this site receives a whopping 10 visitors a month. The free tier VMs usually hibernate after 5 minutes of inactivity, so every visitor would encounter a cold start and wait 5-10 seconds for the server to wake up. That would be a dreadful user experience, so I decided to keep self-hosting the site for now.

I containerized my site with Docker and used my one free private repository on Docker Hub to store the image. This way, I could deploy the site anywhere I wanted with minimal effort and upkeep. I may have some downtime if my network goes down, but I can live with that.

Problems Encountered

No project ever goes swimmingly, and this was no exception. Most of the problems I faced on this project weren't technical, as I had a good idea of what I was getting into with this project.

Life

When I committed to the project, my only commitment for the weekend was to hang out with my family on Sunday for Mother's Day. Life has a funny way of getting in the way of things. My manager and director at my day job were pinging me all weekend about debugging a project, so I had to spend a few hours Saturday and Sunday working on that. Forsea also had an interesting bug surface on the backend that I needed to address as well. My free weekend was suddenly significantly less free than I had anticipated.

Home Page Photos

I have a small gallery of photos on the homepage that highlight me and some of my hobbies. The template I used displays the images fully on desktop and hides the overflow on mobile devices. I noticed that only one image was really visible on mobile devices and wanted people to be able to view all of them.

At first, I simply changed the overflow-hidden attribute to overflow-x-scroll. This worked on Safari-based browsers, but there was an issue on Chrome and Firefox. On Safari, I could easily scroll left and right on the images, but on Chrome and Firefox, I could only scroll right, and the images hidden to the left were not viewable.

The bane of any front-end developer's existence is browser compatibility. In a surprising twist, Safari was the one that was working correctly. I tried a bunch of different things to get it to work but found that flex's justify-center can cause problems for children with relative and absolute positioning.

My solution was to make the margins auto; this solved my scrolling issue, but now the images were no longer centered. I added a ref to my image container and the image I wanted to be centered and used a useEffect to scroll the image container to the center of the image.

useEffect(() => {
  if (containerRef.current && imageRef.current) {
    const container = containerRef.current
    const image = imageRef.current
    const gapWidth = parseFloat(getComputedStyle(container).gap)
    const paddingWidth = parseFloat(getComputedStyle(container).paddingLeft)
    const scrollPosition =
      (image.offsetWidth + gapWidth) * 2 -
      container.offsetWidth / 2 +
      image.offsetWidth / 2 +
      paddingWidth
    container.scrollLeft = scrollPosition
  }
}, [])

To properly center the image, I had to account for the gap between the images, the padding of the container, and the width of the image. It's a bit of a hacky solution, but it's working on all browsers.

Resume Logos

When adding my work history to the resume section of the site, I had a hard time finding logos for some of the companies I had previously been employed at in SVG format. Luckily, logos have been on a trend of being more simplistic, so it was easy for me to vectorize them in Adobe Illustrator.

One thing I overlooked had to do with the light and dark mode theming of the site. Two of the logos were dark, and when the site was in dark mode, they were pretty much invisible. Passing a fill color based on the theme wasn't working as I expected, so I took the lazy route and just made a dark mode variant of the logos to switch between.

Writing

Believe it or not, one of the most challenging aspects of this challenge wasn't writing code, but writing content. The About section of the site was the most difficult for me to write. Finding the right tone and balance of personal and professional was difficult for me. I found myself rewriting this section multiple times over the course of the weekend before I had something I was okay with publishing.

The topic for my first blog post was a no-brainer, as I figured this challenge would be the perfect topic to write about. I knew the general idea of what I wanted to write about, but often found myself restructuring the format of this post. I have a tendency to go off on tangents and often have to reel myself back in.

Old Service Workers

One issue I knew I would face was something I dealt with for Forsea in the form of old service workers. Gatsby had support for running the site as a progressive web app (PWA). PWAs help provide some native (app store) app-like functionality to a web application through the use of service workers. When I built my old site, PWAs were trendy, and I thought it would be cool to show that I knew how to build them despite not having features that would benefit from it. Unfortunately, I didn't think about the implications it would have for me down the line.

When moving away from Gatsby, old users would still have the service worker installed and would not be able to see the new site. The domain is essentially 'infected' unless you have a new service worker install a new version of the app. In my case, I was de-PWAing the site, and I had to remove the old service workers.

Fortunately (Unfortunately?) for me, I have run into this issue before. Forsea's original landing page and blog used Gatsby under the hood, and users that visited those sites were also infected. This was just a simple matter of reusing the code I had written for Forsea, which I have shared below.

if ('serviceWorker' in navigator) {
  console.log('Removing service worker')
  navigator.serviceWorker.getRegistrations().then(function (registrations) {
    for (let registration of registrations) {
      registration.unregister()
    }
  })
}

self.addEventListener('install', function (e) {
  self.skipWaiting()
})

self.addEventListener('activate', function (e) {
  self.registration
    .unregister()
    .then(function () {
      return self.clients.matchAll()
    })
    .then(function (clients) {
      clients.forEach((client) => client.navigate(client.url))
    })
})

Gatsby's service workers are a bit like trying to get rid of bed bugs; you have to make sure you get them all. We first check for the existence of any registered service workers and then unregister them. We then add event listeners on the install and activate events to ensure that no old workers remain active on the new site. If we encounter a new one, we unregister it and force a reload of the page.

We place this code in a sw.js file in the public directory of the NextJs project. When the site is loaded, the browser will detect this file as a service worker and run it.

I was pretty annoyed the first time I encountered the issue that enabling that feature would forever 'infect' a domain with it, but I guess I was a niche use case of going from a PWA-enabled site to a non-PWA site.

Conclusion

In the end, I was very happy with the outcome of this little experiment. Despite some setbacks from my day job and my unpaid job, I was able to complete and deliver the new site by Sunday night. I had maybe hoped to get some additional features added in, but I finished what I had set out to do.

Not every project has to be a massive undertaking, and sometimes it's important to take little breaks from the big projects to work on something small and fun. I plan to continue to add to this site over time. One of the reasons I wanted to rewrite this site was to have a platform to experiment with new technologies and ideas. There are a few features I want to implement in Forsea, but I want to test them out on a smaller scale before implementing them in the app.

I hope you enjoy the new site, and I look forward to writing more posts in the future!