How I Made This Site

There has been some interest on GitHub about the theme for this site. Since I wrote this site more or less from scratch, there is no explicit theme on which it is based. So in this post, I’ll explain a few techniques and tools I’ve used to make it. Most aspects can be learned from analyzing the site using Chrome Developer Tools or Firebug, but a few things may be worth mentioning in more detail.


The blog uses a slightly modified version of Tufte CSS, which I’ve rewritten to SASS to make it easier to create a dark mode for it. I used this guide for creating a dark mode for my site. Other than that, there wasn’t much to change to the original Tufte CSS style, so it should be easy to reproduce the basic appearance. Tufte CSS doesn’t include support for syntax highlighting of code sections, so this was addes using highlight.js, with the colors chosen to match the Tufte CSS background color. To use Tufte CSS with its sidenotes and margin notes with Hugo, I’ve created the following shortcodes:

  1. The sidenote for a numbered note in the margin:

    <label for="sn-{{ index .Params 0 }}" class="margin-toggle sidenote-number">
    <input type="checkbox" id="sn-{{ index .Params 0 }}" class="margin-toggle" />
    <span class="sidenote">
    {{ .Inner }}

    This can be used with:

    {{< sidenote "unique_id" >}} note text {{< /sidenote >}}
  2. The marnote for unnumbered margin notes:

    <!-- margin note -->
    <label for="mn-{{ index .Params 0 }}" class="margin-toggle">&#8853;</label>
    <input type="checkbox" id="mn-{{ index .Params 0 }}" class="margin-toggle" />
    <span class="marginnote">
    {{ .Inner }}
    <!-- /margin note -->

    This can be used with:

    {{< marnote "other_id" >}} note text {{< /marnote >}}

Note that the "id" argument to the side/margin notes has to be unique for the entire post (it is used to toggle note visibility on mobile).


For the photography section of the site, I use PhotoSwipe by Dmitry Semenov. To make this work easily with Hugo, I’ve created a Python tool called HugoPhotoSwipe, which allows you to place photos of an album in a single folder and takes care of generating the Markdown for Hugo, etc. See this blog post for more details.

The photo galleries themselves contain the photo thumbnails arranged using a number of columns which depends on the width of the screen. Using SASS, this can be done easily with a mixin (basically a function). For the desktop version of the site, this mixin is:

@mixin margin-normal($cols, $iw, $im) {
  $thewidth: $cols * ($iw + 2 * $im);
  .gallery {
    width: $thewidth;
    margin-left: calc((100% - #{$thewidth}) / 2);

    figure {
      display: inline-block;
      width: $iw;
      margin: 0 $im 2*$im $im;

    img {
      width: $iw;
      margin: 0;

which is then used with fixed screen widths, as:

@media screen and (min-width: 900px) {
        @include margin-normal(6, $thumbnail-width, $thumbnail-margin);
@media screen and (min-width: 760px) and (max-width: 900px) {
        @include margin-normal(4, $thumbnail-width, $thumbnail-margin);

For the gallery overview page a similar strategy is used to determine how may columns of album cover images should be shown.

Landing page

Perhaps also of interest is the landing page, which loads the high-res background image in the, well…, background. This is a technique for progressive image loading which is becoming more common around the web on sites such as I personally used this guide by Jose Perez, with some modifications to change the image depending on whether you’re on mobile or not.

Bits and pieces

Some other useful resources if you’re curious about which tools I used:

  • The navigation bar uses SlickNav on mobile, which I’ve found easy to use.
  • Chrome on Android supports the theme-color meta tag.
  • The SASS code is run through the SassC command line application, then through CleanCSS to minify the resulting CSS. All this combined with the following GNU Makefile:

    SASS_TARGETS=$(basename $(filter-out $(wildcard _*.scss), $(wildcard *.scss)))
    TARGETS=$(addsuffix .min.css,$(SASS_TARGETS))
    DIR_TARGETS=$(shell find . -mindepth 1 -maxdepth 1 -type d)
    DIR_BUILD=$(addprefix build_,$(DIR_TARGETS))
    DIR_CLEAN=$(addprefix clean_,$(DIR_TARGETS))
    .PHONY: all clean
    all: build $(DIR_TARGETS)
    build: $(TARGETS) $(DIR_BUILD)
    %.css: %.scss
    $(info Building file: $< ...)
    sassc $< $@
    %.min.css: %.css
    $(info Minifying file: $< ...)
    @$(CLEANCSS) -o $@ $<
    $(info Copying file: $@ ...)
    @cp $@ $(OUTPUT_DIR)
    $(DIR_BUILD): build_%: %
    $(info Building directory $< ...)
    @$(MAKE) -C $< all
    clean: $(DIR_CLEAN)
    rm -f *.css *.min.css
    $(DIR_CLEAN): clean_%: %
    $(info Cleaning directory $< ...)
    @$(MAKE) -C $< clean

Hope this overview helps!