<?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/" xmlns:wc="https://wilhelm.codes/ns/feed/1.0"><channel><title>Wilhelm Codes · Css</title><link>https://wilhelm.codes/tags/css/</link><description>Slinger of nibbles, bits and bytes. Over 25 years of professional experience as a software engineer. Love making glowing rectangles go "beep-boop".</description><generator>Hugo</generator><language>en-US</language><managingEditor>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</managingEditor><webMaster>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</webMaster><lastBuildDate>Sun, 23 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://wilhelm.codes/tags/css/index.xml" rel="self" type="application/rss+xml"/><item><title>Some Long Overdue Demolition</title><link>https://wilhelm.codes/blog/some-long-overdue-demolition/</link><pubDate>Sun, 23 Aug 2026 00:00:00 +0000</pubDate><author>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</author><guid>https://wilhelm.codes/blog/some-long-overdue-demolition/</guid><category>hugo</category><category>css</category><category>performance</category><category>wilhelm.codes</category><wc:kind>post</wc:kind><description>Two months ago I wrote Some Long Overdue Housekeeping. A post about giving this blog a proper facelift. I untangled my Tailwind setup, modernised Hugo and taught it to drive the compiler natively and generally felt very pleased with the state of things.
So, of course just weeks later I have this burning desire to nuke it all from orbit!</description><content:encoded><![CDATA[<p>Two months ago I wrote <a href="https://wilhelm.codes/blog/some-long-overdue-housekeeping/">Some Long Overdue Housekeeping</a>. A post about giving this blog a proper facelift. I untangled my Tailwind setup, modernised Hugo and taught it to drive the compiler natively and generally felt very pleased with the state of things.</p>
<p>So, of course just weeks later I have this burning desire to nuke it all from orbit!</p>
<figure class="pull-quote">
  <span class="pull-quote-mark" aria-hidden="true"><span class="icon "><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M108 72v72H40a8 8 0 0 1-8-8V72a8 8 0 0 1 8-8h60a8 8 0 0 1 8 8m108-8h-60a8 8 0 0 0-8 8v64a8 8 0 0 0 8 8h68V72a8 8 0 0 0-8-8" opacity=".2"/><path d="M100 56H40a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h60v8a32 32 0 0 1-32 32a8 8 0 0 0 0 16a48.05 48.05 0 0 0 48-48V72a16 16 0 0 0-16-16m0 80H40V72h60Zm116-80h-60a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h60v8a32 32 0 0 1-32 32a8 8 0 0 0 0 16a48.05 48.05 0 0 0 48-48V72a16 16 0 0 0-16-16m0 80h-60V72h60Z"/></g></svg></span></span>
  <blockquote>
    <p>This means no more standalone Tailwind config, no committed stylesheet, no <code>node_modules</code> in <code>static/</code> and no build toolchain leaking onto the live site. Very cool!</p>
  </blockquote>
  <figcaption>
    <cite>me, in June, admiring a thing I have since bulldozed</cite>
  </figcaption>
</figure>

<p>To be clear, the June cleanup wasn&rsquo;t <em>wrong</em>. The pipeline was genuinely tidy. But, a tidy pipeline is still a pipeline and somewhere between then and now I started asking a more annoying question: why does a personal blog, a pile of static text, need a JavaScript toolchain at all? Why do I need to buy into the Tailwind framework when I knew full well how to write vanilla CSS with my own hands? Every <code>npm install</code> was a little tax that I&rsquo;d eventually have to pay with interest the next time I wanted to update the site&rsquo;s dependencies. I felt as if I wasn&rsquo;t maintaining a blog. I was maintaining the machinery that produces a blog and the machinery had some very strong opinions.</p>
<p>So, I did a hard reset with a <em>very</em> satisfying amount of deleting.</p>
<h2 id="the-demolition-">
  <a class="heading-link" href="#the-demolition-">The demolition &hellip;<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>Here&rsquo;s the bill for the redesign commit:</p>
<div class="code-block">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">230 files changed, 3951 insertions(+), 33365 deletions(-)</span></span></code></pre></div>
</div>
<p>That&rsquo;s roughly a 10-to-1 ratio of deleted to written. Which, as a software engineer, is my favourite kind of ratio. I happily added the following the to chopping block:</p>
<ul>
<li><strong>Tailwind v4 and the typography plugin.</strong> Replaced by one hand-written CSS file. The whole thing gzips to under <code>~10kB</code> with syntax highlighting included.</li>
<li><strong>The entire Node toolchain.</strong> The <code>package.json</code>, lockfile, Prettier and its plugins. There is no <code>npm install</code> step anymore because there is no npm anymore.</li>
<li><strong>Fuse.js and the ⌘K search palette.</strong> Nobody was searching this blog. I checked. It was me. I was nobody.</li>
<li><strong>Two self-hosted webfonts.</strong> More on this in a second, because it&rsquo;s the biggest single win.</li>
<li><strong>The changelog page.</strong> This pulled from the GitHub events API and shipped with an <code>18,016</code>-line JSON fixture for local dev. Eighteen <em>thousand</em> lines of someone else&rsquo;s API response, committed to my repo. So, a page barely anybody visited could render in development. I mean, I added it back becase <em>I</em> missed it, but it&rsquo;s <em>much</em> more efficient now.</li>
<li><strong>68 of the 79 icon SVGs.</strong> A 390-line drawer menu and a taxonomy I&rsquo;d stopped using.</li>
</ul>
<p>The layouts went from <code>2,868</code> lines of template across <code>48</code> files to about <code>800</code> lines across <code>21</code>. Not because I got cleverer, but because most of that code was written for the joy of it. Which, look, is a fine reason to build something. It&rsquo;s just a bad reason to keep serving it to strangers. And with all that complexity and cruft came what felt like a lot of brittleness.</p>
<h2 id="the-fonts-had-to-go-too">
  <a class="heading-link" href="#the-fonts-had-to-go-too">The fonts had to go too.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>This one hurt a little. I like Inter and Rubik. Together they were <code>449kB</code> of <code>woff2</code> riding along on every first visit. Which made them, by an enormous margin, the heaviest thing on the site.</p>
<p>So, I dropped them and switched to the system font stack which, to my surprise, actually looked <em>better</em> on the site. The actual typeface was doing very little. Your operating system already ships a perfectly good set. All lovingly hinted by people with far more patience and experience than myself. Best of all it costs zero bytes to use!</p>
<figure class="pull-quote">
  <span class="pull-quote-mark" aria-hidden="true"><span class="icon "><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M108 72v72H40a8 8 0 0 1-8-8V72a8 8 0 0 1 8-8h60a8 8 0 0 1 8 8m108-8h-60a8 8 0 0 0-8 8v64a8 8 0 0 0 8 8h68V72a8 8 0 0 0-8-8" opacity=".2"/><path d="M100 56H40a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h60v8a32 32 0 0 1-32 32a8 8 0 0 0 0 16a48.05 48.05 0 0 0 48-48V72a16 16 0 0 0-16-16m0 80H40V72h60Zm116-80h-60a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h60v8a32 32 0 0 1-32 32a8 8 0 0 0 0 16a48.05 48.05 0 0 0 48-48V72a16 16 0 0 0-16-16m0 80h-60V72h60Z"/></g></svg></span></span>
  <blockquote>
    <p>By Grabthar's hammer... What a savings.</p>
  </blockquote>
  <figcaption>
    <cite>Dr. Lazarus from Galaxy Quest</cite>
  </figcaption>
</figure>

<h2 id="hugo-finally-home-alone">
  <a class="heading-link" href="#hugo-finally-home-alone">Hugo, finally home alone.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>Thanks to all the trimmed fat, the build process is effectively just <code>hugo</code>. No more extended binary, SCSS, image processing or defining <code>NODE_VERSION</code> in Cloudflare Pages required. Fewer dependencies mean fewer moving parts and that&rsquo;s always an improvement.</p>
<h2 id="numbers-or-it-didnt-happen">
  <a class="heading-link" href="#numbers-or-it-didnt-happen">Numbers, or it didn&rsquo;t happen.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>Right after the deploy I proudly measured the new site and announced it was &ldquo;under <code>100kB</code>&rdquo;. Which would have been true if the production site were served by my dev server, uncompressed, with a <code>40kB</code> livereload script attached. I made the mistake of measuring the network stats on my local development server. <em>Womp, womp</em>.</p>
<p>The real numbers, from the live site with Cloudflare&rsquo;s <code>brotli</code> doing its thing:</p>
<table>
	<thead>
			<tr>
					<th></th>
					<th>Before</th>
					<th>After</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Cold home page</td>
					<td><code>~750kB</code></td>
					<td><code>~24kB</code></td>
			</tr>
			<tr>
					<td>Webfonts</td>
					<td><code>449kB</code></td>
					<td><code>0kB</code></td>
			</tr>
			<tr>
					<td>Stylesheet ( compressed )</td>
					<td>it&rsquo;s complicated</td>
					<td>under <code>~10kB</code></td>
			</tr>
			<tr>
					<td><code>npm install</code></td>
					<td>required</td>
					<td>there is nothing to install</td>
			</tr>
	</tbody>
</table>
<p>The single largest asset on the home page is now the favicon. <code>7.5kB</code>; roughly a third of the total weight. I think you&rsquo;re allowed to stop optimising when your largest single asset is your <code>favicon.ico</code>. It&rsquo;s an image of my gorgeous mug. It stays. I&rsquo;m quite attached to it.</p>
<h2 id="an-ambitious-first-pass">
  <a class="heading-link" href="#an-ambitious-first-pass">An ambitious first pass.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>In retrospect, I wanted to make everything simpler and unintentionally over-compensated in the opposite direction. I stripped out too much and was now staring at a sad little blog with zero personality.</p>
<p>So, the second half of the job was building the personality back and making my selections a bit more deliberate. One thing led to another and I think I&rsquo;ve produced my finest work yet on a personal blog. It&rsquo;s small and lightening fast. I&rsquo;m not sure where else I can optimise without drastically affecting the overall vibe of the design; the law of diminishing returns&hellip; returns.</p>
<p>The thing about now having such a light frame is adding to it stops feeling like a burden, or something future Wilhelm will have to pay. For example, here are the biggest things I&rsquo;ve added and they <em>barely</em> increased the overall &ldquo;weight&rdquo; of the design.</p>
<h3 id="series">
  <a class="heading-link" href="#series">Series<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h3>
<p>A proper taxonomy for multi-part writing. Built because I&rsquo;ve been wanting to write a Cyberpunk-themed set of tutorials for writing advanced HCL in Go. The series will be about eight posts long and outside of sharing a tag, I had no way to string them together. Series posts wear a stacked-sheet card, a segmented progress meter that is absolutely <em>not</em> a cyberdeck RAM readout and a panel showing every part in the run, including the ones that don&rsquo;t exist yet.</p>
<p>The unpublished parts of a series ship as glyph noise and decode, left to right, when you point at them. The real titles sit in <code>aria-label</code> the whole time, so a screen reader is never handed the static. The browser tab pulls the same trick on every page load, because a title bar is just a very small marquee nobody was using.</p>
<p>Try moving your mouse pointer over some of the &ldquo;encrypted&rdquo; titles on the new <a href="https://wilhelm.codes/series">dedicated</a> page.</p>
<h3 id="tired-leds">
  <a class="heading-link" href="#tired-leds">Tired LEDs<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h3>
<p>The lit chips in the series meters, static leaders in the main page&rsquo;s hero section and a scattered handful of cells in the activity graph flicker like hardware that has seen some things. Their animation clocks share no common factors, so the pattern never visibly repeats. The same no-RNG randomness as the heading glitch. On page load the whole graph boots like an old panel, dark, then a sputter per cell, then steady stream of blinking boxes. Reminiscent of the old monochrome screens and beige boxes of my youth.</p>
<p>Christ, I&rsquo;m getting old.</p>
<h3 id="changelog-resurrected">
  <a class="heading-link" href="#changelog-resurrected">Changelog, resurrected<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h3>
<p>Yes, the one from the butcher&rsquo;s block above. However, instead of hitting the associated Github repository directly at build time using a PAT, the new one doesn&rsquo;t call anyone. Commit data is sourced <em>directly</em> from Cloudflare Pages&rsquo; shallow clone it checks out during build time. There&rsquo;s a simple 20-or-so-line shell script reads <code>git log</code> and Hugo renders the result. <code>HEAD</code> gets a lit node that flickers, <a href="https://wilhelm.codes/changelog">because why the hell not</a>?</p>
<h3 id="image-shortcodes">
  <a class="heading-link" href="#image-shortcodes">Image shortcodes<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h3>
<p>Something I&rsquo;ve been meaning to add is support for various ways to display images. Each image can now be clicked-to-zoom with attribution in the following forms:</p>
<ul>
<li>Masonry.</li>
<li>A scrollable, horizontal display.</li>
<li>Last, but not least, the typicla single image display.</li>
</ul>
<p>There&rsquo;s actually a sneaky, super-secret page I use to test all my shortcodes and typography stylings where you can <a href="https://wilhelm.codes/blog/markdown-elements/">see everything for yourself</a>.</p>
<p>Even with all these complex additions, it all adds up to a few kilobytes of CSS and inline script. Which is to say the entire second wave of features weighs an order of magnitude less than one of the webfonts I deleted.</p>
<h2 id="my-new-favourite-fidget-toy">
  <a class="heading-link" href="#my-new-favourite-fidget-toy">My new favourite fidget toy.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>The part I didn&rsquo;t expect was to glue my eyeballs to the Firefox throttle menu under its network tools panel.</p>
<p>I flipped it to a 2G profile just for shits and giggles - call it a generous <code>50kb</code> per second - and did the napkin math. The old site&rsquo;s <code>750kB</code> works out to between <code>60</code> to <code>90</code> seconds of staring at a blank viewport. The new site lands in about four seconds on a connection profile most of the industry stopped thinking about a decade ago.</p>
<p>I have reloaded this page on simulated garbage networks far more times than any reasonable person should, and it is satisfying <em>every single time</em>. The page just&hellip; appears. Like a website. Remember plain&rsquo;ole websites?</p>
<p>None of this is novel, I know. The &ldquo;your blog doesn&rsquo;t need <code>2MB</code> of JavaScript&rdquo; essay has been written a hundred times. Usually on a blog serving <code>2MB</code> of JavaScript. I&rsquo;m not even the hundred-and-first person to do this. But, there&rsquo;s a difference between nodding along to the idea and watching your own site load over a simulated tin-can connection before you can finish blinking.</p>
<h2 id="in-closing-">
  <a class="heading-link" href="#in-closing-">In closing &hellip;<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>The funny thing about the June post is that it ended with me feeling like the blog was finally sorted. And it was! For the architecture it had. It just turned out the architecture itself was the thing that needed to go.</p>
<p>The site is now boring in the best possible way. It builds with one command, it has no dependencies to rot behind my back and the whole design fits in one stylesheet I can actually hold in my head ( not really ). Fewer moving parts, fewer things to forget, fewer surprises the next time I touch it in six months. Best of all, it looks fucking great!</p>
<p>Set-and-forget, but this time with almost nothing left to forget. &hellip; Right?</p>]]></content:encoded></item><item><title>Goodbye Tailwind. Goodbye Node. We’re rolling vanilla HTML, CSS and …</title><link>https://wilhelm.codes/bits/blurb-hours-without-webfonts/</link><pubDate>Thu, 20 Aug 2026 20:50:00 +1000</pubDate><author>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</author><guid>https://wilhelm.codes/bits/blurb-hours-without-webfonts/</guid><category>css</category><category>wilhelm.codes</category><wc:kind>bit</wc:kind><wc:bit>blurb</wc:bit><wc:untitled>true</wc:untitled><description>Goodbye Tailwind. Goodbye Node. We&amp;rsquo;re rolling vanilla HTML, CSS and JS. No more webfonts, we on them system faces now. A couple of hours on I have not missed the webfonts once. The site is lighter, the text is sharper on my screen and the only person who ever noticed was me. 750kb cold start for the main page is now under 18kb.
Everything is so much faster now I should&amp;rsquo;ve done this months ago when I first revamped the site.</description><content:encoded><![CDATA[<p>Goodbye Tailwind. Goodbye Node. We&rsquo;re rolling vanilla HTML, CSS and JS. No more webfonts, we on them system faces now. A couple of hours on I have not missed the webfonts once. The site is lighter, the text is sharper on my screen and the only person who ever noticed was me. <code>750kb</code> cold start for the main page is now <em>under</em> <code>18kb</code>.</p>
<p>Everything is so much faster now I should&rsquo;ve done this months ago when I first revamped the site.</p>
]]></content:encoded></item><item><title>Let Me Show You My Bits</title><link>https://wilhelm.codes/blog/let-me-show-you-my-bits/</link><pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate><author>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</author><guid>https://wilhelm.codes/blog/let-me-show-you-my-bits/</guid><category>hugo</category><category>css</category><category>tailwindcss</category><category>wilhelm.codes</category><wc:kind>post</wc:kind><description>A while back, in Reading Between the Posts, I wired up the home page so the quiet gaps between articles would narrate themselves - &amp;ldquo;1 bit and 41 changelog events in between&amp;rdquo;. There was just one tiny problem with that sentence. At the time I had written exactly zero bits.</description><content:encoded><![CDATA[<p>A while back, in <a href="https://wilhelm.codes/blog/reading-between-the-posts/">Reading Between the Posts</a>, I wired up the home page so the quiet gaps between articles would narrate themselves - &ldquo;<em>1 bit and 41 changelog events in between</em>&rdquo;. There was just one tiny problem with that sentence. At the time I had written exactly <em>zero</em> bits.</p>
<p>So let me finally show you my bits.</p>
<h2 id="what-even-is-a-bit">
  <a class="heading-link" href="#what-even-is-a-bit">What even is a bit?<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>A bit is the smallest unit of &ldquo;I made a thing&rdquo; I&rsquo;m willing to commit to. It could be a quote that stuck with me, a link worth keeping, the odd YouTube video, or a one-line thought too small to earn its own long-form entry. Each one is a little markdown file in <code>/bits</code> and  the only thing it really has to declare is what <em>kind</em> of bit it is:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">YAML</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">quote</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">date</span><span class="p">:</span><span class="w"> </span><span class="ld">2026-06-17</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Edsger Dijkstra&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;Simplicity is a great virtue but it requires hard work to achieve it.&#34;</span></span></span></code></pre></div>
