← All Posts

The Tech Stack Behind This Site

5 min readMarch 4, 2026
tech-stacknext.jstursovercelclaude-codeprisma

The Tech Stack Behind This Site

The Tech Stack Behind This Site
The Tech Stack Behind This Site

People keep asking me what this site is built with. Fair question — it's a portfolio for an AI builder, so the stack should reflect that. Here's the full breakdown.

Development: Claude Code + Skills

Every line of code on this site was written through Claude Code — Anthropic's CLI agent. I use specialized skills like senior-frontend and frontend-design for component scaffolding, styling, and architecture decisions. No traditional IDE workflow — just prompts, code reviews, and iterative refinement.

This isn't a gimmick. Claude Code handles everything from creating new components to debugging Prisma schema issues to pushing deployments. The entire development loop — write, test, commit, deploy — happens through the CLI.

Framework: Next.js 16 + React 19

Next.js 16 with the App Router gives me:

  • Static Site Generation (SSG) for blog posts and project pages — fast loads, SEO-friendly
  • Server Components by default — minimal client-side JavaScript
  • API Routes for the admin CRUD endpoints
  • TypeScript 5 throughout — type safety without the ceremony

The site is primarily static. Blog posts are pre-rendered at build time, which means every deploy bakes fresh content into the HTML.

Styling: Tailwind CSS 4 + Framer Motion

Tailwind CSS 4 handles all styling — no CSS modules, no styled-components. The new version brings:

  • CSS-first configuration
  • Better performance with the Oxide engine
  • Native cascade layers

Framer Motion 12 powers the animations:

  • Page transitions between routes
  • Staggered entrance animations for lists
  • Gradient mesh backgrounds
  • Animated counters in the stats section
  • Marquee ticker for the tools showcase
  • Cursor glow effects

The visual design follows a clean, professional aesthetic with subtle depth — glass cards, gradient meshes, and smooth transitions that feel polished without being distracting.

Database: Turso (Cloud SQLite)

Turso is a cloud-hosted SQLite database built on libSQL. Why Turso over Postgres or PlanetScale?

  • Edge-ready — SQLite at the edge with global replication
  • Generous free tier — more than enough for a portfolio site
  • Simple — it's just SQLite with a URL

The database lives in aws-us-west-2 and connects via the @libsql/client adapter.

A Turso Gotcha

One thing I learned: Prisma's orderBy doesn't reliably work with the Turso adapter. My blog posts were showing in insert order instead of date-descending. The fix: always sort in JavaScript after fetching, regardless of what the ORM says it's doing.

function sortPostsByDate(posts: PostData[]): PostData[] {
  return posts.sort((a, b) => {
    const da = a.publishedAt ? new Date(a.publishedAt).getTime() : 0;
    const db = b.publishedAt ? new Date(b.publishedAt).getTime() : 0;
    return db - da;
  });
}

Trust but verify your ORM.

ORM: Prisma 7

Prisma 7 with the @prisma/adapter-libsql bridge to Turso. Six models:

ModelPurpose
PostBlog posts with markdown content
ProjectPortfolio projects with descriptions
TimelineEventJourney milestones
ToolTools and technologies evaluated
ExperienceCareer history
SiteSettingKey-value config

Data Architecture: DB-First with Static Fallback

This is the pattern I'm most proud of. Every data-fetching function follows the same structure:

  1. Try the database first — query Turso via Prisma
  2. Fall back to static constants — if DB is empty or errors, use SAMPLE_* arrays hardcoded in constants.ts
export async function getPosts(): Promise<PostData[]> {
  try {
    const posts = await prisma.post.findMany({ where: { published: true } });
    if (posts.length > 0) return sortPostsByDate(posts);
  } catch {}
  return sortPostsByDate(SAMPLE_POSTS.filter(p => p.published));
}

This means the site never breaks even if Turso is down. The static fallback is always there. The tradeoff: both sources must stay in sync when adding content.

Auth: Simple Bearer Token

No OAuth, no JWT, no auth library. The admin API uses a single Bearer token:

  • Local development: admin123 from .env
  • Production: ADMIN_PASSWORD environment variable on Vercel

There's no user-facing authentication — it's a portfolio site, not a SaaS product. The admin routes are just for me to manage content via API calls.

Hosting: Vercel

Vercel handles hosting with zero configuration:

  • GitHub integration — push to main triggers auto-deploy
  • Edge network — static assets served from the nearest PoP
  • Environment variablesTURSO_DATABASE_URL, TURSO_AUTH_TOKEN, ADMIN_PASSWORD

A Vercel Gotcha

Vercel's build cache can serve stale CSS bundles. I had a full visual overhaul (gradient meshes, glass cards, animated counters) that deployed successfully but the old styles kept showing. The fix: push an empty commit to force a clean rebuild.

git commit --allow-empty -m "Force rebuild: clear Vercel cache"
git push origin main

Always compare deployed CSS file size against your local build to verify.

Markdown Rendering

Blog content is stored as raw Markdown and rendered with:

  • react-markdown — base renderer
  • remark-gfm — GitHub Flavored Markdown (tables, strikethrough, task lists)
  • rehype-raw — allows raw HTML in markdown (for embeds)
  • shiki — syntax highlighting with VS Code-quality themes

The Full Picture

LayerTech
DevelopmentClaude Code + frontend skills
FrameworkNext.js 16 (App Router, SSG)
LanguageTypeScript 5, React 19
StylingTailwind CSS 4 + Framer Motion 12
DatabaseTurso (cloud SQLite / libSQL)
ORMPrisma 7
AuthBearer token (admin only)
HostingVercel (auto-deploy from GitHub)
Markdownreact-markdown + remark-gfm + rehype-raw + shiki
Data patternDB-first with static fallback

The whole thing is intentionally simple. No overengineering, no premature abstractions. A portfolio site doesn't need a microservices architecture — it needs fast loads, good content, and reliable deploys.

The most interesting part isn't any individual technology choice. It's that the entire site — every component, every API route, every blog post — was built through conversation with an AI agent. The stack is the proof that AI-native development isn't a future promise. It's how I work today.