Building This Site
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
space-y-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:
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.
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