This site is both my portfolio and the home of my sole proprietorship. It had to be built once, properly — not as yet another project I tear down in two years. Here are the three decisions that shaped it.
Hosting: read the terms
Vercel Hobby is technically perfect for a Next.js site — but its terms only allow non-commercial use. A site meant to bring a business customers is commercial. So the plan is Cloudflare Workers at launch (free, commercial use allowed), with Vercel as a preview environment during development.
The key move is keeping the code hosting-agnostic: standard Next.js with no platform-specific APIs. That makes switching hosts a pipeline change, not a rewrite.
Tokens as the single source of color
Every color on the site comes from CSS variables in one file. Never pure white, never pure black, and both modes are hand-picked — dark mode is not "inverted light".
:root {
--color-bg: #f1f1f1;
--color-fg: #1a1a1e;
--color-accent: #0d6b85; /* 5.3:1 against the background */
}
[data-theme="dark"] {
--color-bg: #0e0e11;
--color-fg: #e8e8e6;
--color-accent: #35d0e8; /* 9.0:1 against the background */
}The contrast comments aren't decoration: CI runs Lighthouse requiring a perfect accessibility score, and an accent color that doesn't reach 4.5:1 fails the whole build. My first accent color actually failed exactly that check.
A budget with veto power
Every push runs lint, typecheck, build and Lighthouse with a budget: performance ≥ 95, accessibility = 100. It sounds strict, but that's the point — the budget has veto power over design ideas. An animation or library that can't stay within budget doesn't get in. This discipline is cheap now and impossible to retrofit.
The rest of the stack: Next.js App Router, strict TypeScript, Tailwind v4, next-intl for Norwegian and English, and Velite as a typed content layer — all content is markdown files in the repo, fetched through one contract that can swap backends later without the pages noticing.
The site you're reading this on is itself the result. The source code shows the rest.