IntroductionFormContentv0Claude CodeStyle SystemComponentsTech Stack

Building This Site

January 29, 2026•10 min read
Web DevelopmentAI ToolsDesign

Introduction

I'd be lying if I said this website was a cool side project.

I have long needed a way to better represent myself and my thoughts online. What I didn't expect was to find myself thinking so much about who I was in trying to do so. From the stylistic choices of font and shape to the opener of this post, I found myself thinking:

What do I want people's first impression of me to be?

Thanks to the advice and guidance of a good mentor I had a good perspective on what a good online presence could look like but had yet to create mine. This is the story of creating my website and my framework for my digital self.

Form

So, I thought of that question, and realised it came down to showing off what I have done, my thoughts along the way and the sources of those thoughts.

So the site took that form:

  • JSL: was my elevator pitch
  • Time: was what I have done
  • Create: was my thoughts and ideas along the way
  • Consume: was the source of those thoughts

Content

Alongside the creation of this website came the creation of a solid vision of who I think I am, something I talk a bit more about in my post: In motion. Ultimately, I have decided that I really want to be a futuristic historian.

What's that? Someone that knows enough about the past (Consume) to make the future (Create).

Create

I like writing, a lot more than I thought. So I want to write more, until I like it a bit less.

This will come into two main forms right now:

  • Writing: Shorter form pieces that are the result of short term thinking
  • Research: Long form pieces that are the result of long term strategic thinking and information retrieving; with the goal of generating really impactful insight

I won't restrict myself to it, but I really want to start off research about tech. Specifically AI, since that's what I work with at Middle Ground and seem to be surrounded by all the time. Might change.

Consume

Modern estimates of internet data are unimaginably large.

Text content:

  • The indexed web contains roughly 50 to 60 billion pages
  • Somewhere between 50 to 100 petabytes of text is publicly accessible online
  • For comparison, all books ever written total around 100 to 150 million titles, amounting to only 100 to 200 terabytes of plain text

Video content:

  • YouTube alone hosts over 800 million videos, totaling more than 1 billion hours
  • Netflix, TikTok, Facebook, Instagram, and Vimeo collectively hold billions more hours
  • Total accessible video online reaches several billion hours, or hundreds of exabytes of raw data

So how much of this can one person actually consume?

Assume 80 years of life, with about 70 years of active media consumption. At 8 hours per day, that gives you roughly 200,000 hours in a lifetime. Against YouTube's 1 billion hours alone, your entire life represents about 0.02%. Against all internet content, the number drops to something like 0.0000001%, one ten millionth of one percent.

Put another way: reading 50 billion web pages at 2 minutes each would take over 190 million years.

Now consider books specifically. If you read 20 pages per day for 70 years:

  • 20 pages × 365 days = 7,300 pages per year
  • 7,300 × 70 years = 511,000 pages in a lifetime
  • At 275 pages per book, that equals roughly 1,850 books

The average American reads about 12 books per year, or around 800 in a lifetime. Even dedicated readers only reach a tiny fraction of what exists.

There is far more good material out there than any of us will ever consume. That reality has made me take my choices more seriously. Every book, every hour, is a decision.

This is why I want to share what I consume. If we each only get 1,850 books or 200,000 hours, then the filtering matters. When someone shares what resonated with them, they save others the cost of searching. They act as a scout in an infinite landscape. I have benefited enormously from people who took the time to say "this one is worth it."

Centering a div: The Beginning with v0

Like many developers, I wanted a personal site but didn't want to spend weeks hand-coding every component. v0.dev seemed like the perfect solution: describe what you want, get working React components, iterate visually. And it worked, beautifully, for getting something live fast.

The benefits were obvious: rapid prototyping, immediate visual feedback, and no tedious boilerplate. I had a clean, professional-looking site within hours. But as I started customizing, the limitations emerged. I wanted custom cursor interactions. I wanted animated borders that drew themselves on hover. I wanted a WebGL background with real-time controls.

