Bootstrapping a Blog

November 18th 2022

I’ve tried many static site generators in my time. They all have a great number of opinions, and to their detriment and mine we often don’t see eye-to-eye. I’m going to attempt building the most minimal possible blog I can, and I will write the development process out as I build the blog and deploy it in time with the development process. As I write this paragraph the blog doesn’t exist, and this will be its first post.

My goal is to take the file I’m writing at the moment (a markdown file), convert it into html, and “serve” it with the open command.

MVP first:

Let’s grab the markdown file, run the conversion into HTML, and see if it renders in a browser:

λ  cd blog

~/blog
λ  ls
making_a_static_blog_from_scratch.md

I don’t remember the exact usage of pandoc so:

λ  tldr pandoc

  Convert documents between various formats.
  More information: <https://pandoc.org>.

  Convert file to PDF (the output format is determined by file extension):

      pandoc input.md -o output.pdf

  Force conversion to use a specific format:

      pandoc input.docx --to gfm -o output.md

  Convert to a standalone file with the appropriate headers/footers (for LaTeX, HTML, etc.):

      pandoc input.md -s -o output.tex

  List all supported input formats:

      pandoc --list-input-formats

  List all supported output formats:

      pandoc --list-output-formats

tldr to the rescue.

Fortunately, pandoc has the easiest usage ever:

λ  pandoc making_a_static_blog_from_scratch.md -o first_post.html

λ  ls first_post.html
first_post.html

and

λ  open first_post.hml

Lo and behold:

Um… actually I’m not sure I have any way of embedding images in these documents.

Time to google some basic markdown syntax

searching basic markdown syntax like a n00b

Clicking on the second link provides a nice insight into the syntax, so now I’ll follow that and add this line to the file:

![searching basic markdown syntax like a n00b]()

But, alas, where to link the image? Probably time to make an images directory for this blog:

λ  mkdir assets

λ  mkdir assets/images

That should come in handy.

Now, time to see if we can actually link them. I took a screenshot of me making the search so:

λ  mv ~/Desktop/brave_search_md_syntax.png assets/images
/Users/alex/Desktop/brave_search_md_syntax.png -> assets/images/brave_search_md_syntax.png

Now perhaps if I enter the relative path the link will work:

![searching basic markdown syntax like a n00b](/assets/images/brave_search_md_syntax.png)
λ  pandoc making_a_static_blog_from_scratch.md -o second_render.html

λ  open second_render.html

Aha! Now! Lo and Behold: a screenshot of the second render

This deal gets more recursive all the time.

Elements of Style

I detest unstyled html pages (lookin’ at you Richard Stallman). So let’s beef this thing up shall we?

For some time I’ve wanted to build a blog using Tufte CSS. I’m a big fan of the side column with footnotes. (This will probably prevent me from writing so many parenthetical statements). In fact, it would be cool to automate away my parenthetical statements into footnotes with a script or something.

In the mean time, let’s see how much style we can get away with by pasting a simple link to the public style sheet.

λ  mkdir assets/stylesheets

λ  curl https://raw.githubusercontent.com/edwardtufte/tufte-css/gh-pages/tufte.css > ass
ets/stylesheets/tufte.css
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 11212  100 11212    0     0  41069      0 --:--:-- --:--:-- --:--:-- 41069

λ  ls assets/stylesheets
tufte.css

(There’s a minified version but I’ll grab it later, I’ll keep the large one, easier to tweak)

Let’s look at what pandoc generated shall we?

λ  ll *.html
.rw-r--r-- alex staff 1.8 KB Fri Nov 18 21:52:48 2022  first_post.html
.rw-r--r-- alex staff 3.6 KB Fri Nov 18 22:14:42 2022  second_render.html
.rw-r--r-- alex staff 3.6 KB Fri Nov 18 22:14:48 2022  third_render.html

λ  bat third_render.html
───────┬────────────────────────────────────────────────────────────────────────────────
File: third_render.html
───────┼────────────────────────────────────────────────────────────────────────────────
   1<h1 id="bootstrapping-a-blog">Bootstrapping a Blog</h1>
   2<h2 id="november-18th-2022">November 18th 2022</h2>
   3<p>I’ve tried many static site generators in my time. They all have far too man
y opinions. I decided to build the most minimal possible blog I could. I’m writ
ing this article as I build the blog and deploy it. In real time. So as I write
this paragraph. The blog doesn’t exist. This will be the first post on the blo
g.</p>
   4<p>I’ve started by doing the sensible thing at the beginning of any project, an
