Daniel Schulz
Daniel Schulz iamschulz avatar

Mostly frontend, sometimes art

my Mastodon
A calendar sheet with fields ranging from 1 to 31. The year 2023 is displayed hovering above it.

This website, 2023 edition

So, I rebuilt my website, again. Not because I grew bored of my design (at least not entirely), not because I wanted to update it for the Indieweb Revolution that’s certainly coming this year, for sure. It’s because my old Hugo setup was coming apart at the seams. I overcomplicated things and now i started to get the consequences of it. Time to rebuild a new over-engineered, complicated monstrum from scratch without realizing it!

The tipping point

When I say overcomplicated, I have several aspects in mind. Adding new components was cumbersome, because my DIY component loader isn’t as cool as I thought. My state machine was awkward to use and didn’t provide much value at all. The javascript of my page was tiny and designed with progressive enhancement in mind, but the way I built it was reminiscent of Der Spiegel, where I worked when I wrote it. My page simply didn’t justify such design patterns.

I updated my site by writing Markdown in VSCode. I liked how it’s actually a very nice markdown editor, especially in Zen Mode, but I hated that I didn’t have a good solution to write on my phone. Then, at my current workplace, I started to look at Notion’s API…

However, the tipping point were images. My blog was built with Hugo, a Static Site Generator written in Go, and only Go. If there was no way to code a feature in Go, Hugo simply couldn’t have it. WebP was such a feature for a long time, only coming to Hugo in May 2021. Avif was the next one requiring third party tool. I wrote a node script to generate WebPs and Avifs and included that in my build process, going entirely against Hugo’s philosophy. That step stopped working somehow, regressing my blog to jpegs. I didn’t have the time and willpower to fix that, knowing full well how janky it was. I wanted to burn it all down and restart from scratch, in a language and tools that supported my vision more than their own.

A new beginning

The next step was going to be all JavaScript, front to back end. I switched out Hugo for Eleventy. That takes care of the image problem, since WebP and Avif have implementations in JavaScript. and gives me the advantage of having all my dependencies inside a package.json. As long as I have Node 18+ installed, it’ll work. Older versions will get a meaningful error message.

I killed my darlings and got rid of my component loader altogether. I’ve got seven components, and I’ll initialize them manually. No need for an abstraction layer that I need to study for half an hour to to fully understand after not looking at it for a few months.

I’ve solved my CMS problem by using Notion as an app to write text (I wrote about that). But I don’t want to be dependent on Notion alone, in case Notion gets bought out by evil billionaires and starts to overthrow democracies. I’m backing up my content to git continuously, so I can switch out Notion as I see fit.

Rebuild from scratch

Since I'm throwing out my old JavaScript approach, I had to rewrite it all from scratch. That resulted not only in less boilerplate, but less Javascript overall. Writing purpose-built non-reusable components in Vanilla JS is actually really fast (and fun). I went from 9.1kB for just the main bundle (with more to be lazy loaded) to 4.9kB for everything.

I went from Webpack to ESBuild, which is my default build process for small projects right now. That reduced JS build times from ~30s to ~0.5s. My Webpack config was probably not very optimized for speed, but that only means it’s so much easier to get a fast build out of ESBuild than out of Webpack.

I could re-use most of my CSS, since both layouts are similar, but I modernized it a bit. I added some font scaling, reduced the CLS that comes with the FOUT and I’m now keeping the specificity low on my utility classes with the :where selector. That one is a game changer for me. My CSS workflow is a mixture of BEM and CUBE. That means when I wanted to overwrite a utility class the specificity went over 1, which means introducing more specific selectors down the line. :where is basically a cheat code for “Apply the styles, but don’t be pushy about it”.

My CSS color management found some new life again, but I one-upped it a bit. Instead of a theme switch, I added a brightness slider. Now there’s not only a light and dark theme, but also everything in between.

Almost Goodbye, Uberspace

Having my site on Uberspace is cool, but it’s also work. I don’t want do deal with deploy pipelines, git integrations, cache invalidation and whatnot. I’m moving my site to Netlify. It’s just so much more convenient. But I will keep my Uberspace account, mainly because I still need a server function that I can’t (cheaply) implement on Netlify. I support Likes on my articles. Those get accumulated from Webmentions, Bridgy, Dev.to and my own like counter - which is a CRUD API hosted on Uberspace.


This Design has served me for three iterations of this website now, getting updated step by step, while the underlying technologies changed fundamentally. I’m quite happy with the setup I have now, and even more so with it’s modularity. I’m curious how the next iterations will look like.