Beyond the Horizon

Blog of Spencer Magnusson

How I Made This Blog

Including Steps and Tools

June 12, 2026 | 7 minutes


Everything I learned, helped me realize: man, I probably could have just used Hugo to do all this. But honestly, I had a lot of fun doing this.

But I’m a former web developer. I already know how half of all this works. And I’m a minimalist. I want to make it all myself, so it’s as small as I want, dangit.

But for those like me, here’s how I did it.

Markdown to HTML

I wanted to include my content from my Beehiiv newsletter, at least any still relevant (i.e not all the “hey I just uploaded” posts). I didn’t want to just download the HTML, as most reactive website’s HTML is messy (even the non-reactive HTML). I’m a big fan of plain text and Markdown, so that’s what I converted them to.

I initially found a nice “Beehiiv to markdown” converter online that did a great job. But I ran it at the beginning of this year, thinking I wouldn’t post any more, and deleted the scripts. Unfortunately, the owner took the script off GitHub (may have been forced by Beehiiv to take it off, now that I think about it). So I resorted to an online free “HTML to Markdown” converters for the latest ones. Manual work still had to be done, including checking for special characters that don’t convert to plain text correctly.

Archived HTML to Markdown, done. Now I needed something to convert my Markdown back to HTML. So I just used pandoc. I used it briefly before, but honestly I’m impressed how powerful yet simple it is. All I added was some basic metadata, give a template html file, and it was good to go. Really love it.

I could just convert each markdown file and slap an .html on the end, like so:

build/
|------ sequels_bioshock.html
|------ making_friends_online.html

But I personally like seeing URLs without the file extension, so I changed it to this:

build/
|- sequels_bioshock/
 |- index.html
|------ making_friends_online/
 |- index.html

Many thanks to pandoc for doing the hard work of conversion.

RSS, by hand

This partially inspired me to move off Beehiiv. They hide the RSS option for creators (newsletters don’t automatically get an RSS, you tell it to generate one), and they recently hid it from the default newsletter page.

I probably didn’t need to manually script this. Just find a library or framework to handle RSS for you. Most frameworks now have the option for it.

But I will say that it wasn’t as frightening as I thought. They’re pretty straightforward, especially my articles already have the related metadata. Here’s some Python pseudocode, for anyone interested (I can publish a full template, if I get enough feedback):

from xml.etree import ElementTree as ET

def build_rss(articles, channel_config):
    rss_xml = ET.Element('rss')
    # set XML version and formats, such as atom support

    channel = ET.SubElement(rss_xml, 'channel')
    # set RSS channel info: title, description, link,
    # image, language, latest article's publish date

    article_metadata = []
    # most RSS feeds show only the latest, say, 20 articles.
    # That way it doesn't take forever to load,
    # especially if the *entire* article content is in the RSS
    for article in article_metadata[:20]:
        item = ET.SubElement(channel, 'item')
        # set the title, link, description, publish date
        # set a guid for easier tracking in RSS,
        # just in case you change URLs at some point

    return rss_xml

I don’t have many images in these blog posts, but for those that do, you could probably do some smart scripting to grab the right image from each page (or just the first image) and mark that as the RSS item’s image.

Styling the Site

In the words of Supa Hot Fire, “I’m not a rapper graphic designer.” This is when I spent a day trying to convert my existing website so far to just Hugo. That way I could just use a Hugo theme. But I quickly learned that it’s easier to start Hugo from scratch than to convert.

So I just stayed with my manually built website. I initially used my links page’s CSS as a starting point, which honestly got it pretty far. For help, I consulted UX documentation online. I made the articles skinnier so it was easier to read — no head-turning on my wide monitor.

I asked the Fediverse for visual inspiration. And y’all have some cool-looking sites! Go see people’s websites; don’t just read from your RSS reader. It ended up inspiring a lot of functionality for my website, which I’ll get to later.

The “paper” and monochrome themes were the most appealing to me. For some reason, I started thinking of the Moon, eclipses, and lunar visuals (maybe because of the recent flight to the Moon 😛). I played with crescent shapes. You can easily do this with CSS borders. After further experimenting, I found myself enjoying gradients with box-shadow. So I started adding those, and boy oh boy, did it give my site a facelift.

Nothing is so inherently aesthetic and beautiful than visual consistency. Humans are bad at it, but thankfully code often can be. Make a bunch of rules somewhat related to each other, and then just follow them.

I’ll get into accessibility later, but that played a big part here. Thankfully, a mostly-monochrome paper style made it easy for myself. I may add stronger colors later, but that’s for another time.

My (50cc) Search Engine

Again, you can do this easily in something like Hugo. While I don’t have too many articles yet — about a couple dozen migrated from Beehiiv — I figured it would be helpful for those only interested in certain topics. I can be a bit all over the place. In the words of Ye, “I love this guy’s Blender stuff. What the heck does he know about Kelsea Ballerini?”

Everything I learned, I learned from “Building a Simple Search Engine That Actually Works”. Great article breaking down the mechanics of search engines into pseudocode-ready explanations. But in short, here’s the steps:

  1. Tokenize the data. Scrape titles, descriptions, and content and count individual words and even parts of words (to handle typos and give useful results while the user is still typing). This mostly decides how big your search index is.
  2. For each article, count and “score” each token it has, based on how important the words are (for example, if it’s in the title, I score the token higher). This is all stored in a database, cache file, however you want. If it’s going to be downloaded in the user’s web client, keep it small, ideally less than a couple megabytes (I did this by saving articles by index instead of by name).
  3. When the user types something to search, tokenize the text in the same way you tokenized the data.
  4. Check if each of the user’s tokens are in the database. Retrieve the articles and scores associated with the tokens.
  5. Sum up the scores for each article retrieved. The highest scored articles are ranked first.

My tokenization is done offline, in my build script, all put into a cache file. This cache gets uploaded to the static site. Then my webpage Javascript loads the cache file in, and checks for the tokens as the user searches.

The search is crazy fast, even with 10,000+ tokens. Not bad for <1 MB cache file.

As for how I handle the search listing in the browser, the Javascript just re-sorts the article HTML elements. It’s a little hacky, but it works, and I can easily re-sort it back to its original order if the user clears the search bar.

Other Necessities

Most of these I found while browsing others’ blogs, so I’ll document here: