Skip to content

saaranshM/Portfolio-Website

Repository files navigation

Engineered editorial — portfolio of Saaransh Menon

A typography-led Nuxt 4 portfolio (saaranshmenon.me): warm dark broadsheet, hairline rules, one hot accent, and a hero sentence annotated like the transaction trace my payment systems produce. Production ships zero framework JavaScript — the whole client runtime is ~30 inlined lines.

Stack

  • Nuxt 4 — static generation via nuxt generate, features.noScripts: 'production'
  • Vue 3 + TypeScript (strict) — dev-time only; no hydration ships
  • Sass — hand-rolled token system (no Tailwind, no modules, no icon/image libs)
  • Fraunces + JetBrains Mono, variable woff2, self-hosted in public/fonts/ (latin subset, downloaded once from Google Fonts)

Architecture

  • Content lives in app/data/*.ts (typed; components never hardcode copy). Update projects/bio/case studies there.
  • Design system: app/assets/scss/_tokens.scss is the single source — every color/space/shape/motion value is a CSS custom property on :root. _typography.scss holds the three type roles (display serif / mono metadata / system body) and the @font-face rules. _base.scss holds the hairline section grammar, link/button behaviors, reveal grammar, grain.
  • Motion: one inline script (MOTION_JS in nuxt.config.ts) — IntersectionObserver reveals (60ms sibling stagger) + the kinetic hero weight (pointer-x → wght 320–640). Everything sits fully visible with no JS and under prefers-reduced-motion.
  • Signature: the hero sub-sentence renders spans with dotted underlines and tiny mono tags (uptime / auth ok / settle t+1), typeset like a payment transaction trace. The Instagram Comment Analyzer case study stays published, framed explicitly as past after-hours work.
  • Fonts: fraunces-latin-var.woff2 (67KB, preloaded) · jetbrains-mono-latin-var.woff2 (40KB) · fraunces-italic-sub.woff2 (24KB, lowercase-only subset for the single accent-italic word — keep italics lowercase or glyphs fall back to Georgia italic).
  • Machine layer: public/llms.txt, JSON-LD Person (app.vue), data-driven server/routes/sitemap.xml.ts, OG image rendered in the site's own type system.

Usage

npm install

npm run dev        # dev server at http://localhost:3000 (scripts/HMR on)
npm run generate   # prerender static site to .output/public (no scripts)
npm run preview    # serve the generated output locally
npm run typecheck  # vue-tsc via nuxt typecheck

Deploy

Firebase Hosting (project vue-express-54b2e), serving prerendered .output/public — fully static. Hashed assets (/_nuxt) and /fonts/** get immutable cache headers; HTML revalidates every request.

npm run deploy:preview   # generate + deploy to the "staging" preview channel
npm run deploy           # generate + deploy to production

Content checklist

Every TODO(saaransh) in app/data/ — resolve before launch:

  • profile.ts — hero metric strip: three real outcomes (payments scale · one before→after number · team growth)
  • profile.ts — the signature's uptime todo tag: the real uptime number (e.g. 99.99%)
  • profile.ts — status line wording (open to …)
  • caseStudies.ts — confirm the year in dated: 'after-hours project · 2025 · past work'
  • profile.ts — drop the resume PDF into public/resume.pdf
  • caseStudies.ts — unsnooze: two outcome metrics (downloads/week · sessions revived), the AI-vs-judgment honest note, confirm the rejected-alternatives framing
  • caseStudies.ts — instagram-comment-analyzer: scale paragraph (context), the real constraint, how the fusion reconciles (decision), the two actually-rejected alternatives, three outcome metrics, honest notes
  • projects.ts — expand the Vue Account Manager description (everything else verified against the GitHub API 2026-07-11: recency order, repo names, live URLs)
  • socials.ts — confirm the Medium account is still active

About

This is my portfolio website built using Vue and Sass, hosted on Firebase.

Resources

Stars

1 star

Watchers

1 watching

Forks

Contributors