Portfolio Website

June 2025

A place to showcase some of my hobby projects as I learn more about front-end development.

Next.jsReactTypeScriptTailwind CSS

Project

This is a portfolio website built to showcase my work and learn more about modern web development. Prior to building this website, my only knowledge of front-end development was using Streamlit as well as injecting some basic HTML, CSS and JavaScript along the way. As a Quantitative Developer, this was enough to get by for small projects with a limited amount of internal users. But as we expanded our capabilities and wanted to build more sophisticated dashboards/websites, we began to hit somewhat of a ceiling. This motivated me to want to learn about React. And so with some extra time on my hands, I began the journey.

Learning

Before I could dive into creating the website, I needed to first learn how to build it. After some brief research, it seemed like React would be a good framework to learn given the size and popularity of the community. But I realized that as a prerequsite, I would also need to learn more about HTML, CSS and JavaScript. Luckily for me, I stumbled upon Scrimba which I found to be a great platform for learning more about these languages and framework.

Building

After feeling confident enough with my HTML, CSS, JavaScript and React fundamentals, I was ready to begin building this site. The first thing I did was research best practices around tooling and deployment. I found Vercel, Next.js, Typescript and Tailwind CSS.

  • Vercel for deployment.
  • Next.js for routing.
  • TypeScript for typesafe JavaScript (aka better JavaScript).
  • Tailwind CSS for easy styling.

However, I also need a design to follow. I spent some time playing around with Figma, but ultimately, I really liked and was inspired by a template I found on Wix. With a design in hand, I began building. I setup my environment and started with having an LLM create the boiler-plate code. From there, I spent a lot of time creating and refining what you see here today.

Creating the Homepage

The first thing I needed to do was create the homepage such that it had a two-toned background and containers that would dynamically size with the screen size. Accomplishing the two-toned background was simple enough with a flexbox and setting the proportions (I went with 3/10 and 7/10).

The second thing I needed to do was get the card element on the left and the text element on the right to position correctly with an expanding/minimizing window. This was tricky but I found a way with the following Tailwind CSS:

flex flex-1 items-center justify-center lg:justify-end relative
  • flex creates a flexbox.
  • flex-1 makes sure that the element will take up as much space possible (sharing with other siblings).
  • items-center vertically centers items.
  • justify-center horizontally centers items.
  • lg:justify-end if the screen size is large, item will horizontally align on the right of the container.
  • relative will set the card relative to the absolute position of the other children in the flex. This was the key so that the card element didn't overlap with the text element on the right as well as was still positioned correctly on smaller screens (centered above the text element).

Creating the About Page

The About Page was the easiest page to build. Just four main containers and using flexboxes to organize everything. It was while building this page that I finalized the color scheme. I wanted the body to blend in completely with the header at the top, and then as you scroll down, move towards the beige colors on the homepage. Tailwind CSS made it super easy to do this.

bg-gradient-to-b from-[white] to-[#E6DACE]
  • bg-gradient-to-b sets the background to a gradient and goes from top to bottom.
  • from-[white] defines in this case our background top color.
  • to-[#E6DACE] defines in this case our background bottom color.

One of the fun things I was able to come up with was to have logos inplace of bullet points in the Experience and Education containers. The challenge here was to get the logos to render with the same dimensions and similar resolution. To accomplish this, I went out to the web and found .svg files and loaded them in with consistent dimensions, margins and paddings.

Creating the Project Page

I wanted my Project Page to be a timeline of projects, which upon clicking into, will route to more information about that particular project. To accomplish this, I would store each project in an array. I could then map through the array to render the timeline. But in order for this to work dynamically and for the experience to be consistent, I needed each project in the array to be defined in the same manner. For this, I created the following interface:

export interface ProjectData {
  slug: string;
  title: string;
  date: string;
  description: string;
  technologies: string[];
  overview: React.ReactNode;
  icon: string;
  iconColor: string;
}

The slug, title, date, description, technologies, icon and iconColor would all be strings used to create the container in the timeline--additionally the container would get replicated on the top of the project detail page too. But for the details pertaining to each individual project, I wanted to allow for nuance and customization. So I kept this just a react object under overview.

To handle the dynamic rendering of each individual project, I put a [slug] in the repo. We use [slug] to tell Next.js that the URL will get its value from params.slug (which in this case, gets defined in our interface). I then needed to create an async function so that we could await the params object.

export default async function ProjectDetailPage({ params }: { params: Promise<{ slug: string }> }) {
    let project: ProjectData | null = null;
    try {
    // Dynamic import based on slug
    const { slug } = await params; 
    project = (await import(`@/data/projects/${slug}`)).default;
    } catch (e) {
    console.error("Project import failed:", e);
    notFound();
    }
    ...

The await keyword waits for the params to resolve before rendering the page--params gets resolved whenparams.slug is defined which happens when the proper URL request is made (i.e. .../projects/portfolio-website)

Conclusion

This project was a lot of fun to make and checked off a major personal goal! While I still have a long way to go in my front-development journey, I'm proud of how this turned out and look forward to adding to it and making it even better!