my personal site, live at tejasnaladala.com. a CRT/terminal-themed portfolio i built because my best friend at udub is obsessed with that aesthetic, and it stuck.
it is a Next.js app (App Router, React 19, TypeScript) deployed on Vercel. cream-and-orange palette, monospace type, hairline rules, a few easter eggs.
- landing page - a studio portrait card that swaps to an alternate image on hover, a tabbed CV (projects, skills, awards) with morphing headline text, and a technical-identity hero. full content is also rendered in a hidden
sr-onlyblock so screen readers and search engines get the whole thing, not just the active tab. /work- every project grouped into ventures, research, industry systems, competitions, and open source. pulled from one typed data file (src/data/projects.ts) so the work page, the landing tabs, and the resume never drift apart./thesis- things i currently believe to be true about hardware, startups, and shipping./gallery- photo grid with a keyboard-navigable lightbox (arrows to move, esc to close) and graceful fallbacks for images that fail to load./stories- locked behind an engineering brain teaser. answer it to read./resume- the clean, boring version for people who just want the PDF.
there's a contact form that posts to an API route, plus a generated Open Graph image so links unfurl nicely.
a few things i actually care about under the hood:
- single source of truth for content. projects, skills, awards, thesis entries, and stories all live as typed objects in
src/data/. the pages just render them. no copy-paste between routes. - contact API that degrades instead of breaking.
src/app/api/contact/route.tsvalidates input with Zod, applies a 5-requests-per-minute-per-IP in-memory rate limit, and sends through Resend. if the API key is missing or the upstream send fails, it returns amailtofallback so the form never silently eats a message. the key is read fromprocess.envonly and is never committed. - OG image generated at the edge.
src/app/api/og/route.tsxbuilds the 1200x630 social card withnext/oginstead of shipping a static PNG, so the tagline and stats stay in sync with the rest of the site. - accessible by default. skip-to-content link, the hidden full-content block behind the tabbed UI, reduced-motion handling, and aria labels on the interactive bits.
Next.js 16, React 19, TypeScript, Tailwind CSS v4, Framer Motion, Zustand, Three.js / Spline, Howler.js, Zod, Resend.
npm install
npm run devopens on localhost:3000.
the contact form needs RESEND_API_KEY in your environment to actually send mail. without it the form still works and falls back to a mailto: link, so you don't need it for local dev. optional overrides: RESEND_FROM, RESEND_TO.
npm run build # production build
npm run lint # eslinthosted on Vercel. push to master and it deploys, or run npx vercel --prod. set RESEND_API_KEY (and optionally RESEND_FROM / RESEND_TO) in the Vercel project env.
MIT. see LICENSE.