39 bits · 6 kinds · since 2026

0 entries
week 1
0 entries
week 2
0 entries
week 3
0 entries
week 4
0 entries
week 5
0 entries
week 6
0 entries
week 7
0 entries
week 8
0 entries
week 9
0 entries
week 10
0 entries
week 11
0 entries
week 12
0 entries
week 13
0 entries
week 14
0 entries
week 15
0 entries
week 16
0 entries
week 17
0 entries
week 18
0 entries
week 19
0 entries
week 20
0 entries
week 21
0 entries
week 22
0 entries
week 23
0 entries
week 24
0 entries
week 25
0 entries
week 26
0 entries
week 27
0 entries
week 28
1 entry
week 29
0 entries
week 30
0 entries
week 31
1 entry
week 32
0 entries
week 33
1 entry
week 34
1 entry
week 35
0 entries
week 36
1 entry
week 37
1 entry
week 38
7 entries
week 39
2 entries
week 40
3 entries
week 41
0 entries
week 42
3 entries
week 43
2 entries
week 44
3 entries
week 45
2 entries
week 46
1 entry
week 47
2 entries
week 48
3 entries
week 49
1 entry
week 50
1 entry
week 51
3 entries
week 52

39 entries over 52 weeks of bits activity. Less More

blurb #plantsm.art, #analytics#umami

Woke up to a massive “god candle” appearing in my Umami instance for Plant🌱Smart. I had to do a triple-take as I immediately wrote it off as Google finally re-indexing the site after almost 2 years of nothing. Then, I took a much closer look and noticed 99% of the traffic was coming from email clients. Which, to be frank, I found quite odd. Why email clients? As it turns out, The Morning Brew — a popular news… newsletter in the States — mentioned my little project in that morning’s issue. Almost 10k visits to the site when the most that could be expected was, maybe, 20 per day if I was lucky. After all, I haven’t really done much in the way of advertising it. The big spike has obviously fizzled out quite a bit, but as a result, the site has been linked to and mentioned in several other places. This can only be good for the site’s rankings in Google’s index, so no complaints coming from me!

A screenshot of the site's Umami traffic chart.

I’m obviously a little bummed out the traffic has steadied out as much as it has, but this is a small purpose-built utility site. I’m not going to get 1,000’s of visitors per day here as a normal occurrence. Just stoked that people find it interesting!

update #wilhelm.codes, #homelab, #css#design

I’ve been rebuilding the public landing page for the homelab this weekend and somewhere in the middle of it I laid a scanline over the whole thing. It’s a single hairline that repeats itself every three pixels. It’s faint enough that the page stops looking quite so flat without you ever working out why. Then, obviously, I wanted it here too.

The lazy version is a white line at about 1.5% opacity, which is what the other page uses, because the other page is forty words of neon over black; legibility isn’t really the goal. However, this site is mostly prose, so a white line lightens the words as well as the background. I didn’t spend all that time on the reading column just to lay a haze over it.

So the grain mixes off --text instead of white:

CSS
--grain: color-mix(in srgb, var(--text) 1.5%, transparent);

This can be applied like so:

CSS
.grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 10;
  background: repeating-linear-gradient(
    to bottom,
    var(--grain) 0 1px,
    transparent 1px 3px
  );
}

And finally referened like so just above the closing </body> tag:

HTML
<div class="grain" aria-hidden="true"></div>

This works out great, because a colour mixed with itself gives that colour straight back. The grain physically cannot touch the body copy. I’ve learned quite a few new things today!

As a bonus the lightbox is a <dialog> and the browser promotes a modal dialog into the top layer. This sits above every z-index on the page; including the one holding the grain. So a zoomed photograph comes up perfectly clean and I didn’t have to write a rule to make that happen.

Whether you can see any of this depends entirely on your monitor, your colour settings and even your resolution. If you can’t see it, it’s working.

update #wilhelm.codes, #hugo#performance

Page loads had been creeping up again, so I went looking. Turns out I’d been quietly serving enormous screenshots. One post was pulling 5.1 MB of PNGs, and a single one of those was a 1.96 MB app screenshot sitting in a column 736 pixels wide. Oof.

The fix was mostly plumbing. My img shortcode was handing the raw src straight to the <img> tag, so Hugo never got a look in. Everything now goes through one partial that resizes to the width it actually displays at, re-encodes to WebP and emits a srcset, plus width and height so the layout stops jumping around while things load. Markdown images get the same treatment through a render hook. The awkward bit: images under static/ aren’t resources, so Hugo can’t touch them. A second mount in hugo.toml fixes that without breaking any existing URLs.

While I was in there I dealt with the JavaScript. Every page was carrying all seven inline scripts, even the 90-odd pages with no code blocks, no zoomable images and no locked series titles. Those three now load only where they actually do something. The two that genuinely run everywhere moved into one small fingerprinted bundle, so your browser fetches it once for the whole visit instead of re-reading it on every page. I also finally turned on HTML minification, which I’d apparently never bothered with.

Where it landed:

whatbeforeafter
Images the site serves12.3 MB1.5 MB
HTML over the wire, mean per page10,236 B6,671 B
Worst offending post, first paint5.1 MB112 KB

That last one is about 46 times smaller. Nothing changed visually, which was the whole point. The sparks still spark and the locked titles still scramble.

One last silly thing. I added a new short code to style “Plant Smart” as “Plant🌱Smart”. Fun!

image #plantsm.art

