PROJECTSACHIEVEMENTSBLOGPOEMSGALLERYCONTACT
← Back to projects

My old site lived (still continues to, I'll take it down on 16th July 2026, my birthday) at imalharr.github.io. Vanilla HTML, CSS, a bit of JS. I built it in 2024 and then kept patching it instead of rebuilding it, which meant it aged badly. The design was fine for a 14-year-old but I'm not 14 anymore. So this summer I finally killed it and built something that actually feels like me.

The stack is Next.js 15 with the App Router, TypeScript, Tailwind v4, shadcn/ui, and Keystatic as the CMS. Keystatic specifically because it commits content directly to GitHub as markdown files, no database, no subscription, completely free. I'm on Vercel's Hobby plan so the whole thing costs nothing to run.

The design system came first. One background color (#070b0a), one accent (#4ADE80), and a rule that those are the only two unless you're doing something in the background layer where cyan is allowed. Four fonts as CSS variables. All CSS lives in globals.css, zero Tailwind utility classes inside components. I made that rule early and it saved me from the usual Tailwind mess where you can't tell what's doing what.

The hero took the longest. It has a LetterGlitch canvas underneath everything (ported from React Bits), a PCB circuit-trace background with animated signal traces and traveling glow pulses, and a HackerText component on the heading that scrambles on mount. The two-column layout with my photo on the right came later, after I realized the full-width text version felt too sparse.

The weirdest feature is the poem gate. My poems section is locked behind a two-step mechanism: first you tune a frequency slider to a specific value (it means something, I'm not explaining it), then you enter a passphrase. Three wrong attempts and it locks out for the session. The whole thing is a canvas static noise visualizer with a custom slider. It's probably the thing on this site I'm most proud of because it's completely unnecessary and I built it anyway.

The roughest part of the build wasn't the design at all. It was a missing <a tag in a project page file. Nine cascading TypeScript errors, all pointing to different things, none pointing at the real problem. Every time I pasted the fix, the <a silently got dropped somewhere in the Windows clipboard. I eventually gave up and wrote the entire file directly to disk with a cat > heredoc command in the terminal. That's the kind of thing they don't warn you about.

The other good one was the HackerText hydration mismatch. The component was initializing with a random scramble, which meant server and client rendered different text, which React correctly rejected. Fix was to initialize state with the real text and only start scrambling in useEffect. Obvious in hindsight, annoying to debug at midnight.

Keystatic's image fields inside arrays also don't behave the way the docs suggest: instead of returning a plain string path, they return an object with filename, extension, and data properties. Had to write a type union (KsImg) and a resolver function to handle both cases, because it switches behavior depending on whether you're in local or GitHub mode.

The site's still in progress. Blog migration, contact form, mobile polish, SEO. The kind of stuff that isn't fun to build but is annoying to not have. The repo's private for now, until I'm sure everything is actually how I want it. Maybe someday.