</div>
<p><code>type</code> is the whole trick that everything else hangs off.</p>
<h2 id="one-shape-per-thought">
  <a class="heading-link" href="#one-shape-per-thought">One shape per thought.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>The thing I didn&rsquo;t want was for a pithy quote to render like a bare link or to render like a video. They&rsquo;re different shapes of thought, so they ought to look different.</p>
<p>I&rsquo;d already learned this lesson building the <a href="https://wilhelm.codes/blog/a-changelog-that-builds-itself/">changelog</a>, which leans on one tiny partial per event type. So I stole from myself. There&rsquo;s a single lookup table mapping each <code>type</code> to an icon, an accent colour and a label:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Go</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="err">$</span><span class="nx">meta</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">dict</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;quote&#34;</span><span class="w"> </span><span class="p">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;icon&#34;</span><span class="w"> </span><span class="s">&#34;quote-right&#34;</span><span class="w">    </span><span class="s">&#34;colour&#34;</span><span class="w"> </span><span class="s">&#34;rose&#34;</span><span class="w">    </span><span class="s">&#34;label&#34;</span><span class="w"> </span><span class="s">&#34;quote&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;link&#34;</span><span class="w">  </span><span class="p">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;icon&#34;</span><span class="w"> </span><span class="s">&#34;arrow-up-right&#34;</span><span class="w"> </span><span class="s">&#34;colour&#34;</span><span class="w"> </span><span class="s">&#34;blue&#34;</span><span class="w">    </span><span class="s">&#34;label&#34;</span><span class="w"> </span><span class="s">&#34;link&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;video&#34;</span><span class="w"> </span><span class="p">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;icon&#34;</span><span class="w"> </span><span class="s">&#34;youtube&#34;</span><span class="w">        </span><span class="s">&#34;colour&#34;</span><span class="w"> </span><span class="s">&#34;red&#34;</span><span class="w">     </span><span class="s">&#34;label&#34;</span><span class="w"> </span><span class="s">&#34;video&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;blurb&#34;</span><span class="w"> </span><span class="p">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;icon&#34;</span><span class="w"> </span><span class="s">&#34;pen-nib&#34;</span><span class="w">        </span><span class="s">&#34;colour&#34;</span><span class="w"> </span><span class="s">&#34;emerald&#34;</span><span class="w"> </span><span class="s">&#34;label&#34;</span><span class="w"> </span><span class="s">&#34;blurb&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}}</span></span></span></code></pre></div>
</div>
<p>It then matches a micro-partial per type that decides how the content actually renders. Adding a brand new kind of bit is now &ldquo;add one row, add one tiny template&rdquo;, which is about as many steps as I&rsquo;m willing to tolerate.</p>
<p><img
  src="https://wilhelm.codes/blog/let-me-show-you-my-bits/bits_hu_88740d4c34822745.webp"
  srcset="/blog/let-me-show-you-my-bits/bits_hu_88740d4c34822745.webp 736w, /blog/let-me-show-you-my-bits/bits_hu_7c64b843d8098172.webp 1104w, /blog/let-me-show-you-my-bits/bits_hu_321d663198ba2c25.webp 1472w"
  sizes="(max-width: 48rem) 100vw, 736px"
  width="736"
  height="596"
  alt=""
  
  loading="lazy"
  decoding="async"