With all the frenetic redesigning of Plant🌱Smart these past few weeks, I couldn’t believe I completely forgot about the image gallery on the details page. The old design looked a bit dated like everything else, soooo the galleries also get a refresh and lightbox. Yay.

update #wilhelm.codes#github

Welp, I finally did it! I’ve moved the git repository for this site from Github to the Forgejo instance in my homelab. Further, I’ve replaced Cloudflare’s build pipeline with a simple 20-line YAML file used by my Woodpecker CI server, which is also hosted in my homelab. I’m still using Wrangler to push the static assets to Cloudflare Pages and I’ll always need an external DNS provider for my domain, but you can’t reasonably self-host everything.

It’s nice to have almost full control of my little website now! And all it took was 3 attempts to get the integration right. 😅

Alternative text for screen readers

A great note to end on for a Monday evening!

quote #wisdom

“Nearly all men can stand adversity, but if you want to test a man’s character, give him power”

— Robert G. Ingersoll

blurb #css#wilhelm.codes

Goodbye Tailwind. Goodbye Node. We’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’ve done this months ago when I first revamped the site.

quote #wisdom

“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.”

— Brian Kernighan, The Elements of Programming Style

update #wilhelm.codes

Spent an evening on the tags. A few of them were doing the same job under different names, so the duplicates are merged and the thin ones are gone. The cloud in the rail is shorter and rather more honest for it.

blurb #aws#cost-optimisation

Opened the bill expecting a fright and found a NAT gateway I have not needed since March. It has been quietly charging me rent to forward packets nowhere. Coding is my passion.

blurb

I keep promising myself I will write more on the weekends. The drafts folder keeps returning a different answer.

quote #go#wisdom

“A little copying is better than a little dependency.”

— Rob Pike, Go Proverbs

blurb #tmux

Sat down to fix one broken keybind. Three hours later the whole tmux config is rewritten and the keybind is still broken.

quote #wisdom

“The most damaging phrase in the language is: ‘It’s always been done that way.’”

— Grace Hopper

blurb #golang

Strangely sudden news today. Looks like “Go Report Card” has been completely sunset as of a couple weeks ago. I really dug that little service. At least it’s still available for self-hosting, but now I have to go on cross-repo badge cleanup duty.

update #wilhelm.codes#hugo

Decided to introduce a new projects page to showcase some of the bigger things I’m working on. I’m still working out the kinks and I’m not 100% sold on the current design. Each card is powered by a .toml based datasource in Hugo, contains a kind of linear heatmap of activity related to the associated project’s tag as well as the 5 latest related posts. Good enough to publish, I guess.

I also can’t decide on which activity graph animation I like the most. 😅

update #wilhelm.codes#github

I am very smart. I only just realised over the weekend that issues and discussions in the parent repo for this site were disabled. Meaning, article coments using Giscuss was functionally disabled here. WOOPS!

blurb #glazier#hcl

Welp. I’ve convinced myself that Glazier needs Terraform-style variable declaration blocks to validate --var flags. I’m doing it and I will not be stopped. I’m thinking something as simple as the following session block-adjecent top-level spec:

HCL
variable "name" {
  description = optional(string)
  type        = required(string | bool | number)
  default     = optional(type)
}

Time to do what I do best: make things more complicated than they have any right to be.

blurb #plantsm.art

I should update Plant🌱Smart. Thinking it should get a similar treatment to this site. Maybe even exploring moving completely to Hugo instead of a haphazardly-assembled static Svelte site.

update #wilhelm.codes

Some small incremental updates for the site today:

  • The addition of a new bit type for site updates like this one.
  • Proper RSS feeds for both long-form articles and bits. Check the footer!
  • A quick sweep of the site map to ensure everything maps out properly. There were a few dead links previously.
  • You can now filter bits by type! Have a play with it at the top of this page.
  • Some minor grammatical updates here and there for some recent content.

Something to note is that the bits filter is purely JS based and assumes all content will be displayed in a single page. This is fine for now, but there may be 100s or even 1,000s of entries on a long enough timeline. At that point, basic text search and filtering that supports pagination may be necessary. But, not today!

blurb

Tempted to drop the custom fonts altogether. They account for well-over half the transfer on a hard refresh.

blurb #woops#wilhelm.codes

I am not a smart man. I completely forgot I was using the “Pro” version of FontAwesome and had the entire library committed to this very public repository without any licensing coverage. I mean, the pro version shouldn’t be shared anyway. You may have noticed the associated Github repository has been temporarily set to private and that all commit history as been wiped. Sucks, but I felt this was much more pragmatic than removing multiple specific files from the tree. I am not an expert git surgeon, so that got a hard pass from me.

I’m currently experimenting with some alternative OPEN SOURCE web icons and updating the third-party licensing notice bundled with the repo to cover icons as well as web fonts.

Luckily, only I noticed this issue and immediately worked to address it.

edit: Good-bye, sweet git history!

quote #wisdom

“Simplicity is a great virtue but it requires hard work to achieve it and education to appreciate it. And to make matters worse: complexity sells better.”

— Edsger Dijkstra

blurb

I’m keeping these stupid test quotes around. I love them. Fight me.

quote #wisdom

“There are only two hard things in Computer Science: cache invalidation and naming things.”

— Phil Karlton

quote #wisdom

“Programs must be written for people to read, and only incidentally for machines to execute.”

— Harold Abelson

quote #wisdom#design

“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.”

— Antoine de Saint-Exupéry