Skip to content

Building an IDE-Themed Portfolio with Next.js 16

How I built a developer portfolio that looks and feels like a code editor — terminal emulator, pane navigation, keyboard shortcuts, and layered security.

January 10, 20264 min read
Next.jsReactTypeScriptArchitecturePortfolio

Why an IDE Theme?

Most developer portfolios fall into two camps: minimal single-page sites that undersell technical ability, or over-designed showcases that prioritize visuals over substance. I wanted something that signals "I think like an engineer" from the first second — so I built a portfolio that looks and behaves like a code editor.

The concept: a dashboard with a sidebar (profile panel), tabbed panes (About, Projects, Stack, Contact), a functional terminal emulator, and keyboard shortcuts throughout. Dark mode by default with a polished light mode. The whole thing is a Next.js 16 app deployed on Vercel with a PostgreSQL backend for the contact pipeline and interactive demos.

Architecture Overview

The app is structured around a few core patterns:

Pane Router

Instead of traditional page navigation, the home page uses a client-side pane system. Four tabs — About, Projects, Stack, Contact — each render a different component in the main content area. The URL hash syncs with the active pane so direct links work.

const PANE_COMPONENTS: Record<PaneTab, React.ComponentType> = {
  about: AboutPane,
  projects: ProjectsPane,
  stack: StackPane,
  contact: ContactPane,
};

Navigation happens via tab clicks, keyboard shortcuts (1–4), or terminal commands (goto projects). All three paths converge on the same setActiveTab call, keeping state centralized.

Terminal Emulator

The terminal at the bottom of the layout is a fully functional command interpreter. It supports:

  • Navigation: cd about, goto projects, or just type contact
  • Form interaction: fill name "Dominick", autofill, send
  • Info commands: help, skills, sysinfo, metrics
  • Easter eggs: cowsay, matrix, sudo, rm -rf /
  • Autocomplete: Tab completion with fuzzy matching
  • Command history: Arrow keys traverse previous commands

The command resolver uses Zod schemas for input validation and Levenshtein distance for typo correction:

// If no exact match, suggest the closest command
const suggestion = findClosestCommand(input);
if (suggestion) {
  return { lines: [{ text: `Command not found. Did you mean: ${suggestion}?` }] };
}

The terminal boots with a matrix rain animation on first load, then presents a prompt. It dispatches custom DOM events (terminal:command-result, terminal:navigate) that the parent layout listens to, keeping the terminal decoupled from pane state.

Theme System

The design uses CSS custom properties organized by semantic role rather than raw color values:

  • Surfaces: --surface-base, --surface-raised, --surface-overlay
  • Text: --text-primary, --text-secondary, --text-muted
  • Borders: --border-subtle, --border-default, --border-strong
  • Tones: --tone-blue, --tone-violet, --tone-amber, --tone-rose

Light and dark modes swap the entire token set. Components never reference raw hex values — everything goes through the token layer. This means adding a new theme variant (high contrast, color-blind friendly) only requires defining a new set of tokens.

Contact Pipeline

The contact form is a full production pipeline, not a mailto link:

  1. Client: Zod-validated form → POST /api/contact
  2. API route: Origin validation, rate limiting, body size check, Zod re-validation
  3. Database: PostgreSQL insert (contacts table with UUID primary key)
  4. Email: Auto-reply to sender via Resend + notification to admin
  5. Admin dashboard: Authenticated inbox with search, filter, archive, and reply

Security layers include CSP headers, CSRF-style origin checks, rate limiting (per-IP and global), and request body size limits. The site scores A+ on Mozilla Observatory.

Interactive Demos

The portfolio hosts two full-stack demos as first-class routes:

Real-time Polls (/projects/polls): Create polls and watch votes stream in live. Built with Server-Sent Events for real-time push, PostgreSQL for storage and aggregation, Framer Motion for animated result bars, and cookie-based fingerprinting for duplicate prevention.

Kanban Task Board (/projects/taskboard): Drag-and-drop task management with @dnd-kit. PostgreSQL-backed with board creation, column management, and task ordering persisted server-side.

Both demos share the portfolio's design system and database infrastructure — they're not separate apps, they're integrated features.

Testing Strategy

Testing is multi-layered:

  • Unit tests (Vitest): Command resolver logic, schema validation, utility functions. The terminal command resolver alone has comprehensive test coverage — navigation aliases, intent matching, error handling, and autocomplete.
  • E2E tests (Playwright): Contact form submission flow, admin authentication and message management, security headers on public routes, and admin security hardening (lockout behavior, recovery flow).
  • Type safety: tsc --noEmit in CI with zero errors. Zod schemas bridge runtime and compile-time validation.

What I'd Do Differently

  1. Start with the blog earlier. Technical writing compounds — having posts from day one would have been more valuable than adding them later.
  2. RSS feed. I should add one. Developers and recruiters who use feed readers would appreciate it.
  3. Lighthouse CI in the pipeline. I monitor Core Web Vitals via Vercel Analytics, but automated regression detection would catch performance issues before deploy.

The source code for this portfolio is public at github.com/dev-dominick/portfolio.