/>
</p>
<h2 id="hugo-why-are-you-like-this">
  <a class="heading-link" href="#hugo-why-are-you-like-this">Hugo, why are you like this?<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>My bits started life with two perfectly reasonable front-matter fields: <code>type</code> and <code>url</code>. Hugo took one look and quietly robbed me of both.</p>
<p>It turns out <code>url</code> is a <em>reserved</em> key which Hugo uses to treat as the page&rsquo;s permalink. So, the moment I put a <code>https://...</code> in there, the build fell over with a stern little message about unsupported protocols. <code>type</code> doesn&rsquo;t even have the decency to error. It silently moves out of <code>.Params.type</code> and into <code>.Type</code>, which <em>also</em> hijacks the layout lookup. So my <code>.Params.type</code> was resolving to nothing and every single bit was quietly rendering as the fallback.</p>
<p>Turns out hte fix for this was simply to rename <code>url</code> to <code>link</code> and  read <code>.Type</code> instead of <code>.Params.type</code>. Half an hour of my life dedicated to two words I didn&rsquo;t know were already spoken for.</p>
<p>Coding is my passion.</p>
<h2 id="bits-flapping-in-the-wind">
  <a class="heading-link" href="#bits-flapping-in-the-wind">Bits flapping in the wind.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>Rather than dumping bits into the same fold-out list as the GitHub noise, I pulled them out as their own first-class timeline rows. The lower-signal changelog chatter stays tucked behind the &ldquo;<em>N changelog events in between</em>&rdquo; disclosure. So, the home page now reads in three tiers:</p>
