I promise this is gonna be like, the last time I RICE 1 1 Race Inspired Cosmetic Enhancement. Who came up with this acronym.  up this blog. Probably until when I find something more interesting than Hugo.

Why Hugo? It is a Static Site Generator. My blog has now 0 JavaScript, cost 0 dollar to host, and has a chance of like 0.001% of total failure, thanks to Git. I own my blog content (which are in Markdown format), and can back it up, work on it, share it just like any other plaintext file. I love this setup, as it really motivate me to write more.

How I write my blog πŸ”—

I use Vim keybinding. That’s to say I’m not a Vim/Neovim purist who lives only in a terminal πŸ’­ πŸ’­ I hope to not come across as a neckbeard sysadmin/developer guy to people I work with , but I prefer to have the most basic Vim like keybinding when I’m typing something long, like source code, documents, essays… That’s why I almost never write stuffs in apps like Google/Microsoft Office Suites, simply because it doesn’t allow me to hjkl around the codebase. For most note taking/textual editing, I like my Obsidian configuration much much better. πŸ’­ πŸ’­ I know that there are stuffs like kindvim that add quasi-Vim keybinding to most text input in Mac, but let’s face it, it doesn’t cut as good as editor with native Vim keybinding .

Obsidian obeys the Unix philosophy, from the point of view of the user it is simple, easy to integrate with other stuff to sync, process, transform (they are just plaintext files after all). It is also very polished as far as a Markdown editor goes, with all the editing & viewing UX ironed out. And it has a Vim mode. So for me Obsidian is a no-nonsense notepad that I can write down stuffs very easily.

How I host my blog πŸ”—

I tried several blogging solutions on the market. From WordPress to Ghost as fully hosted solutions, then several homegrown React application, or a bunch of SSG codebase like Gatsby, Eleventy. They are all good tech and serves a purposes, but ultimately they are not what I need. As I adopt an increasingly minimalistic ethos, I realize those are just too much bloat for my taste. For example:

  • WordPress/Ghost need a hosting to run on. Hosting it on my Raspberry Pi is fine, but I don’t like having it idling around serving 1 request every 1 hour πŸ’­ πŸ’­ Which is probably just me checking it
  • Ghost has a lot of functions that I don’t use: membership, commenting system, analytics, mailing list, etc… WordPress is essentially a platform.
  • The React/JS based SSG tools are just kind of slow and clunky to setup.
  • I just want to put out my website to the world, along with a bit of my personal imprint.

So I reach for Hugo as the current solution. Why? Hugo is written in Go. It is essentially a single Go binary with several embedded scripts to help with the compilation pipeline. The generation script run in under a second. The end result is just a single HTML + CSS πŸ’­ πŸ’­ I’ll probably add some JS as I go, maybe for searching or the like that can be send to any hosting in the world. I’m going with Cloudflare, but in the future, if I’m unhappy with how Cloudflare conduct their business, I can just pick it up and deploy it elsewhere 2 2 Dan Abramov (ex? Facebook) breakdowns the problem of closed social network: the content you create are not truly yours, so it takes a non-trivial effort to switch to a different provider.  .

How I present my blog πŸ”—

The blog that you are reading is presented in the Hugo Terminal Theme, but I have forked it and applied my rather liberal interpretation:

  • The color scheme is less boring. I applied directly Solarized Light, my favorite editor theme.
  • The layout follows Edward Tufte’s style, which is adapted to the web by the creator of tufte-css. I took inspiration (and CSS code) from this stylesheet. I just think it’s neat to see footnote πŸ’­ πŸ’­ Hopefully you do too next to where it is used, not necessarily because I’m a fan of Tufte or anything (I haven’t read his book).

