Skip to content

Repository files navigation

Terminal Portfolio

This is a TanStack Start portfolio site that presents the homepage as an interactive terminal. Visitors can read boot output, run commands, open project rows, and view inline project case studies.

Quick Start

pnpm install
pnpm dev

The dev server runs through Vite. The app entry route is src/routes/index.tsx, which renders src/TerminalPortfolio.tsx.

Common Commands

pnpm dev      # Start the local dev server
pnpm lint     # Run ESLint
pnpm test     # Run Vitest tests
pnpm build    # Build client and SSR output
pnpm format   # Format with Prettier and run eslint --fix
pnpm check    # Check Prettier formatting

Note: Vite 8 expects Node 20.19+ or 22.12+. The build may still complete on slightly older Node versions, but upgrading avoids the warning.

Project Structure

.
├── src/
│   ├── components/
│   ├── data/
│   ├── routes/
│   ├── commands.ts
│   ├── router.tsx
│   ├── routeTree.gen.ts
│   ├── styles.css
│   ├── TerminalPortfolio.tsx
│   ├── TerminalPortfolio.test.tsx
│   └── types.ts
├── dist/
├── node_modules/
├── package.json
├── pnpm-lock.yaml
├── vite.config.ts
├── eslint.config.js
├── prettier.config.js
├── tsconfig.json
└── .gitignore

How The App Fits Together

src/routes/index.tsx is the homepage route. It is intentionally small and only renders TerminalPortfolio.

src/routes/__root.tsx is the document shell. It owns metadata, viewport settings, the JetBrains Mono font link, the global stylesheet link, TanStack devtools, and the final <Scripts /> injection.

src/TerminalPortfolio.tsx is the main interactive terminal. It manages boot timing, command input, scroll behavior, selected project state, open project state, and keyboard shortcuts.

src/commands.ts is the command engine. It converts terminal commands such as help, ls, projects, whoami, cat contact.txt, cat <project-id>, and clear into renderable terminal lines.

src/data/portfolio.ts is the content source for the portfolio. Update this file when changing the name, role, contact details, project list, project metadata, or case study copy.

src/types.ts contains the shared TypeScript shapes for portfolio data, projects, terminal lines, and command results.

Component Directory

src/components/TerminalLine.tsx renders one terminal line based on its kind: system, prompt, output, error, spacer, or project list.

src/components/ProjectList.tsx renders the terminal-style project file list. It owns each expandable row button, aria-expanded state, mobile-friendly row layout, and the case study region wrapper.

src/components/CaseStudyInLine.tsx renders the expanded project case study: tagline, metadata, problem, insight, decisions, and image placeholder.

Styling

src/styles.css imports Tailwind and defines the terminal-specific theme tokens. It also contains custom CSS for the CRT scanline overlay, vignette overlay, custom scrollbars, blinking caret, case study expand animation, and project row states.

Most layout, spacing, borders, typography sizing, and responsive behavior live directly in component class names with Tailwind utilities. Keep custom CSS in styles.css for effects or tokens that are awkward to express as utilities.

Routing And Generated Files

TanStack Router uses file-based routing from src/routes/.

src/routeTree.gen.ts is generated by the TanStack Router plugin. Do not edit it by hand; it updates from route files.

src/router.tsx creates the router instance, enables scroll restoration, and registers router types for TanStack Router.

Vercel Deployment

This app uses TanStack Start with Nitro for server-aware Vercel output. vite.config.ts includes nitro() after tanstackStart(), which lets Vercel build the app into .vercel/output instead of treating it like a plain static Vite site.

Without Nitro, Vercel can deploy the build but return 404 NOT_FOUND at / because the SSR route handler is missing.

Tests

src/TerminalPortfolio.test.tsx covers the main terminal behavior:

  • boot output renders and the prompt becomes available
  • commands can be submitted from the input
  • project rows expand and collapse
  • cat <project-id> opens the matching case study
  • unknown commands show an error
  • clear restarts the session

The component test uses Vitest with happy-dom. happy-dom is used because the installed jsdom dependency path currently fails during Vitest worker startup in this project.

Generated And Ignored Directories

dist/ is production build output from pnpm build. It is generated and ignored by git.

node_modules/ contains installed dependencies. It is generated by pnpm install and ignored by git.

.tanstack/, .vinxi/, .nitro/, .output/, .wrangler/, and similar paths are framework or deployment build caches listed in .gitignore.

.pnpm-store/ may appear if pnpm uses a workspace-local package store. It is ignored by git.

Updating Portfolio Content

For content-only updates, start in src/data/portfolio.ts. Project IDs are user-facing because terminal commands use them, for example cat PROJECT_ALPHA.app.

If you add a new field to portfolio data, update src/types.ts first, then update the component that renders it.

If you add a new command, update src/commands.ts and add or adjust tests in src/TerminalPortfolio.test.tsx.

Validation Checklist

Before considering changes done, run:

pnpm lint
pnpm test
pnpm build

Releases

Packages

Contributors

Languages