<ul>
<li>the loud headline articles</li>
<li>the medium-volume bits</li>
<li>the quiet changelog hum</li>
</ul>
<p>The same interstitial machinery I built last time, but bits just get to stand a little taller than a commit.</p>
<p><img
  src="https://wilhelm.codes/blog/let-me-show-you-my-bits/home_hu_e7f8fe2d00bce999.webp"
  srcset="/blog/let-me-show-you-my-bits/home_hu_e7f8fe2d00bce999.webp 736w, /blog/let-me-show-you-my-bits/home_hu_bf5b90ae24a2b65.webp 1104w, /blog/let-me-show-you-my-bits/home_hu_62fda724f7b72c05.webp 1472w"
  sizes="(max-width: 48rem) 100vw, 736px"
  width="736"
  height="310"
  alt=""
  
  loading="lazy"
  decoding="async"
/>
</p>
<h2 id="the-tag-that-went-nowhere">
  <a class="heading-link" href="#the-tag-that-went-nowhere">The tag that went nowhere.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>Bits are deliberately <code>render: never</code> in Hugo. They live inline on the bits page and never get their own URL. Except &ldquo;no page&rdquo; also quietly means &ldquo;no place in the taxonomy&rdquo;, which means a tag only ever used by a bit has no <code>/tags/...</code> page to point at. If I link it anyway I&rsquo;ve shipped a <code>404</code>.</p>
<p>So, the tags only become links if there&rsquo;s actually somewhere to go:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Go</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">site</span><span class="p">.</span><span class="nf">GetPage</span><span class="w"> </span><span class="p">(</span><span class="nx">printf</span><span class="w"> </span><span class="s">&#34;/tags/%s&#34;</span><span class="w"> </span><span class="p">(</span><span class="nx">urlize</span><span class="w"> </span><span class="p">.))</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">&lt;</span><span class="nx">a</span><span class="w"> </span><span class="nx">href</span><span class="p">=</span><span class="s">&#34;{{ .RelPermalink }}&#34;</span><span class="p">&gt;</span><span class="err">#</span><span class="p">{{</span><span class="w"> </span><span class="p">.</span><span class="nx">Data</span><span class="p">.</span><span class="nx">Term</span><span class="w"> </span><span class="p">}}&lt;</span><span class="o">/</span><span class="nx">a</span><span class="p">&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">&lt;</span><span class="nx">span</span><span class="p">&gt;</span><span class="err">#</span><span class="p">{{</span><span class="w"> </span><span class="p">.</span><span class="w"> </span><span class="p">}}&lt;</span><span class="o">/</span><span class="nx">span</span><span class="p">&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="nx">end</span><span class="w"> </span><span class="p">}}</span></span></span></code></pre></div>
</div>
<p>A tag with a real page becomes a link and a bit-only tag stays as plain text. No broken links and the nice part is it heals itself the moment a proper article picks up the same tag.</p>
<h2 id="in-closing-">
  <a class="heading-link" href="#in-closing-">In closing &hellip;<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>The front page has quietly turned into a proper little stream. Long-form when I&rsquo;ve actually got something to say, bits when I don&rsquo;t and  a <a href="https://wilhelm.codes/blog/a-year-in-circles/">year of squircles</a> up top keeping a tally of it all.</p>
