Most developer portfolios are digital business cards designed to pass an ATS scan. A Next.js template, some placeholder copy, a Vercel deployment. It's efficient. It's also pretty boring. I wanted a site that is itself an engineering project: proof of how I approach architecture and security, not a claim that I can write code.

If you're a developer planning your own build, or a recruiter checking my claims: this is the technical breakdown. No hand-waving, failures included.

The Hero Section.


The Templated Identity Crisis

Why does every portfolio in a computer science department look exactly the same??

During my first year, I noticed a sea of identical templates. They look polished and deploy in minutes, but they say nothing about the person who supposedly built them. As someone focused on backend systems and security, a landing page is not my natural habitat. The shell is.

The first iteration of this site was a modified Hugo template. It felt like wearing someone else's clothes. I decided to rebuild from scratch with three hard constraints:

  1. Zero UI libraries. No Radix, no Shadcn, no component libraries. Every element is built from raw HTML and CSS.
  2. Terminal-first. The site must be navigable via a keyboard-driven terminal overlay.
  3. Proof of work. Every feature must solve a real engineering problem. (Decorative features that only exist to look good are a waste of everyone's time.)

The Architecture of Tokens

React 19, TypeScript, and Vite form the framework layer. That's all.

I avoided CSS-in-JS and Tailwind in favor of CSS Custom Properties. Everything is driven by design tokens defined in a single authoritative stylesheet: index.css.

(I spent two hours debugging why a logo appeared black before realizing the token was defined in a file I never imported. Lesson in source-of-truth management: learned, slowly.)

Every component references these tokens. When the theme flips, everything updates. No conditional rendering, no React state for colors, no class toggling on forty different elements.

:root {
  --bg: #111113;
  --text: #f0eee8;
  --mint: #a8d8c8;
  --lavender: #c4b5e8;
  --peach: #f4b8a4;
  --sky: #9ec8e8;
}

[data-theme="light"] {
  --bg: #f0eee8;
  --text: #1a1a1f;
  /* ... overrides cascade automatically */
}

Typography Tokens Color Tokens

The theme toggle uses a radial clip-path wipe that originates from the button's position. It's thirty lines of code that make the transition feel deliberate instead of jarring.


The Terminal Interpreter

The terminal isn't a styled text area. It's a structured command interpreter with a typed pipeline, history management, and SPA navigation support.

(It is also the most-used feature on the site, so the keyboard-first bet paid off.)

Every command handler returns a ProcessResult. I used a template literal type, NAVIGATE:${string}, to trigger SPA navigation. It allowed me to keep the result type as a flat primitive union without refactoring twenty files. Pretty elegant, for a fix that dodged an architecture mess.

type ProcessResult =
  | TerminalLine[]
  | 'EXIT'
  | 'CLEAR'
  | `NAVIGATE:${string}`
  | Promise<TerminalLine[]>;

// In the navigation handler:
if (typeof result === 'string' && result.startsWith('NAVIGATE:')) {
  onExit();
  onNavigate(result.slice('NAVIGATE:'.length));
  return;
}

The system understands argument context. If you type projects --details and hit Tab, it cycles through project IDs. It is some sort of command registry where every handler returns a typed result.

Terminal Overlay


Client-Side Resume Generation

Most portfolios host a static PDF on a cloud bucket. This creates drift. You update a job title on the site but forget to re-export the resume.

I eliminated this by generating the PDF entirely in the browser using @react-pdf/renderer. The data is pulled from the same TypeScript files that drive the UI. When you click download, the engine lazy-loads, renders the document in memory, and triggers the browser download. No server required.

const handleDownload = async () => {
  const [{ pdf }, { ResumePDFDoc }] = await Promise.all([
    import('@react-pdf/renderer'),
    import('./ResumePDF'),
  ]);
  const blob = await pdf(React.createElement(ResumePDFDoc)).toBlob();
  // ... trigger download logic
};

The resume and the website are always in sync. Update a job title in one file, and both the UI and the downloadable PDF reflect it immediately. (Anything else is just a maintenance liability waiting to happen.)

Resume PDF Preview

Security Hardening

Security is often an afterthought in frontend development. That is a pretty dangerous assumption.

The project detail page renders GitHub READMEs using react-markdown. The initial implementation used rehype-raw to parse embedded HTML. This was a vulnerability?? Yes. GitHub READMEs are not trusted content. An attacker could inject a malicious script into a README and it would execute on my domain. I removed the plugin entirely. Standard Markdown is sufficient. The rest is just an attack vector.

I also implemented a strict Content Security Policy. No inline scripts are allowed. Even if an XSS payload slips through, the browser will block execution. That's defense-in-depth.

default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https://raw.githubusercontent.com;
connect-src 'self' https://raw.githubusercontent.com https://api.github.com;

(I also wrapped every instance of dangerouslySetInnerHTML in DOMPurify.sanitize() just in case. If the data files are ever compromised, the blast radius is limited.)

Accessibility Audit Accessibility Rules


Performance & Production Polish

Fast is a feature, not a number you check at the end. Every scroll-reveal animation runs off IntersectionObserver, so nothing animates until it enters the viewport.

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      // Trigger animation logic
    }
  });
}, { threshold: 0.12, rootMargin: '0px 0px -10% 0px' });

Vite's manualChunks config splits the framework code into a react-vendor chunk, so it caches independently of application code. The site loads like a static document and behaves like an app.


The Engineering Conclusion

A portfolio should be proof of how you think, not a list of skills. This one is mine.

Standard CSS turned out to be more than enough for modern layout. Shipping beats perfection. (I deleted three days of work on a glitch effect because it compromised readability.)

The code should do the talking. Is your site an engineering project, or is it just a template??


I am a CSE student who builds backend systems and cares about how they break.