The Transition to Claude Code

The shift from v0 to Claude Code wasn't about replacing one tool with another. It was about moving from generative to collaborative. Except instead of scaling a company, I scaled my team of recursive tool calling LLMs.

It became a lot easier to have an agent transfer all of your blog posts, while creating a new custom component if you can run the two on parallel tracks, instead of having them bump into each other.

The Style System

As the site grew, patterns emerged. Rather than fighting consistency, I leaned into it and built a proper design system. Here's what makes it work:

Colors

Everything is built on CSS variables with semantic naming and opacity variants for hierarchy:

Color A

#fbf8f4

Color B

#e2dada

Font Color

#000000

Accent

#000000

Opacity Variants

text-foreground

Full opacity - primary content

text-foreground/80

80% - secondary content

text-foreground/60

60% - tertiary content

border-foreground/10

10% - subtle borders

bg-foreground/5

5% - background accents

Typography

The entire site uses the Domine serif font with weights ranging from 300 to 500. Responsive sizing follows a consistent scale:

Heading 1

text-3xl md:text-5xl lg:text-6xl

Heading 2

text-xl md:text-2xl

Heading 3

text-lg md:text-xl

Body text with consistent line-height and spacing.

text-sm md:text-base

Spacing

Consistent responsive padding and vertical rhythm throughout:

px-6 md:px-12 lg:px-24

Content with responsive padding

space-y-3

Element 1
Element 2
Element 3

Dynamic Settings

The settings button in the bottom-right corner opens a control panel with over 40 parameters for customizing the site's appearance. Everything from shader effects to color schemes to border widths can be adjusted in real-time.

The site's visual parameters are controlled by the BackgroundContext and sync across all components:

  • Border Width: Animates borders on navigation and interactive elements
  • Font Color: Controls the --foreground CSS variable globally
  • Color A/B: Primary and secondary colors for WebGL shader effects
  • Blur Effect: Opacity of the blur layer overlay (blurLayerOpacity)

All settings persist to localStorage and apply site-wide in real-time, creating a cohesive visual language where UI elements respond to the same parameters as the WebGL effects. Presets make it easy to switch between different visual themes.

The Cool Components

1. ShaderBackground & BackgroundControls

The animated background uses WebGL via Three.js and React Three Fiber. It supports two shader effects:

  • Swirl: Noise-based organic motion with turbulence and distortion
  • ChromaFlow: Color aberration with chroma offset and glow effects

2. Navigation with Animated Borders

The navigation component adapts to viewport size: horizontal menu on desktop, dropdown on mobile. Each link features the four-sided box drawing animation mentioned earlier, along with backdrop blur and scroll-triggered opacity changes.

Hover to see the animation:

Example Link

3. ContentWithSkeleton

A mount-gated skeleton loading pattern that prevents layout shift and provides smooth fade transitions. It waits for the component to mount before showing content, ensuring hydration consistency.

Preview:

Article Title

This is the actual content that appears after the skeleton loading state. The transition is smooth and prevents layout shift.

Tag 1Tag 2

4. BlogPost with Progress Bar

The article layout includes a scroll progress indicator that tracks your position through the content. It uses IntersectionObserver to highlight which section you're currently reading, providing subtle wayfinding without cluttering the interface.

5. CustomCursor

An enhanced cursor with smooth interpolation and spring physics. It subtly follows your mouse with a slight lag, creating a more tactile feel when navigating the site.

Custom Cursor Animation Demo:

The outer square border follows smoothly with a lag (lerp factor: 0.25), while the inner dot scales based on velocity. Watch it accelerate in the center and expand larger at higher speeds.

Tech Stack

Framework

Next.js 15.5.6 (App Router)

UI

React 19, TypeScript

Styling

Tailwind CSS v4, CSS Variables

Graphics

Three.js, React Three Fiber, GLSL

Components

shadcn/ui, Radix UI

Deployment

Vercel