<p>Which is the entire point. The less effort it takes to post a half-formed thought, the more likely I am to actually post it. And the more I post, the more those gaps fill in.</p>
<p>And, as always, you can check everything out for yourself in the <a href="https://github.com/wilhelm-murdoch/wilhelm.codes">repo</a>.</p>]]></content:encoded></item><item><title>Reading Between the Posts</title><link>https://wilhelm.codes/blog/reading-between-the-posts/</link><pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate><author>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</author><guid>https://wilhelm.codes/blog/reading-between-the-posts/</guid><category>hugo</category><category>css</category><wc:kind>post</wc:kind><description>The front page of this site has always been a plain list of articles. Which would be fine if I wrote longer-form articles a bit more often. I do, however, make code commits to this site far more frequently. I&amp;rsquo;m also working on implementing a short-form section ( a.k.a. &amp;ldquo;bits&amp;rdquo; ) for links, videos and other small low-stakes thoughts. So the front page doesn&amp;rsquo;t really show the full story. In reality, there&amp;rsquo;s a lot more going on behind the scenes.</description><content:encoded><![CDATA[<p>The front page of this site has always been a plain list of articles. Which would be fine if I wrote longer-form articles a bit more often. I do, however, make code commits to this site far more frequently. I&rsquo;m also working on implementing a short-form section ( a.k.a. &ldquo;bits&rdquo; ) for links, videos and other small low-stakes thoughts. So the front page doesn&rsquo;t really show the full story. In reality, there&rsquo;s a lot more going on behind the scenes.</p>
<p>I want to fill those gaps in and not with more full-fat rows, but with a small line between each pair of articles. Something like &ldquo;<em>1 bit and 41 changelog events in between</em>&rdquo;; a kind of interstitial event timeline for all the work that never quite became a full blog post.</p>
<h2 id="why-cant-i-hold-all-these-events">
  <a class="heading-link" href="#why-cant-i-hold-all-these-events">Why can&rsquo;t I hold all these events?<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>The trick to merging things that don&rsquo;t look alike&hellip; You&rsquo;re never going to believe this&hellip; is to make them look alike. I already pull my GitHub activity for the <a href="https://wilhelm.codes/blog/a-changelog-that-builds-itself/">changelog</a>, or, at least, the last 100 or so events. So, I reused that, threw the bits in alongside it, and normalised the whole lot into one boring, uniform shape. This data structure doesn&rsquo;t hold all the data for each event, but just enough to cover the highlights.</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Go</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="err">$</span><span class="nx">events</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="err">$</span><span class="nx">events</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nf">append</span><span class="w"> </span><span class="p">(</span><span class="nx">dict</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;kind&#34;</span><span class="w">  </span><span class="s">&#34;commit&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;date&#34;</span><span class="w">  </span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">AsTime</span><span class="w"> </span><span class="p">.</span><span class="nx">commit</span><span class="p">.</span><span class="nx">author</span><span class="p">.</span><span class="nx">date</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;title&#34;</span><span class="w"> </span><span class="p">(</span><span class="nf">index</span><span class="w"> </span><span class="p">(</span><span class="nx">split</span><span class="w"> </span><span class="p">.</span><span class="nx">commit</span><span class="p">.</span><span class="nx">message</span><span class="w"> </span><span class="s">&#34;\n&#34;</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;url&#34;</span><span class="w">   </span><span class="p">.</span><span class="nx">html_url</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;icon&#34;</span><span class="w">  </span><span class="s">&#34;code-commit&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;colour&#34;</span><span class="w"> </span><span class="s">&#34;green&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;data&#34;</span><span class="w">  </span><span class="p">.)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}}</span></span></span></code></pre></div>
