Building This Portfolio — Technical Decisions and What Stuck
A behind-the-scenes look at the architecture, design decisions, and lessons from building sandeepp.in — a Next.js 14 portfolio with file-based markdown content.
A behind-the-scenes look at the architecture, design decisions, and lessons from building sandeepp.in — a Next.js 14 portfolio with file-based markdown content.
My previous portfolio was a React + GSAP project that looked impressive on a fast connection and on the right screen size. It didn't meet what I actually needed from a portfolio in 2026: fast, readable, SEO-indexed, and easy to update without touching component code.
The new stack: Next.js 14 App RouterNext.js's routing system built on the app/ directory and React Server Components, with nested layouts and data fetching on the server., TypeScript, Tailwind CSS, file-based markdown content, deployed to Vercel at sandeepp.in.
The core decision was treating content as files, not database records. Projects and blog posts live as .md files in a content/ directory. gray-matter parses frontmatter; next-mdx-remote renders the body. No CMS, no API calls, no syncing issues. git push is the publish step.
This made the whole system easier to reason about and maintain. Adding a new project is adding a file.
output: 'export' and Vercel don't play together. Vercel builds Next.js natively — adding output: 'export' causes a routes-manifest.json error at deploy time because the framework expects to own the build output format. Remove it.
@apply inside @layer utilities is flaky. Moved custom CSS into @layer components with plain CSS instead of @apply. More predictable, easier to debug.
require() in TypeScript Tailwind config. Switched from require("@tailwindcss/typography") to a proper ESM import statement. The require call was silently failing in certain build environments.
async generateStaticParams — wrapping a synchronous function in async caused Next.js's static export checker to misidentify it. Sync is correct.
Clean, typographic, content-first. Inspired by paco.me and leerob.io — sites that trust the content to carry the page without needing animation or decoration to hold attention. The design should be invisible.
Sticky nav, readable body text, consistent spacing. No hero video, no particle backgrounds on this one.
Ongoing project. More posts coming as I keep building things worth writing about.
Building Aegis — A Guardrail Gateway You Can Actually Measure
Notes from building a two-tier LLM guardrail gateway and the 127-case red-team harness that scores it — the escalation-router design, a streaming guard that catches leaks spanning chunk boundaries, and the calibration bugs that fell out of actually running it.
Building IronStride — A Local-First Training PWA With No Backend
Notes from building IronStride, an installable, offline-first training app on IndexedDB: dates as plain keys, a programme stored as data, an insights engine of pure functions with minimum-data guards, a backup that respects browser limits, and the bugs that only tests and a real browser found.
Building Opener — A File Explorer That Treats AI as Opt-In, Not the Point
Notes from building Opener, an offline-first Android file explorer and editor: the storage seam that made everything else easy, a diff-reviewed AI rewrite flow, folder search built on SQLite FTS5 because Groq has no embeddings API, and the honest call to hide a feature that worked but was too slow.