The big deal is: Pure CSS visualization. I’m trying to live the day of Web 1.0 that I never had. The fact is, CSS is getting much much more powerful & easy to write by the day. With a few clever tricks 3 3 Lyra Rebane’s excellent guide  4 4 Daisy UI is actually an HTML/CSS first UI component library: check out how you can implement a drawer without JS  5 5 Not sold yet? Checkout some simple demo to crazier stuffs  , you can implement compliant, accessible UI elements without having to write a single line of JS. Why go the length? Consider it a challenge to really stress my CSS knowledge & creativity. Also hopefully my blog will one day be read by certain hardcore people who doesn’t enable JS in their browser, and this will not cause them any inconvenience.

Fin πŸ”—

A blog for me is a certain form of webnote. Some people like to interact with others through those by the means of comments. I do not. I’m putting this out for somebody to come across and read & understand a little bit more about myself πŸ’­ πŸ’­ Probably will mainly consist of prospective employers . And so, this static site generator setup makes me feel really comfortable writing up my thoughts & share it to the world.

Appendix: How I get Tufte’s style sidenote working with Hugo πŸ”—

Hugo default Markdown generator β€” goldmark β€” doesn’t allow overriding the render of footnotes. Essentially what it needs to work is to place the sidenote content right next to the trigger in the markup, like so:

<label for="fn1" class="margin-toggle sidenote-number">
    <sup>Sidenote Trigger</sup>
</label>
<input type="checkbox" id="fn1" class="margin-toggle"> 
<span class="sidenote">
    Side note content
</span>

The content, put next to the trigger, allow it to stay relatively close to the position of the trigger. The input element is another HTML + CSS trick to show/hide element on click: You can style the sidenote to be shown/hidden based on the :checked state of the input. This helps turning the sidenote into a toggle-able version on mobile. It looks something like this

@media (max-width: 684px) {
    input[type='checkbox'].margin-toggle + sidenote {
         display: none;   
    }
    input[type='checkbox'].margin-toggle:checked + sidenote {
         display: inline;   
    }
}

What a clever thing to do! Constraints really always give rise to ingenious ideas.

Anyway, back to the point: Hugo doesn’t allow me to hook into the HTML generation of the footnote. This is due to the inherent complicatedness of it: a footnote normally consist of a trigger link, in the midst of the content, then the actual footnote content is instead put at the bottom of the markup. It’s a two-phase process, and is inherently tied into the HTML generation. What they gave me looks like this:

<p>I'm referencing something in a footnote here <a href="#fn:1" id="fnref:1" >1</a> </p>

<!-- All the way, to the bottom -->

<ul class="footnotes">
    <li id="fn:1">Footnotes content <a href="#fnref:1">Back</a></li>
</ul>

So what can we do at this point. After receiving the resulting HTML string from my Markdown, I have to find a way to manipulate the markup.

See how I bolden the word string? You know what we can do with string?

Exactly: Search & Replace. Here comes a sacrilegious mess of Regex:

  • Use regex to find all the footnotes, extract their id
  • Loop through the footnotes id, find their link
  • Replace the link with the needed sidenote markup, along with the footnote content πŸ’­ πŸ’­ Technically, I also need to remove the Back button on each sidenote
  • Remove the original footnote markup

The end result is a sorta working setup. It breaks in a single, very predictable case that I can identify directly: nested list.

<li>
    Outer list item
    <ul>
        <li>Inner list item #1</li>
    </ul>
</li>
<li>Another outer list item</li>

If your regex is lazy: (?s)<li>(.*?)</li> 6 6 Hugo (Golang) use RE2 regex format. (?) block sets the flags single line  , the expression match

<li>
    Outer list item
    <ul>
        <li>Inner list item #1</li>

And if your regex is greedy (?s)<li>(.*)</li>, it catches everything. Because regex is stateless, you cannot have an expression that select just the right amount of </li> closing tag. For that, you need a parser. πŸ’­ πŸ’­ I have an idea of adding Goquery into the mix and allow me to do some Node based manipulation of the output, but that’s for the future . For now I need only to remember: no nested list.