</div>
<p>Every piece of content on this site has <em>at the very last</em> these attributes. So created a unifying model is pretty straight-forward. A <code>kind</code>, a <code>date</code>, a <code>title</code>, a link, and enough leftover metadata to do something creative with later if I feel like it. I made a point of keeping the original object tucked away in <code>data</code> too; a gift to future Wilhelm.</p>
<h2 id="minding-the-gaps">
  <a class="heading-link" href="#minding-the-gaps">Minding the gaps.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>With one big sorted pile of events, the rest is just bookkeeping. Walk the articles newest to oldest, and for each one work out the window between it and the article above it. Then, scoop up every event that falls within the relevant dates:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Go</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="err">$</span><span class="nx">upper</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="err">$</span><span class="nx">now</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">gt</span><span class="w"> </span><span class="err">$</span><span class="nx">i</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">}}{{</span><span class="w"> </span><span class="err">$</span><span class="nx">upper</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">(</span><span class="nx">index</span><span class="w"> </span><span class="err">$</span><span class="nf">articles</span><span class="w"> </span><span class="p">(</span><span class="nx">sub</span><span class="w"> </span><span class="err">$</span><span class="nx">i</span><span class="w"> </span><span class="mi">1</span><span class="p">)).</span><span class="nx">Date</span><span class="w"> </span><span class="p">}}{{</span><span class="w"> </span><span class="nx">end</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="err">$</span><span class="nx">e</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="err">$</span><span class="nx">events</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nf">and</span><span class="w"> </span><span class="p">(</span><span class="err">$</span><span class="nx">e</span><span class="p">.</span><span class="nx">date</span><span class="p">.</span><span class="nx">After</span><span class="w"> </span><span class="err">$</span><span class="nx">article</span><span class="p">.</span><span class="nx">Date</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nf">not</span><span class="w"> </span><span class="p">(</span><span class="err">$</span><span class="nx">e</span><span class="p">.</span><span class="nx">date</span><span class="p">.</span><span class="nx">After</span><span class="w"> </span><span class="err">$</span><span class="nx">upper</span><span class="p">))</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">{{</span><span class="w"> </span><span class="err">$</span><span class="nx">group</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="err">$</span><span class="nx">group</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nx">append</span><span class="w"> </span><span class="err">$</span><span class="nx">e</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="w"> </span><span class="nx">end</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="nx">end</span><span class="w"> </span><span class="p">}}</span></span></span></code></pre></div>
</div>
<p>The newest article&rsquo;s &ldquo;window&rdquo; runs all the way up to <em>now</em>, so whatever I&rsquo;ve been doing since the last post shows up at the very top. If there&rsquo;s an empty gap the event list doesn&rsquo;t render.</p>
<p><img
  src="https://wilhelm.codes/blog/reading-between-the-posts/interstitial-events-top_hu_213053be54f9122c.webp"
  srcset="/blog/reading-between-the-posts/interstitial-events-top_hu_213053be54f9122c.webp 736w, /blog/reading-between-the-posts/interstitial-events-top_hu_f4bb42f82050343a.webp 1104w, /blog/reading-between-the-posts/interstitial-events-top_hu_f4fd9da2a1500826.webp 1472w"
  sizes="(max-width: 48rem) 100vw, 736px"
  width="736"
  height="155"
  alt=""
  
  loading="lazy"
  decoding="async"
/>
</p>
<h2 id="listing-the-interstitial-events">
  <a class="heading-link" href="#listing-the-interstitial-events">Listing the interstitial events.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>A summary is nice, but I wanted to be able to actually <em>see</em> the events without leaving the page. Normally that&rsquo;s where a pile of JS shows up. But, just as with the activity graph, we&rsquo;re going to rely on pure HTML and CSS. The <code>&lt;details&gt;</code> element does the entire job for free:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">HTML</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">details</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;group&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">summary</span><span class="p">&gt;</span>... 41 changelog events in between ...<span class="p">&lt;/</span><span class="nt">summary</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- one little row per event, complete with icon and link --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">details</span><span class="p">&gt;</span></span></span></code></pre></div>
</div>
<p>Click the summary, the gap unfolds into a tidy little list of every commit, PR and bit. Each linked off to wherever it lives. The default disclosure triangle gets binned and replaced with a chevron that flips on open, courtesy of Tailwind&rsquo;s <code>group-open:</code> variant. Still no JS and I&rsquo;ll remain quietly smug about that.</p>
<p><img
  src="https://wilhelm.codes/blog/reading-between-the-posts/interstitial-events-timeline_hu_90a890e91eb37f0a.webp"
  srcset="/blog/reading-between-the-posts/interstitial-events-timeline_hu_90a890e91eb37f0a.webp 736w, /blog/reading-between-the-posts/interstitial-events-timeline_hu_dc4ebb376786fef.webp 1104w, /blog/reading-between-the-posts/interstitial-events-timeline_hu_7197e16fbb375629.webp 1472w"
  sizes="(max-width: 48rem) 100vw, 736px"
  width="736"
  height="262"
  alt=""
  
  loading="lazy"
  decoding="async"
/>
</p>
<h2 id="saving-myself-a-small-headache">
  <a class="heading-link" href="#saving-myself-a-small-headache">Saving myself a small headache.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>Finally, there&rsquo;s the the responsive question. This site&rsquo;s overall design isn&rsquo;t exactly &ldquo;mobile first&rdquo; and viewing it on a small screen strips away a lot of detail already. All these thin dividers and fold-out lists look great on a wide screen and turn into a cramped mess on a phone.</p>
<p>I thought about it for a while and weighed up some clever reflowing. In the end I did the only sensible thing and hid the whole timeline below the <code>sm</code> breakpoint. On a phone you get the clean list of articles, but on a desktop you get the full story between them.</p>
<figure class="pull-quote">
  <span class="pull-quote-mark" aria-hidden="true"><span class="icon "><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M108 72v72H40a8 8 0 0 1-8-8V72a8 8 0 0 1 8-8h60a8 8 0 0 1 8 8m108-8h-60a8 8 0 0 0-8 8v64a8 8 0 0 0 8 8h68V72a8 8 0 0 0-8-8" opacity=".2"/><path d="M100 56H40a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h60v8a32 32 0 0 1-32 32a8 8 0 0 0 0 16a48.05 48.05 0 0 0 48-48V72a16 16 0 0 0-16-16m0 80H40V72h60Zm116-80h-60a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h60v8a32 32 0 0 1-32 32a8 8 0 0 0 0 16a48.05 48.05 0 0 0 48-48V72a16 16 0 0 0-16-16m0 80h-60V72h60Z"/></g></svg></span></span>
  <blockquote>
    <p>Sometimes the right amount of effort is zero and the discipline is in admitting it.</p>
  </blockquote>
  <figcaption>
    <cite>Me. Wilhelm. I said that.</cite>
  </figcaption>
</figure>

<p>That being said, I do need to return and focus on making the mobile experience a bit more palatable.</p>
<h2 id="in-closing-">
  <a class="heading-link" href="#in-closing-">In closing &hellip;<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>What I like about this is that it turns my worst blogging habit - vanishing for months and then resurfacing in a manic flurry - into something the site can actually narrate. The big rows are the headlines while the quiet lines between them are everything else.</p>
<p>It&rsquo;s also wired up so I can grow it later without unpicking anything. Right now it&rsquo;s counts and lists, but tomorrow it could filters, more details, or do something I haven&rsquo;t thought of yet. All off the same pile of events.</p>
<p>For now, though, the gaps have something interesting in them. Which is a much nicer thing to scroll past than a year of silence.</p>]]></content:encoded></item><item><title>A Year in Circles... I mean Squares. Or, is it Squircles?</title><link>https://wilhelm.codes/blog/a-year-in-circles/</link><pubDate>Mon, 15 Jun 2026 00:00:00 +0000</pubDate><author>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</author><guid>https://wilhelm.codes/blog/a-year-in-circles/</guid><category>hugo</category><category>css</category><category>tailwindcss</category><wc:kind>post</wc:kind><description>If you scroll up to the top of the home page, you&amp;rsquo;ll find a pair of little rows of green squircles. It&amp;rsquo;s my own dumbed-down take on GitHub&amp;rsquo;s contribution graph. Effectively, it&amp;rsquo;s the same idea, except instead of a year of days it&amp;rsquo;s a year of weeks. Two rows of twenty-six with one squircle per week, with each squircle coloured a deeper shade of green the busier that week was.</description><content:encoded><![CDATA[<p>If you scroll up to the top of the home page, you&rsquo;ll find a pair of little rows of green squircles. It&rsquo;s my own dumbed-down take on GitHub&rsquo;s contribution graph. Effectively, it&rsquo;s the same idea, except instead of a year of days it&rsquo;s a year of <em>weeks</em>. Two rows of twenty-six with one squircle per week, with each squircle coloured a deeper shade of green the busier that week was.</p>
<p>I wanted something that summed up &ldquo;Has Wilhelm actually been doing anything lately?&rdquo; at a glance, without the density of 365 tiny day cells. Fifty-two squircles felt about right. Coarse enough to read across the room, fine enough to still give a picture.</p>
<h2 id="what-counts-as-a-weeks-worth-of-activity">
  <a class="heading-link" href="#what-counts-as-a-weeks-worth-of-activity">What counts as a &ldquo;week&rsquo;s worth of activity&rdquo;?<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>The short answer is <em>everything</em>. The <a href="https://wilhelm.codes/blog/a-changelog-that-builds-itself/">changelog</a> already pulls my GitHub activity at build time, so I reuse that exact same data. Then, I throw in the things GitHub doesn&rsquo;t know about; every type of blog post on this site.</p>
<p>Because the changelog fetching already lives in a tidy little partial, sourcing the everything is just a matter of asking for each one and collecting all the publish dates:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Go</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="err">$</span><span class="nx">dates</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">slice</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;changelog/fetch.html&#34;</span><span class="w"> </span><span class="p">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;url&#34;</span><span class="w"> </span><span class="err">$</span><span class="nx">commitsUrl</span><span class="w"> </span><span class="s">&#34;fixture&#34;</span><span class="w"> </span><span class="err">$</span><span class="nx">fixture</span><span class="p">)</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="w"> </span><span class="err">$</span><span class="nx">dates</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="err">$</span><span class="nx">dates</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nf">append</span><span class="w"> </span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">AsTime</span><span class="w"> </span><span class="p">.</span><span class="nx">commit</span><span class="p">.</span><span class="nx">author</span><span class="p">.</span><span class="nx">date</span><span class="p">)</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="nx">end</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">where</span><span class="w"> </span><span class="nx">site</span><span class="p">.</span><span class="nx">RegularPages</span><span class="w"> </span><span class="s">&#34;Section&#34;</span><span class="w"> </span><span class="s">&#34;blog&#34;</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="w"> </span><span class="err">$</span><span class="nx">dates</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="err">$</span><span class="nx">dates</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nx">append</span><span class="w"> </span><span class="p">.</span><span class="nx">Date</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="nx">end</span><span class="w"> </span><span class="p">}}</span></span></span></code></pre></div>
</div>
<p>Nothing crazy going on here. I&rsquo;m just creating a big list of timestamps from wherever I happen to leave a trail.</p>
<h2 id="sorting-a-pile-of-dates-into-weekly-buckets">
  <a class="heading-link" href="#sorting-a-pile-of-dates-into-weekly-buckets">Sorting a pile of dates into weekly buckets.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>Each of these dates now need to land in one of fifty-two buckets. Working out which week a given date belongs to is just some boring epoch arithmetic; how many whole weeks ago was it?</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Go</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="err">$</span><span class="nx">ago</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="p">(</span><span class="nf">div</span><span class="w"> </span><span class="p">(</span><span class="nx">sub</span><span class="w"> </span><span class="nx">now</span><span class="p">.</span><span class="nx">Unix</span><span class="w"> </span><span class="err">$</span><span class="nx">d</span><span class="p">.</span><span class="nx">Unix</span><span class="p">)</span><span class="w"> </span><span class="mi">604800</span><span class="p">)</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nf">and</span><span class="w"> </span><span class="p">(</span><span class="nx">ge</span><span class="w"> </span><span class="err">$</span><span class="nx">ago</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nx">lt</span><span class="w"> </span><span class="err">$</span><span class="nx">ago</span><span class="w"> </span><span class="mi">52</span><span class="p">)</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="w"> </span><span class="err">$</span><span class="nx">counts</span><span class="p">.</span><span class="nf">Add</span><span class="w"> </span><span class="p">(</span><span class="nx">printf</span><span class="w"> </span><span class="s">&#34;%d&#34;</span><span class="w"> </span><span class="p">(</span><span class="nx">sub</span><span class="w"> </span><span class="mi">51</span><span class="w"> </span><span class="err">$</span><span class="nx">ago</span><span class="p">))</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="nx">end</span><span class="w"> </span><span class="p">}}</span></span></span></code></pre></div>
</div>
<p>That <code>604800</code> is the number of seconds in a week. And yes, I did have to look that up. Anything older than fifty-two weeks just falls off the back and is quietly ignored.</p>
<h2 id="painting-the-squircles">
  <a class="heading-link" href="#painting-the-squircles">Painting the squircles.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>My favourite part of this is there&rsquo;s no JS involved. The whole thing is a CSS grid of twenty-six columns, and because each squircle is <code>aspect-square</code>, the rows just work and the squircles stretch to fill whatever container I drop the partial into:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">HTML</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;grid grid-cols-26 gap-1.5&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 52 of these green little dudes --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;aspect-square rounded-full bg-emerald-400&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span></span></span></code></pre></div>
</div>
<p>Working out the shade was the fiddly bit. My first attempt simply scaled each week against the busiest one, which sounds sensible right up until you remember my data is basically one enormous week and a whole lot of flat nothing. That single monster week hogged the dark end and squashed everything else into the same pale green. So I did what GitHub does and reached for <a href="https://en.wikipedia.org/wiki/Quartile">quartiles</a> instead. You rank the weeks that actually saw some activity, chop them into four groups, and let a week&rsquo;s colour come from where it lands in the pack rather than from some absolute number. Empty weeks stay a faint <code>emerald-100</code>; the rest climb through four steps of green up to <code>emerald-600</code>. The part I like is that it&rsquo;s relative to <em>me</em>; a busy week is only busy compared to my <em>other</em> weeks.</p>
<p>The little tooltip that pops up when you hover over a squircle is also pure CSS. A <code>group</code> on the wrapper, a <code>group-hover:opacity-100</code> on the popup, a <code>transition</code> is all I needed. Each squircle now gives a little <code>scale</code> on hover too, just because it&rsquo;s nice.</p>
<h2 id="the-graphs-painful-honesty">
  <a class="heading-link" href="#the-graphs-painful-honesty">The graph&rsquo;s painful honesty.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>Now on to the more embarrassing part. When I first rendered it with real data, I got <em>one</em> lonely green squircle and fifty-one empty ones. Surely, you&rsquo;ve noticed it on the front page.</p>
<p>For the curious, this is what it <em>would</em> look like if I didn&rsquo;t have commitment issues.
<img
  src="https://wilhelm.codes/blog/a-year-in-circles/activity-graph-demo_hu_ea2ab57358bd9cb8.webp"
  srcset="/blog/a-year-in-circles/activity-graph-demo_hu_ea2ab57358bd9cb8.webp 736w, /blog/a-year-in-circles/activity-graph-demo_hu_ec221f404c400ffe.webp 1104w, /blog/a-year-in-circles/activity-graph-demo_hu_1cd3ec9be12d0e93.webp 1472w"
  sizes="(max-width: 48rem) 100vw, 736px"
  width="736"
  height="91"
  alt=""
  
  loading="lazy"
  decoding="async"
/>
</p>
<p>At the time I assumed I&rsquo;d done something wrong. But, to my great shame, I hadn&rsquo;t. Turns out that when you vanish from your own website for close to 18 months and then cram an <a href="https://wilhelm.codes/blog/some-long-overdue-housekeeping/">entire renovation</a> into a single week, the graph renders exactly that. A long, flat, pale stretch of road ending with one pathetic little green emerald.</p>
<p>There&rsquo;s an extra little indignity baked into the quartiles, too. They need a <em>spread</em> to rank against, and with exactly one active week there&rsquo;s nothing to compare it to. So my massive renovation-cramming week doesn&rsquo;t even get to be properly dark green. It turns up as a polite, middling shade.</p>
<p>I effectively built a little a tool whose entire job is to hold up a mirror to my own lack of consistency and commitment. Very cool! 😬👌</p>
<h2 id="drop-it-in-anywhere">
  <a class="heading-link" href="#drop-it-in-anywhere">Drop it in anywhere.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>This whole thing is a single self-contained partial. It was waaaay easier to build out than I had originally thought. And, thanks to Tailwind I didn&rsquo;t even have to fall back on any JS! I count that as a bonus.</p>
<p>For the moment, it&rsquo;ll live on the home page, but I can now just place this anywhere in my Hugo site and it&rsquo;ll work:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Go</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="p">{{</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;activity-graph.html&#34;</span><span class="w"> </span><span class="p">.</span><span class="w"> </span><span class="p">}}</span></span></span></code></pre></div>
</div>
<p>I might even build it out a bit more to support different colour schemes or specificy types of targeted site content. So, the more I write, the more those squircles fill in.</p>
<p>Consider yourself warned, <em>me</em>.</p>]]></content:encoded></item><item><title>Some Long Overdue Housekeeping</title><link>https://wilhelm.codes/blog/some-long-overdue-housekeeping/</link><pubDate>Sun, 14 Jun 2026 00:00:00 +0000</pubDate><author>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</author><guid>https://wilhelm.codes/blog/some-long-overdue-housekeeping/</guid><category>hugo</category><category>tailwindcss</category><category>css</category><category>refactoring</category><category>wilhelm.codes</category><wc:kind>post</wc:kind><description>This blog has been quietly chugging along for a while now without me paying it much attention. Which is, I suppose, the whole point of a set-and-forget setup. But &amp;ldquo;set-and-forget&amp;rdquo; has a sneaky way of becoming &amp;ldquo;forgotten&amp;rdquo;, and the longer you leave something untouched, the more it quietly rots behind your back. So I rolled up my sleeves and gave the whole thing a proper tune-up.</description><content:encoded><![CDATA[<p>This blog has been quietly chugging along for a while now without me paying it much attention. Which is, I suppose, the whole point of a <a href="https://wilhelm.codes/blog/my-blog-publishing-setup/">set-and-forget</a> setup. But &ldquo;set-and-forget&rdquo; has a sneaky way of becoming &ldquo;forgotten&rdquo;, and the longer you leave something untouched, the more it quietly rots behind your back. So I rolled up my sleeves and gave the whole thing a proper tune-up.</p>
<h2 id="confession-time">
  <a class="heading-link" href="#confession-time">Confession Time<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>While poking around the build, I discovered something a little embarrassing. My Tailwind setup - the source, the config, and the <em>entire</em> <code>node_modules</code> directory - was living inside Hugo&rsquo;s <code>static/</code> folder.</p>
<p>If you know Hugo, you already know where this is going. Everything in <code>static/</code> gets copied, verbatim, into the final site. Which means I had been cheerfully publishing my whole build toolchain - megabytes of it - to the live site on every single deploy. So, yeah. Production was shipping <code>node_modules</code>. Coding is my passion.</p>
<p>Nobody noticed, nothing broke, and the world kept turning. But it&rsquo;s the kind of thing that, once you see it, you can&rsquo;t <em>un</em>-see. To be fair to myself, when I originally put this Hugo site together, I only learned enough to get something shipped.</p>
<h2 id="letting-hugo-do-the-heavy-lifting">
  <a class="heading-link" href="#letting-hugo-do-the-heavy-lifting">Letting Hugo do the heavy lifting.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>The reason that mess existed in the first place was that I&rsquo;d wired up Tailwind as a separate, manual build step that spat out a compiled stylesheet for Hugo to pick up. It worked, but it was a second moving part I had to remember existed.</p>
<p>The good news is that recent versions of Hugo can drive <a href="https://gohugo.io/functions/css/tailwindcss/">Tailwind</a> itself, natively, as part of the normal asset pipeline. Combined with Tailwind v4 - which finally ditches the JavaScript config file in favour of configuring everything in CSS - I got to delete a <em>lot</em> of stuff:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git diff --shortstat main...modernize
</span></span><span class="line"><span class="cl"><span class="m">64</span> files changed, <span class="m">1872</span> insertions<span class="o">(</span>+<span class="o">)</span>, <span class="m">40197</span> deletions<span class="o">(</span>-<span class="o">)</span></span></span></code></pre></div>
</div>
<p>The whole stylesheet now starts its life as a single entry point:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">CSS</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">@</span><span class="k">import</span> <span class="s2">&#34;tailwindcss&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">plugin</span> <span class="s2">&#34;@tailwindcss/typography&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">source</span> <span class="s2">&#34;hugo_stats.json&#34;</span><span class="p">;</span></span></span></code></pre></div>
</div>
<p>That <code>hugo_stats.json</code> bit is the clever part. Hugo writes out a list of every utility class it actually emits, and Tailwind reads <em>that</em> to decide what to generate. No more pointing Tailwind at my templates and hoping it guesses right.</p>
<p>Then a small partial hands it all off to Hugo to compile, minify and fingerprint:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Go</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="p">{{</span><span class="o">-</span><span class="w"> </span><span class="nf">with</span><span class="w"> </span><span class="p">(</span><span class="nx">templates</span><span class="p">.</span><span class="nf">Defer</span><span class="w"> </span><span class="p">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;key&#34;</span><span class="w"> </span><span class="s">&#34;css&#34;</span><span class="p">))</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="o">-</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">resources</span><span class="p">.</span><span class="nx">Get</span><span class="w"> </span><span class="s">&#34;css/main.css&#34;</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">{{</span><span class="o">-</span><span class="w"> </span><span class="err">$</span><span class="nx">opts</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;minify&#34;</span><span class="w"> </span><span class="p">(</span><span class="nx">not</span><span class="w"> </span><span class="nx">hugo</span><span class="p">.</span><span class="nx">IsDevelopment</span><span class="p">)</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">{{</span><span class="o">-</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="p">.</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nx">css</span><span class="p">.</span><span class="nx">TailwindCSS</span><span class="w"> </span><span class="err">$</span><span class="nx">opts</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="p">{{</span><span class="o">-</span><span class="w"> </span><span class="cm">/* ...do very cool things... */</span><span class="w"> </span><span class="o">-</span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">{{</span><span class="o">-</span><span class="w"> </span><span class="nx">end</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="o">-</span><span class="w"> </span><span class="nx">end</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">{{</span><span class="o">-</span><span class="w"> </span><span class="nx">end</span><span class="w"> </span><span class="p">}}</span></span></span></code></pre></div>
</div>
<p>The <code>templates.Defer</code> wrapper is there because the CSS can&rsquo;t compiled ( transpiled? ) until Hugo has finished rendering every page and knows the full list of classes. So, in a very real way, Hugo solves an annoying 🐔 and 🥚 problem.</p>
<p>This means no more standalone Tailwind config, no committed stylesheet, no <code>node_modules</code> in <code>static/</code>, and no build toolchain leaking onto the live site. Very cool!</p>
<h2 id="so-many-deprecations">
  <a class="heading-link" href="#so-many-deprecations">So. Many. Deprecations.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>Of course, nothing that&rsquo;s been left alone for a year comes back to life cleanly. Bumping Hugo to the latest release lit up the console like a Christmas tree.</p>
<p>A few of my templates were leaning on things that have since been politely shown the door:</p>
<ul>
<li><code>resources.GetRemote ... .Err</code> for the <a href="https://wilhelm.codes/changelog/">changelog page</a> - that pattern was removed in favour of a shiny new <code>try</code> keyword.</li>
<li><code>.Language.LanguageCode</code> and <code>.Language.LanguageDirection</code>, both deprecated in favour of <code>.Locale</code> and <code>.Direction</code>.</li>
<li>The <code>_build</code> and <code>cascade._target</code> front matter keys, now just <code>build</code> and <code>cascade.target</code>.</li>
</ul>
<p>None of it was hard to fix, but it&rsquo;s a good reminder that &ldquo;it still builds&rdquo; and &ldquo;it builds <em>without complaints</em>&rdquo; are two very different bars.</p>
<h2 id="a-footgun">
  <a class="heading-link" href="#a-footgun">A Footgun!<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>Here&rsquo;s a fun one. The changelog page pulls in a Github event fixture file during local development by fetching it over <code>http://localhost:1313</code>. Effectively, from the very dev server that&rsquo;s <em>trying to build the page</em>.</p>
<p>Those of you who are familiar with such things can probably see the problem. Hugo builds the site <em>before</em> it starts listening on that port, so the build sits there waiting for a server that doesn&rsquo;t exist yet. A deadlock of my own making. The obvious fix was to just read the file off disk instead of asking the network nicely.</p>
<h2 id="some-honourable-mentions">
  <a class="heading-link" href="#some-honourable-mentions">Some honourable mentions.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>A grab-bag of smaller wins while I had the hood up:</p>
<ul>
<li>Fonts are now served as <code>woff2</code> instead of raw <code>ttf</code>, with <code>font-display: swap</code> so text shows up immediately instead of hanging around invisible. This alone shaved the font payload down by about 60%.</li>
<li>I also deleted a few MB of fluff that wasn&rsquo;t being loaded by anything.</li>
<li>Prettier got a nice version bump and I taught it to sort my Tailwind classes, so I can stop pretending I do that consistently by hand.</li>
</ul>
<h2 id="was-the-juice-worth-the-squeeze">
  <a class="heading-link" href="#was-the-juice-worth-the-squeeze">Was the juice worth the squeeze?<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>Aside from a few cosmetic updates here and there, the site should look pretty much the same as it did before. Which is, weirdly, the whole point; all of this work was about the parts you can&rsquo;t see. I got a leaner build, faster page loads, and a project I can come back to in another year without wincing; famous last words, etc&hellip;</p>
<p>For now, the house is clean. Very nice.</p>]]></content:encoded></item></channel></rss>