d opened my terminal. My goal for tonight is to take this file I’m writing at t
he moment, convert it into html, and “serve” it with the <code>open</code> comm
and. Here are a few things I’ll need:</p>

...

I’m pleasantly surprised. Pandoc didn’t generate a full html file with a doctype declaration, body etc. It only translated the direct elements like headers and paragraphs. I have an idea about how I’ll build this into a neat little system. But more on that later.

Thinking on paper here, each page when rendered is going to need:

  • A <!doctype> declaration
  • A <head>
  • A <body>

So let’s hack that out real quick:

<!DOCTYPE HTML>
<html>
    <head>
    </head>

    <body>

    content goes here
    
    </body>
</html>

Nothing fancy. Let’s add the stylesheet link, as suggested by the Tufte CSS README:

<!DOCTYPE HTML>
<html>
    <head>
        <link rel="stylesheet" href="/assets/stylesheets/tufte.css"/>
    </head>

    <body>

    content goes here
    
    </body>
</html>

Let’s make the beginning of the file like this:

<!DOCTYPE HTML>
<html>
    <head>
        <link rel="stylesheet" href="/assets/stylesheets/tufte.css"/>
    </head>

    <body>

and put it in a file called head.html;

λ  cat << EOF > head.html
<!DOCTYPE HTML>
<html>
    <head>
        <link rel="stylesheet" href="/assets/stylesheets/tufte.css"/>
    </head>

    <body>
EOF

Then we can take the bottom half of our html file and put it in tail.html;

λ  cat << EOF > tail.html
    </body>
</html>
EOF

And that gets us:

λ  bat head.html tail.html
───────┬────────────────────────────────────────────────────────────────────────────────
File: head.html
───────┼────────────────────────────────────────────────────────────────────────────────
   1<!DOCTYPE HTML>
   2<html>
   3<head>
   4<link rel="stylesheet" href="/assets/stylesheets/tufte.css"/>
   5</head>
   6
   7<body>
───────┴────────────────────────────────────────────────────────────────────────────────
───────┬────────────────────────────────────────────────────────────────────────────────
File: tail.html
───────┼────────────────────────────────────────────────────────────────────────────────
   1</body>
   2</html>
───────┴────────────────────────────────────────────────────────────────────────────────

This is nice because it will let us do things like

λ  cat head.html third_render.html tail.html
<!DOCTYPE HTML>
<html>
    <head>
        <link rel="stylesheet" href="/assets/stylesheets/tufte.css"/>
    </head>

    <body>
<h1 id="bootstrapping-a-blog">Bootstrapping a Blog</h1>
<h2 id="november-18th-2022">November 18th 2022</h2>
<p>I’ve tried many static site generators in my time. They all have far too many opinions. I decided to build the most minimal possible blog I could. I’m writing this article as I build the blog and deploy it. In real time. So as I write this paragraph. The blog doesn’t exist. This will be the first post on the blog.</p>

...

<pre><code>λ  pandoc making_a_static_blog_from_scratch.md -o second_render.html

λ  open second_render.html
</code></pre>
<p>Aha! Now! Lo and Behold: <img src="/assets/images/second_render_image.png" alt="a screenshot of the second render" /></p>
    </body>
</html>

Which gets our entire file.

We can get our fourth iteration of the page with:

cat head.html third_render.html tail.html > fourth_render.html

And now we have style: the website but with tufte css

However we haven’t updated the site content in a while so let’s produce a more current version of our fourth iteration by running:

λ pandoc making_a_static_blog_from_scratch.md -o fourth_iteration_content.html

λ  cat head.html fourth_iteration_content.html tail.html > fourth_render.html

λ  open fourth_render.html
updated fourth render

(An astute reader will notice I had swapped the parens and square braces for the previous image. That is now fixed.)

Automating

image credit xkcd.com/1319

This whole process of running pandoc and concatenating files is starting to get a little tedious. It would be nice to have a script we could use like this:

cat blog_post.md | ./publish > blog_post.html && open blog_post.html

So let’s write it. This one is pretty simple so I’ll make it a little shell script:

#!/bin/sh

cat - | pandoc -f markdown -t html | cat head.html - tail.html

This reads from stdin, hands it to pandoc with instructions to convert markdown to html, and then concatenates the head.html with stdin (which is now stdout from pandoc) and tail.html.

Make it executable:

λ  chmod u+x publish

And test it:

λ  cat making_a_static_blog_from_scratch.md | ./publish > render_five.html && open render_five.html

Which works fine:

Successful Automation

Define a quick alias and we won’t have to think about it much anymore:

alias publish='cat making_a_static_blog_from_scratch.md | ./publish > current.html && open current.html'

Actually, better yet throw that into a Makefile:

