Skip to main content

Personal Portfolio

TypeScript

Personal Portfolio

Personal portfolio for Shashank Kumar Singh — Full-Stack & DevOps Engineer. Dark "gym steel" visual theme, scroll-driven home page, and a Projects section that syncs itself from GitHub.

Stack: Next.js 15 (App Router) · React 19 · TypeScript · Tailwind CSS v4 · Framer Motion · shadcn/ui · Zod · Resend Hosting: Vercel


Getting started

npm install
npm run dev          # http://localhost:3000

Scripts

CommandWhat it does
npm run devDev server with hot reload
npm run buildProduction build
npm startServe the production build locally
npm run type-checktsc --noEmit
npm run lintESLint over the whole repo
npm run formatPrettier, write mode
npm run format:checkPrettier, check mode (what CI runs)

Environment variables

All optional — the site builds and runs without any of them.

VariableEffect if unset
RESEND_API_KEYContact form accepts submissions but only logs them; no email is sent.
GITHUB_TOKENGitHub API calls stay unauthenticated (60 req/hour instead of 5,000).

Copy .env.example to .env.local to set them for local development.


Project structure

src/
├── app/                       # Next.js App Router
│   ├── (site)/                # Route group: every page with nav + footer chrome
│   │   ├── layout.tsx         #   the chrome itself
│   │   ├── page.tsx           #   home
│   │   ├── about/
│   │   ├── achievements/
│   │   ├── contact/
│   │   ├── experience/
│   │   ├── projects/          #   index + [slug] detail, both GitHub-driven
│   │   └── skills/
│   ├── api/contact/route.ts   # POST handler for the contact form
│   ├── layout.tsx             # root shell: fonts, metadata, JSON-LD
│   ├── robots.ts              # /robots.txt
│   └── sitemap.ts             # /sitemap.xml
│
├── components/
│   ├── ui/                    # shadcn/ui primitives (button, card, badge, …)
│   ├── layout/                # SiteNav, NavOverlay, NavItem, Footer, MusicBubble
│   ├── common/                # Section, StatCounter, Magnetic
│   ├── home/                  # the home page's scroll-driven acts
│   ├── projects/              # GitHub repo cards, README renderer, skeletons
│   ├── skills/                # SkillPlates
│   └── charts/                # hand-rolled SVG charts (no chart library)
│
├── content/                   # all copy and data, validated by Zod at load
│   ├── site.ts                # identity, nav, SEO defaults
│   ├── skills.ts
│   ├── experience.ts
│   └── achievements.ts
│
├── hooks/
│   └── use-magnetic.ts        # cursor-following hover physics
│
├── lib/
│   ├── github.ts              # GitHub REST client (1h ISR cache)
│   ├── seo.ts                 # per-page metadata + JSON-LD
│   ├── utils.ts               # cn(), formatDate()
│   └── validators.ts          # Zod schemas — source of truth for data shapes
│
└── styles/
    ├── globals.css            # entry point; imports the three partials below
    ├── theme.css              # design tokens (@theme + CSS custom properties)
    ├── base.css               # element defaults, scrollbar, body texture
    └── utilities.css          # .steel-plate, .gym-shadow, .nav-fab, …

Conventions

  • @/ maps to src/. Use it for cross-folder imports; relative paths only within the same folder.
  • Components are .tsx, styles are .css. No CSS-in-JS files. Static styling lives in src/styles/; inline style objects are reserved for values driven by Framer Motion, which CSS cannot express.
  • Content never lives in components. Copy and data go in src/content/, behind a Zod schema, so a bad value fails the build instead of the page.
  • Tailwind v4 is configured from CSS. There is no tailwind.config.ts — tokens are declared in src/styles/theme.css.

How the pages work

Home is three scroll-driven acts stacked with no gaps:

  1. AboutSection — the hero recedes while a green panel rises over it across a 200vh sticky scroll (CinematicTransition orchestrates it).
  2. SelectedWork — a dark cap slides away to reveal the work index.
  3. ContactCta — a black panel takes over before the footer.

Projects reads the GitHub REST API rather than a hand-maintained list. Public, non-forked, non-archived repos are shown newest-push-first, and each detail page renders that repo's README. Responses are cached for an hour, so pushing to GitHub updates the site without a redeploy.

Contact posts to /api/contact, which validates with Zod and sends the message through Resend. There is no database and nothing is written to disk — Vercel's filesystem is read-only.


Editing content

To change…Edit
Name, email, socials, SEOsrc/content/site.ts
Nav menu / sitemapsrc/content/site.tsnavigation
Skills and levelssrc/content/skills.ts
Work historysrc/content/experience.ts
Certifications and awardssrc/content/achievements.ts
Colours and fontssrc/styles/theme.css
Resume PDFpublic/Shashank_RESUME2025.pdf

Adding a page? Create it under src/app/(site)/, then add it to navigation in src/content/site.ts — the side nav, overlay nav, footer and sitemap all pick it up from there.


Deployment

Vercel builds from main. vercel.json sets the region, security headers, the /resume redirect, and the contact function's timeout; everything else is inferred from the framework.

CI (.github/workflows/ci.yml) runs type-check, lint, format-check and build on every push and pull request to main.

Known follow-ups

  • src/content/site.ts points seo.ogImage at the profile photo. Replace it with a purpose-built 1200×630 card for better link previews.
  • The ambient audio in public/audios/ is a copyrighted track; swap it for something licensed before promoting the site widely.