Rubin Bhandari

Rubin Bhandari — Portfolio, Interactive Resume & MDX Blog

A production-ready personal website built on the TanStack ecosystem: TanStack Start (SSR + server functions), TanStack Router (file-based routing, search params), TanStack Query, and TanStack Store — with UnoCSS (wind4 preset, a drop-in Tailwind v4 replacement), shadcn/ui, Motion, and a lazy-loaded React Three Fiber 3D scene on the home page.

Stack


Overview

Area Highlights
Home Full-viewport hero with lazy 3D workspace scene (R3F), animated gradient blobs, marquee, stats counters, mouse parallax
Sections About, interactive skills (tabs + orbit constellation + tooltips), experience timeline (scroll-grown line, accordions), featured projects, contact CTA
Projects /projects — search-param driven category pills + debounced search, responsive grid, empty state
Blog /blog + /blog/:slug — MDX via content-collections, Shiki code blocks, KaTeX math, Mermaid diagrams, TOC, share buttons, related posts, giscus comments
Resume Downloadable PDF (/resume.pdf) via header/hero/terminal links
Contact Home section — interactive card: copy-email chip with clipboard feedback, availability badge, and a magnetic mailto CTA
Extras ⌘K command palette, light/dark/system theme, smooth scrolling (Lenis), scroll progress, custom 404 + error pages, RSS, sitemap, robots.txt, JSON-LD

Tech Stack

Note on Radix imports: UI primitives are imported exclusively from the radix-ui monolith (e.g. import { Slot } from 'radix-ui'), not from individual @radix-ui/react-* packages — those are transitive deps and not listed in package.json. Keep it that way when adding components.

Getting Started

pnpm install
pnpm dev          # http://localhost:3000

Required first run (content generation):

pnpm content:build

Other scripts:

Script Purpose
pnpm dev Start the Vite dev server
pnpm build Production build (.output/)
pnpm start Run the production server
pnpm typecheck tsc --noEmit
pnpm content:build Regenerate content-collections output
pnpm test:snapshots Build + compare visual snapshots against the Tailwind v4 baseline goldens
pnpm test:snapshots:baseline Regenerate baseline goldens from the Tailwind v4 build (HEAD)

Visual Snapshot Testing

A Playwright suite guards against visual regressions by comparing the rendered site, pixel-for-pixel, against goldens captured from the previous Tailwind CSS v4 build. This is how the UnoCSS migration was verified as visually 1:1 (see compare.md for the full audit).

pnpm test:snapshots              # build + run the suite (18 tests)

Regenerating the goldens

Goldens live in tests/e2e/__screenshots__/visual.spec.ts/ and were originally captured from the Tailwind v4 baseline (8b4b624) via scripts/snapshot-baseline.mjs HEAD. They were since regenerated from the UnoCSS build after the WebP image conversion (see below); tests/snapshots/baseline.json records the current provenance (UnoCSS build + original Tailwind commit). Regenerate them only when the baseline intentionally changes (e.g. a deliberate redesign):

pnpm test:snapshots:baseline     # builds HEAD in a temp worktree, serves it on :3199, captures goldens
pnpm exec playwright test --update-snapshots=all   # or: regenerate in place against the current build

Note: --update-snapshots (default mode) only rewrites missing or failing snapshots — use --update-snapshots=all to bake in an intentional visual change (e.g. the image-format conversion), since the old goldens may still pass within the pixel threshold.

Environment Variables

The app needs no environment variables — all content, fonts, and imagery are build-time static data.

Adding a Blog Post

  1. Create content/blog/my-post.mdx
  2. Add frontmatter:
    ---
    title: "My Post Title"
    date: "2026-08-01"
    description: "One-liner used in cards, meta, and RSS."
    tags: ["react", "tanstack"]
    category: "engineering"
    featured: false
    ---
    
  3. Write the body. Supported out of the box: Shiki code fences, KaTeX math ($$...$$), Mermaid diagrams (```mermaid), standard markdown, custom Mermaid component.
  4. Run pnpm content:build — the post appears automatically at /blog/my-post. No routing or registration changes.

Editing Content

All site content is data-driven in src/data/:

File Controls
site.ts Name, role, email, URL, socials, SEO defaults
nav.ts Header/footer navigation
profile.ts Hero bio, highlights, interests, philosophy
skills.ts Skill categories, levels, years, related projects
experience.ts Work history + achievements + technologies
projects.ts Portfolio projects (categories, tech, links)
testimonials.ts Quotes

Edit a file, save, and the UI updates — no component changes required.

Deployment (Vercel)

  1. Push the repo to GitHub and import it into Vercel.
  2. Vercel auto-detects the framework. Build command pnpm build, output .output/public.
  3. Deploy. The site is SSR’d with route-level code splitting; the 3D scene, Mermaid, Shiki, and giscus load lazily.

Project Structure

content/blog/          MDX posts (one file = one post)
content-collections.config.ts
public/                robots.txt, og.png, projects/*.webp, blog/**/*.webp (all imagery is WebP — converted from PNG/JPG, see `compare.md`)
scripts/               content build, smoke test, snapshot-baseline helpers
tests/e2e/             Playwright visual snapshot suite + Tailwind v4 goldens
src/
  components/
    animations/        Reveal, MagneticButton, TiltCard, Counter, TextReveal
    blog/              MDX renderer, code blocks, TOC, share, giscus…
    home/              Hero, sections, marquee
    layout/            Header, footer, command palette, theme, lenis…
    projects/          Project card + filters
    three/             Lazy 3D scene
    ui/                Primitives: button, badge, tabs, dialog, sheet, dropdown-menu,
                        command, tooltip, accordion, input, skeleton, toast, toaster,
                        brand-icons, responsive-image
  data/                All editable site content
  lib/                 cn util, constants, seo helpers
  routes/              File-based routes (incl. sitemap.xml, rss.xml)
  server/              Server functions (blog)
  stores/              Theme + command palette stores
  styles/globals.css   Design tokens, prose, print styles
  types/               Shared TypeScript types

Production Hardening Notes

Before going public, consider:

Accessibility & Motion

License

Private — all rights reserved.