Why rebuild it
The first version of stevesuhr.com was a Wix site. It did the job, but I didn't own any of it. For a software engineer's resume, the site itself should be evidence. So I rebuilt it by hand, with two goals: a showcase I could point people to, and a sandbox for learning Next.js and Postgres on a real deployed app instead of a tutorial.
Stack
- Next.js 15 + React 19
- App Router, server components, and server actions. The resume page is prerendered as static HTML; the side projects render on the server per request.
- TypeScript
- Used for everything new. The original resume components are still plain JavaScript and get converted when I touch them.
- Tailwind CSS + CSS custom properties
- Tailwind for layout utilities, plus a small set of hand-written design tokens for color and type so light and dark mode come from one place.
- NextAuth (Google sign-in)
- Real authentication for the side projects without storing passwords myself.
- Prisma + Postgres on Neon
- A typed ORM over serverless Postgres. Sessions and user data live in the same database as the projects that use them.
- Vercel
- Hosting and deploys from GitHub. Every pull request gets a preview build.
How it evolved
v1: the flashy version
Aug 2025A full-screen hero with a parallax background, GSAP scroll animations, and gradient cards for each job. It was a good way to learn the framework, and it looked like a lot of other developer portfolios.
Sandbox features
Aug 2025Added Google sign-in, then Postgres through Prisma, then a small signed-in to-do app to exercise the whole path: auth, database, server actions, and per-user data access checks.
Midway through I migrated from the Pages Router to the App Router and upgraded to Next.js 15. Running both routers side by side for a while made the tradeoffs concrete, especially around where session data gets loaded.
v2: the document redesign
Sep 2026I replaced the hero, parallax, and animations with a layout that reads like a well-edited document: a masthead, a sticky table of contents, and numbered sections. The content is the same; it's just much easier to skim.
Design decisions
- Built for skimming. Most people give a resume under a minute. Numbered sections and a table of contents let a reader jump straight to experience, and the numbers come from CSS counters, so adding or hiding a section never means renumbering by hand.
- Three typefaces, three jobs. Serif for names and headings, sans for body copy, and monospace for metadata like dates, locations, and skills. You can tell what a line is before you read it.
- One set of color tokens. Warm neutrals with a single amber accent, defined once as CSS variables and redefined for dark mode. Secondary text meets WCAG AAA contrast (7:1) in both themes, after feedback that the gray was hard to read on a phone in bright light.
- Static where it can be. The layout used to read the session on every request, which forced the resume to render dynamically. Moving the auth check into a client component let the resume be prerendered as static HTML, while the signed-in projects still render on the server.
- Motion only where it helps. The scroll animations are gone. What's left is small hover feedback on links, and it turns off for anyone with reduced motion enabled.
- Mobile first-class. Below 900px the two-column layout collapses, and the table of contents becomes a compact grid at the top instead of a sidebar.
Tools
I used AI coding assistants (OpenAI Codex and Anthropic's Claude) for parts of the work, like the App Router migration and the v2 restyle. I made the design and architecture calls and reviewed every change before it merged, the same way I would with a teammate's pull request.