publish_blog:
        @echo "Publishing..."
        @cat making_a_static_blog_from_scratch.md | pandoc -f markdown -t html | cat head.html - tail.html > current.html && open current.html

And now an invocation of make will pop open the most recent version of the post in my browser. I’d like to do batches of posts eventually and additional makefile commands will be useful as the site becomes more sophisitcated. But we’ll cross that bridge when we get there.

Custom Styling

Now, looking at the page I can already see a few styling nitpicks I’m going to have with the default pandoc/tufte generated output:

  • Lists (like the one I’m making now) don’t turn into <li> elements, they just get shunted into the html file in plain text. Not ideal
  • Code blocks lack syntax highlighting, everything is the same color and it blends together a bit
  • No way of representing margin notes in markdown. I’m honestly fine tweaking the html itself in the early stages but it would be nice to automate that bit
  • Large images are too wide, they exceed the width of the column of text
  • Single line code blocks are obscured by the horizontal scrollbar, and they don’t scroll until the end of the line
  • Blockquotes lack a distinct visual style and tend to blend into the rest of the content

Luckily since every piece of style here is under my control we can fix these. Let’s start with the easy stuff first:

Lists

An quick google search leads me to this stackoverflow question, which is nice since I’m not the only one to have this issue. It seems the markdown parser in pandoc requires lists to be separated by a newline between the paragraph and the list. That seems a bit draconian, I’ll try using the commonmark format as suggested by the answer to that question:

#!/bin/sh

cat - | pandoc -f commonmark -t html | cat head.html - tail.html
Lists work better with commonmark

…and it works! Lists now follow the law of least astonishment. Commonmark spec for reference

But is it worth it? After some finagling and looking around at different features I find I like the <figcaption> tags generated by the regular markdown parser. I’ll stick with that and remember to include a newline between lists items and the preceding line. You win some, you lose some.

Image Width

See how that image hangs off the right hand side? Not ideal.

This one was easy but involved delving a bit more deeply into the tufte css file. Most of the layout styling is applied to elements which are children of a semantic <section> tag, and the default tufte handling of images is a much more sane approach. Sure enough, adding <article> and <section> tags to head.html applies the sane styling to images:

<!DOCTYPE HTML>
<html>
    <head>
        <link rel="stylesheet" href="/assets/stylesheets/post.css"/>
    </head>

    <body>
      <article>
        <section>

This of course means each <article> has only one <section>, but it works well enough for now so I’ll leave it.

Syntax Highlighting

I went down several rabbit holes here. Starting with pure CSS libraries, moving into random command line tools, contemplating giving up my goal of using zero Javascript for this site. Pandoc though, continues to impress me. Examining the output of any code block generated reveals that pandoc parses the code and puts individual terms into <span> tags with a set of pre-defined classes. Where are those classes defined you ask? Well, it depends. Pandoc syntax highlighting relies on another haskell library called skylight to generate colorized output in pretty nearly any form you choose (HTML, ePub, PDF etc.). This is amazing! And, if I were using the handy -a option pandoc provides, it would generate the needed stylesheet and insert the classes into a <style> tag in the head of the document.

As mentioned before, I’m using pandoc so far to create only fragments of HTML and not complete documents. After discovering the syntax highlighting feature I seriously considered using pandoc as a complete solution. But truth be told I’d like to have as much control over the HTML as I can. So I’ll stick with my simple (if unorthodox) cat solution for the time being.

However, I’d still love the syntax highlighting capability for <code> blocks. Pandoc by default will annotate code blocks and their content with various classnames:

```ruby
def foo
    puts "bar"
end
```

Becomes:

<div class="sourceCode" id="cb1">
  <pre class="sourceCode ruby">
    <code class="sourceCode ruby">
      <a class="sourceLine" id="cb1-1" title="1"><span class="kw">def</span> foo </a>
      <a class="sourceLine" id="cb1-2" title="2">    puts <span class="st">&quot;bar&quot;</span></a>
      <a class="sourceLine" id="cb1-3" title="3"><span class="kw">end</span></a>
    </code>
  </pre>
</div>

As you can see, pandoc annotates each token in the language generically and lets us apply specific styles later. All we need to do is provide the classes that represent keywords and strings etc. I looked into using skylight directly to generate the needed CSS, but it looks like the actual HTML/CSS generation is hidden within a Haskell library and the command line version won’t generate pure css. Fortunately we can cheat a little.

As this stackoverflow question points out, it’s possible to dump the raw theme file (which is just json), modify it, and then tell pandoc to use that theme. I’d like to make my code blocks match my terminal theme and editor color customizations as closely as possible. For now I’ll just copy the default theme generated by pandoc --print-highlight-style breezedark > breezedark.theme and tweak it to my liking.

λ  bat breezedark.theme
───────┬────────────────────────────────────────────────────────────────────────────────
File: breezedark.theme
───────┼────────────────────────────────────────────────────────────────────────────────
   1   │ {
   2"text-color": "#cfcfc2",
   3"background-color": "#232629",
   4"line-number-color": "#7a7c7d",
   5"line-number-background-color": "#232629",
   6"text-styles": {
   7"Other": {
   8"text-color": "#27ae60",
   9"background-color": null,
  10"bold": false,
  11"italic": false,
  12"underline": false
  13   │         },
  14"Attribute": {
  15"text-color": "#2980b9",
  16"background-color": null,
  17"bold": false,
  18"italic": false,
  19"underline": false
  20   │         },
  21"SpecialString": {
  22"text-color": "#da4453",
  23"background-color": null,
  24"bold": false,
  25"italic": false,

  ...

I’ll leave the default color options for syntax highlighting alone for now and set the background and foreground colors to #0629435c and #deb88d respectively.

{
    "text-color": "#deb88d",
    "background-color": "#0629435c",
    "line-number-color": "#deb88d",
    "line-number-background-color": "#0629435c",
    "text-styles": {

...

Then take an arbitrary markdown file with a code block and convert it to HTML with our custom theme:

λ  pandoc -s foo.md --highlight-style=custom.theme -o foo.html

After a few other tweaks we get something like:

Code block with syntax highlighting

Now we can slurp up the css from our generated file:

Emacs colorized hex color codes for syntax highlighting

And move it into our assets/stylesheets directory.

It’s time to get organized (but not too much) with our stylesheets so I’ll create a new post.css file for post content and include both the existing tufte.css in it:

@import url('./tufte.css');
@import url('./syntax_highlight.css');

And in head.html update the stylesheet to reference the new post file:

<link rel="stylesheet" href="/assets/stylesheets/post.css"/>

Time to test it:

Test of syntax highlighting

Boom.

Some of the default styling needs work, I’ll probably keep nearly everything from Tufte as far as layout of code blocks and then let the pandoc/skylight styling do the rest with some modifications in highlights as they come up.

Margin Notes

Proper margin and footnotes in Tufte follow this pattern:

<p>
One of the most distinctive features of Tufte’s style is his extensive use of sidenotes.
  <label for="sn-extensive-use-of-sidenotes" class="margin-toggle sidenote-number"></label>
  <input type="checkbox" id="sn-extensive-use-of-sidenotes" class="margin-toggle">
  <span class="sidenote">This is a sidenote.</span> 
Sidenotes are like footnotes, except they don’t force the reader to jump their eye to the bottom of the page, but instead display off to the side in the margin.
</p>

I don’t want to have to type all that out by hand every time I want to make a note. Preferably I’d be able use syntax like what pandoc recognizes for notes and have the process be automatic.

I’ll try out pandoc sidenote.

λ  cat making_a_static_blog_from_scratch.md | pandoc --filter pandoc-sidenote -f markdown -t html | cat head.html - tail.html > current.html
pandoc-sidenote: Error in $: Incompatible API versions: encoded with [1,17,5,4] but attempted to decode with [1,22].
CallStack (from HasCallStack):
  error, called at src/Text/Pandoc/JSON.hs:107:64 in pandoc-types-1.22-Bw7urHogZSoFhJ20EcUJPg:Text.Pandoc.JSON
Error running filter pandoc-sidenote:
Filter returned error status 1

Well that’s no fun. It’s worth noting that I installed pandoc some time ago and sure enough:

λ  pandoc --version
pandoc 2.5

Which according to the documentation is not a supported version. I think I’ll take a risk and blow pandoc away, and install the latest which by my count is 2.19. It makes me nervous this late in the game, but hey, what’s the worst that could happen?

λ  brew link --overwrite pandoc
Linking /usr/local/Cellar/pandoc/2.19.2... 3 symlinks created.

λ  pandoc --version
pandoc 2.19.2

I was really hoping to humorously tempt fate when I wrote that, but trying the command runs successfully. That’s nice except that there are no actual sidenotes or marginnotes in this document.

Let’s add one: No time like the present

Trying the command runs successfully. That's nice except that there are no actual sidenotes or marginnotes in this document.

Let's add one: [^1]
[^1]: No time like the present

With any luck the previous sentence before the code block will now render with a Tufte style sidenote. Running the command again produces:

screenshot of an unsuccessful sidenote

No dice.

After some finagling, I found that one needs to specify the footnotes extension as part of the markdown format to pandoc:

pandoc --filter pandoc-sidenote -f markdown+footnotes -t html

Once that’s in place, it goes smoothly:

screenshot of a successful sidenote

Just like magic. Kudos to markdown and pandoc-sidenotes. Sorry-not-sorry if the reverse time recursive image thing is getting a little out of hand.

Scrollbar issues with <code> blocks

I’m not in love with the current appearance of the horizontal scrollbars:

The unstyled scrollbar

Fortunately MDN provides a description of a css attribute scrollbar-color. Which allows you to style the scrollbar no matter what random redditors have to say:

div.sourceCode {
  ...
  scrollbar-color: #ffffff17 #fff0;
}

This works fine, except on webkit browsers, so to get that working we’ll need:

.sourceCode::-webkit-scrollbar-track {
  background: #fff0;
}

.sourceCode::-webkit-scrollbar-thumb {
  background-color:#ffffff17;
}

Blockquotes

Here’s an example of a blockquote:

Sometimes it’s better to light a flamethrower than curse the darkness.

– Terry Pratchett

As I write this paragraph, that blockquote looks like this:

unstyled blockquote

It’s not awful, but it’s not excellent either.

A few things that could make it better:

  • Font styling
  • Background color
  • Left border
  • The ability to control where newlines appear

Let’s work on adding those. Here’s what the css looks like currently:

blockquote {
    font-size: 1.4rem;
}

blockquote p {
    width: 55%;
    margin-right: 40px;
}

First off, I’d like the background color to be slightly different. However since the blockquote element takes up the full content of the <section>, I think I’ll swap the width of the blockquote p with the blockquote, and let the child <p> always take up 100% of its parent <blockquote>:

blockquote {
    font-size: 1.4rem;
    width: 55%;
}

blockquote p {
    width: 100%;
    margin-right: 40px;
}

Once that’s taken care of all that remains is to set up some background color and padding:

blockquote {
    font-size: 1.4rem;
    background: #5B5B662E;
    padding: 2px;
    width: 55%;
    padding-right: 15px;
    margin-left: 0px;
}

blockquote p {
    width: 100%;
    margin-right: 40px;
    padding-left: 25px;
}

Additionally, I would like a font that’s easily distinguishable from the rest of the text, and a border to the left:

blockquote {
    font-size: 1.4rem;
    background: #5B5B662E;
    padding: 2px;
    border-left: 10px #db9d59 solid;
    width: 55%;
    padding-right: 15px;
    margin-left: 0px;
}

blockquote p {
    width: 100%;
    margin-right: 40px;
    padding-left: 25px;
    font-size: 1.5rem;
    font-style: italic;
    font-family: courier;
}

Now here’s what the blockquote looks like:

styled blockquote

Better. I would still like the -- Terry Pratchett bit to appear with a newline between the quote body though.

I’m not sure there’s a way to get the markdown to work that way honestly. Pandoc removes the implied newline completely, which is in line with markdown spec if I recall. Here’s what it is now:

> Sometimes it's better to light a flamethrower than curse the darkness.
> -- Terry Pratchett

Playing around with it for a moment I find that I can get it to behave the way I want if I include a non-emptyNote the single space character on the “blank” line

line between the two:

> Sometimes it's better to light a flamethrower than curse the darkness.
> 
> -- Terry Pratchett

Here’s the final result for the blockquote:

final blockquote style

I like this for big callouts to a piece of text. However I would prefer smaller callouts to be available. For now I suspect I will simply italicize those and leave them in italics.

The End?

In short, no. Though much has been accomplished:

  • Lists don’t turn into <li> elements => Include a newline between the list and the previous line, or use commonmark
  • Code blocks lack syntax highlighting => Solved with pandoc extension
  • No way of representing margin notes in markdown. => Solved with pandoc extenstion
  • Large images are too wide => Structure the page the way tufte css expects
  • Code block scrollbar issues => Use the scrollbar-color css attribute and some pseudo-classes to style
  • Blockquotes need more distinct styling => Done with the above customizations.

There are various other changes I made as I went along that I don’t feel the need to document here, both in terms of text and style. If you’re curious, you can always pull up the site inspector yourself. After all, the map is not the territory.

It is at this point that I will stop documenting styling and layout changes to posts. It has been a useful excercise, and I suspect that the only reason I’ve come this far is because I’ve treated the development process as a way to both code and write at the same time. After all, the two things are not so different when done properly.

Content styling is complete. The remainder has yet to be built.

There are more things in heaven and earth, Horatio,

Than are dreamt of in your philosophy.