<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Web Development on Neil's Space</title><link>http://neilmin.com/tags/web-development/</link><description>Recent content in Web Development on Neil's Space</description><image><title>Neil's Space</title><url>http://neilmin.com/images/papermod-cover.png</url><link>http://neilmin.com/images/papermod-cover.png</link></image><generator>Hugo</generator><language>en-US</language><lastBuildDate>Mon, 06 Apr 2026 13:30:00 -0700</lastBuildDate><atom:link href="http://neilmin.com/tags/web-development/index.xml" rel="self" type="application/rss+xml"/><item><title>How I Vibe-Coded This Blog Website Just to Publish One Post</title><link>http://neilmin.com/posts/building-my-hugo-blog-with-github-pages/</link><pubDate>Mon, 06 Apr 2026 13:30:00 -0700</pubDate><guid>http://neilmin.com/posts/building-my-hugo-blog-with-github-pages/</guid><description>How I built a bilingual Hugo blog on GitHub Pages with PaperMod, Vercount analytics, Giscus comments, a custom domain, and a few lessons from debugging a frustrating 404 deployment issue.</description><content:encoded><![CDATA[<p>This whole thing, honestly, was basically the software version of buying a dish of vinegar and ending up making an entire batch of dumplings.</p>
<p>A while ago, I ran into a bug that was both obscure and genuinely hard to fix. Stack Overflow was not giving me anything useful. Google was not giving me much either. In the end I had to grind through it myself and solve it the hard way.</p>
<p>Once I finally got it resolved, my first reaction was that I should write the whole winding story down.</p>
<p>Because if I do not write down problems like that, there is a very good chance that a few months later even I will no longer remember how I reasoned my way through them. And after burning that many brain cells on one issue, not turning it into something useful would feel like a waste.</p>
<p>But then the obvious problem showed up:</p>
<p>I had written the post. But where exactly was I supposed to publish it?</p>
<h2 id="the-real-starting-point-was-not-i-want-a-personal-website">The real starting point was not &ldquo;I want a personal website&rdquo;</h2>
<p>I did not begin with some grand plan to build a personal website.</p>
<p>My actual thought process was much simpler: I just wanted a decent place to publish that debugging write-up.</p>
<p>So I spent some time researching where it should live.</p>
<p>On the Chinese internet, platforms like CSDN, Zhihu, and Blog Garden were all options. It was not like I had nowhere to post. But they all felt too constrained, and some of those platforms have a reputation for pulling stunts that make your content feel like it does not fully belong to you anymore. A clean technical post gets wrapped in platform noise, and that was not what I wanted.</p>
<p>Platforms like Medium or Blogger feel lighter, sure, but at the end of the day you are still renting space in someone else&rsquo;s house. You can decorate it a little, but the walls are not yours, the address is not yours, and if the platform changes its rules, some part of your setup stops being fully under your control.</p>
<p>I was not doing this to monetize anything either. As an engineer, I ended up at the most predictable conclusion possible:</p>
<p>If I wanted full control, I should just build the thing myself.</p>
<p>And once that idea showed up, the rest of the stack choice became pretty straightforward. Static hosting, free infrastructure, a workflow that fits naturally with Git, and no need to think too hard about servers. It is hard to imagine something more like a proper little digital home than <a href="https://pages.github.com/">GitHub Pages</a>.</p>
<h2 id="if-i-was-going-to-build-it-i-might-as-well-build-it-properly">If I was going to build it, I might as well build it properly</h2>
<p>When it came to choosing the framework, I looked at the usual suspects first.</p>
<ul>
<li><a href="https://hexo.io/">Hexo</a> on the Node.js side</li>
<li><a href="https://jekyllrb.com/">Jekyll</a>, the old GitHub Pages classic in the Ruby world</li>
<li><a href="https://gohugo.io/">Hugo</a>, which has a reputation for being absurdly fast</li>
</ul>
<p>After comparing them a bit, I ended up choosing Hugo.</p>
<p>The reason was actually very simple: I had seen another site built with Hugo, liked how it looked overall, and for a site this small, the technical differences between these frameworks are not life-or-death anyway.</p>
<p>For the theme, I went with <a href="https://github.com/adityatelange/hugo-PaperMod">PaperMod</a>.</p>
<p>I like how restrained it is. The default look is clean, not noisy, and it keeps the content front and center. For someone like me, who mostly wants to write technical posts and keep a lightweight personal site around them, it was a really solid starting point.</p>
<p>If you want to build something similar, the basic setup is honestly pretty small:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># 1. Install Hugo (macOS)</span>
</span></span><span style="display:flex;"><span>brew install hugo
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 2. Create the site</span>
</span></span><span style="display:flex;"><span>hugo new site my-blog
</span></span><span style="display:flex;"><span>cd my-blog
</span></span><span style="display:flex;"><span>git init
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 3. Add PaperMod as a submodule</span>
</span></span><span style="display:flex;"><span>git submodule add https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
</span></span></code></pre></div><p>There is one small detail here that is worth calling out very explicitly: <strong>PaperMod really should be added as a submodule.</strong></p>
<p>Do not just <code>git clone</code> it into <code>themes/</code> and assume you are done. That can look perfectly fine locally and still bite you later when you push to GitHub. Nested repositories and theme directories do not always behave the way people expect, and it is a very easy way to end up with a mysteriously broken site, missing theme files, or a pretty embarrassing 404 situation after deployment. I am, unfortunately, speaking from lived experience here.</p>
<h2 id="the-funniest-part-is-that-vibe-coding-actually-worked-really-well">The funniest part is that Vibe Coding actually worked really well</h2>
<p>If this had happened a few years ago, the project would still have been manageable, but it absolutely would have been the kind of thing that quietly consumed an entire weekend.</p>
<p>You would have to set up the theme, wire the Hugo config, decide how bilingual routing should work, figure out deployment, get GitHub Actions going, deal with the custom domain, tweak the styling, wire comments, wire analytics, and then spend way too much time on front-end details that individually look tiny but collectively refuse to go away.</p>
<p>Now we live in the era of <strong>Vibe Coding</strong>.</p>
<p>What really stood out to me this time is that AI was not most useful because it magically knew what I wanted. It was useful because once I knew the direction, it could help me chew through a huge amount of tedious, fragmented, annoying implementation work.</p>
<p>This site did not come together because an AI produced one perfect answer. It came together because I kept refining the target and using AI to try things with me:</p>
<ul>
<li>how to structure bilingual routes</li>
<li>how to pair English and Chinese content files</li>
<li>how to make the language switcher prefer the current page&rsquo;s translation instead of always dumping you back on the homepage</li>
<li>how fast the animated glow background should move</li>
<li>how to make it visible enough in light mode</li>
<li>what to use for page views after the first counter turned out to be flaky</li>
<li>how to wire comments so the UI language follows the page language</li>
</ul>
<p>And a lot of those details were not &ldquo;one and done&rdquo; decisions. We went back and forth on them many times.</p>
<p>The bilingual content convention, for example, ended up being very simple:</p>
<ul>
<li>English lives at <code>/</code></li>
<li>Chinese lives at <code>/zh/</code></li>
<li>default English content files stay unsuffixed</li>
<li>Chinese translations use <code>.zh.md</code></li>
</ul>
<p>So it looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>content/posts/my-post.md
</span></span><span style="display:flex;"><span>content/posts/my-post.zh.md
</span></span></code></pre></div><p>Even with vibe coding making things much easier, none of this really came out perfect in one shot. A lot of the site was not &ldquo;AI gave me the final answer.&rdquo; It was much closer to me standing next to a very fast collaborator and repeatedly saying:</p>
<p>This is not right, move it a bit left.</p>
<p>That animation is too fast, slow it down.</p>
<p>That color is ugly, try another one.</p>
<p>This interaction feels like magnets. I want it to feel like something soft being gently pushed aside.</p>
<p>That is why I have grown to like this workflow so much. I am not personally typing every line, but I am still doing the design work, the judgment, and the steering. AI feels less like a replacement and more like a teammate with a lot of execution stamina who does not get tired of being told to tweak things one more time.</p>
<h2 id="once-i-started-i-could-not-help-making-it-more-complete">Once I started, I could not help making it more complete</h2>
<p>Originally I only wanted a place to publish one technical post.</p>
<p>But once the site was half-built, the dangerous sentence showed up in my head:</p>
<p>Well, since I am already here, I might as well finish it properly.</p>
<p>And that was how things escalated.</p>
<h3 id="1-bilingual-support">1. Bilingual support</h3>
<p>I already knew I wanted the site to support both Chinese and English, so from the beginning this was not designed as a single-language blog.</p>
<p>The current structure is:</p>
<ul>
<li>English at <code>/</code></li>
<li>Chinese at <code>/zh/</code></li>
<li>posts, search, and resume pages all follow the bilingual model</li>
</ul>
<p>On paper, that sounds like just a few Hugo config settings. In reality, the annoying part is all the edges: should menus be localized, should search be language-specific, should the language switcher jump to the homepage or the translated page, should the comment widget switch its UI language too? None of those decisions is huge on its own, but together they determine whether the site feels properly bilingual or merely &ldquo;technically available in two languages.&rdquo;</p>
<h3 id="2-github-actions-deployment">2. GitHub Actions deployment</h3>
<p>For deployment, I kept it simple and just let GitHub Actions handle the job.</p>
<p>The Pages workflow is already in <code>.github/workflows/gh-pages.yml</code>, and the day-to-day workflow is basically:</p>
<ol>
<li>Change content or styling locally</li>
<li><code>git add</code></li>
<li><code>git commit</code></li>
<li><code>git push</code></li>
</ol>
<p>Then CI/CD takes care of the rest.</p>
<p>This has one dangerous side effect: it lowers the cost of polishing things so much that you start fixing everything. Sometimes even one sentence that feels slightly off is enough to trigger a whole new commit.</p>
<p>And honestly, Hugo is very pleasant in this setup. Build with <code>hugo --gc --minify</code>, let GitHub Pages host the output, let GitHub Actions publish it, and you are done. There is almost no extra ops drama in the middle, which is exactly the level of complexity I wanted for a content-first site.</p>
<h3 id="3-buying-the-domain">3. Buying the domain</h3>
<p>Using <code>neilmin.github.io</code> would have been perfectly fine.</p>
<p>But then I made the mistake of checking domain prices and discovered that a <code>.com</code> with my name on it cost something like ten or twelve dollars a year. That is a very effective way to destroy a person&rsquo;s self-control. For less than the cost of a meal, I could have a little corner of the internet with my own name on it. Hard to argue with that.</p>
<p>So I bought <code>neilmin.com</code>.</p>
<p>Functionally, a custom domain does not change much. Emotionally, it changes a lot. The moment you type that URL into the browser, the site immediately feels more real and more yours.</p>
<h2 id="after-the-basics-worked-the-little-extras-became-the-most-addictive-part">After the basics worked, the little extras became the most addictive part</h2>
<p>Once a site like this is functional, the next wave of fun is usually not &ldquo;can it work?&rdquo; but &ldquo;can I make it feel a little nicer?&rdquo;</p>
<p>That was the point where I started adding extra little toys.</p>
<h3 id="the-animated-glowing-background">The animated glowing background</h3>
<p>If you are reading this on a desktop, the homepage and search page should have a few softly moving glowing shapes in the background.</p>
<p>That effect is not an image or a video. It is just CSS plus a bit of JavaScript. It started as a mesh-gradient-like atmosphere thing, and then I kept iterating until it became much more interactive.</p>
<p>The funny part is how many rounds it took to get the feel right. The first version looked too mechanical. Another version felt weirdly magnetic and slippery. Eventually I rewrote the interaction to feel more like soft bubbles being gently pushed aside, using <code>lerp</code>-style easing and a smoother falloff curve. Now when you move the mouse through it, it should feel more like you are nudging something viscous than poking three glowing objects that want to fly away.</p>
<h3 id="page-views-from-busuanzi-to-vercount">Page views: from Busuanzi to Vercount</h3>
<p>At first I tried the usual Chinese blog counter, Busuanzi.</p>
<p>But these days its reliability feels&hellip; situational. So I eventually switched to <a href="https://www.vercount.one/">Vercount</a>.</p>
<p>What I really liked there was that it is almost a drop-in replacement for Busuanzi at the DOM level.</p>
<p>In practice that meant I barely had to touch the existing <code>busuanzi_*</code> IDs. I could swap the script source, keep the markup mostly intact, and move on. That is exactly the kind of migration I appreciate when something is already wired into templates and I do not want to rebuild the whole feature from scratch.</p>
<h3 id="giscus-comments">Giscus comments</h3>
<p>For comments, I ended up choosing <a href="https://giscus.app/">Giscus</a>.</p>
<p>I really like the model: no separate database, no extra backend to maintain, just GitHub Discussions underneath. For a small site that already treats GitHub as home base, that is a very natural fit.</p>
<p>And it matches the rest of the site nicely:</p>
<ul>
<li>no custom comment backend to operate</li>
<li>dark mode support out of the box</li>
<li>comment UI language can follow the page language</li>
</ul>
<p>So now the Chinese posts render a Chinese Giscus UI, and the English posts render an English one. Those details are small, but once they line up, the whole site feels much more coherent.</p>
<p>And honestly, this is exactly the kind of thing that makes projects like this addictive. You think you are just adding comments, and then a minute later you care about whether the comment UI language switches correctly and whether the theme tracks light/dark mode properly too.</p>
<h2 id="looking-back-this-really-was-a-full-on-dumpling-project">Looking back, this really was a full-on dumpling project</h2>
<p>At the beginning, I just wanted to publish one technical write-up.</p>
<p>By the end, I had:</p>
<ul>
<li>set up Hugo</li>
<li>wired in PaperMod</li>
<li>implemented bilingual support</li>
<li>added search</li>
<li>configured GitHub Pages deployment</li>
<li>bought a custom domain</li>
<li>added visitor stats</li>
<li>added comments</li>
<li>tuned the animated background</li>
<li>refined the interaction feel</li>
</ul>
<p>All that because I wanted somewhere to post one debugging story.</p>
<p>And honestly, I am glad it happened.</p>
<p>On one level it solved a very practical problem: I now have a place that is fully mine, where I can publish the kinds of things I actually want to write. On another level, it brought back a very familiar kind of engineering joy, the kind that says, &ldquo;Well, since I already got this far, I might as well make it a little better.&rdquo;</p>
<h2 id="final-thoughts">Final thoughts</h2>
<p>That original bug write-up is now out in the world.</p>
<p>And this site, in a way, is the side effect it dragged into existence.</p>
<p>If you happened to land on this post, feel free to scroll down and leave a comment. Log in with GitHub, say hi, and if you want, help me test whether this whole setup is actually as stable as I think it is.</p>
<p>Because for all the extra polish it has now, the origin story of this site is still extremely simple:</p>
<p>I just wanted a good place to publish one post that felt worth preserving.</p>
]]></content:encoded></item></channel></rss>