Skip to Content

Leaves Reach Across the Page

Good Words.

I’m really happy with that note, first because I like good words and I’m happy to have a place to collect them. Second because it looks nice, although it draws a little too heavily on some of its inspirations. And third, and maybe most importantly, it’s built through a new layer for rendering structured content.

Over the years I’ve built up this site so I can write in a lightweight markup language (CommonMark), a concise templating language (Pug), or Markup Classic (HTML). And despite having all of these available, I still disliked writing structured content, like tables or complex lists with repeated elements.

I might write a table like this:

table
  thead
    th Year
    th Artist
    th Title
  tbody
    tr
      td 1984
      td Steve Reich
      td The Desert Music: I
    tr
      td 1998
      td Steve Reich
      td Different Trains: After the War
    tr
      td 1995
      td Björk
      td Hyperballad
    [...]

And then I might realize I should an Album column, or maybe I should use a definition list and group the songs by artist.

Those changes might require redoing the entire thing, and that made me reluctant to try different layouts to figure out what worked best, made me anxious about working on this kind of content in general, and I’d put it off.

Here’s the new system: structured content is written in YAML, a serialization format with far too many sharp edges but somehow still one of the nicer ways to write structured content by hand:

---
-
  year: 1984
  artist: Steve Reich
  title: "The Desert Music: I"
-
  year: 1998
  artist: Steve Reich
  title: "Different Trains: After the War"
-
  year: 1995
  artist: Björk
  title: Hyperballad
...

This data can then be referenced from a Pug template:

table
  [...]
  tbody
    each song of allSongs
      tr
        td= song.year
        td= song.artist
        td= song.title

Say I did want to use that definition list instead. The data stays the same and all I need is a modified template:

dl
  // group is an injected function that returns a Map<Key, Element[]>.
  each [artist, songs] of group(allSongs, (s) => s.artist)
    dt= artist
    each song of songs
      dd "#{song.title}" (#{song.year})

I want to be clear: this isn’t novel. We do this all day long in software—separate data from its presentation, or write templates that are driven by the results of query languages; this is what Pug is for. But there’s something very subtle about mixing structured data with flowing content, inlining it with prose.11 This idea isn’t novel either. I’m fascinated by Obsidian’s Dataview plugin (external link), but I’m not writing a relational query engine for this site—yet?

1 This idea isn’t novel either. I’m fascinated by Obsidian’s Dataview plugin (external link), but I’m not writing a relational query engine for this site—yet?

This layer is still very simple, and there’s room for it to grow, but already it feels like it’s unlocked a class of notes that I’ve been having trouble working on.

On Preview
There are multiple formatting issues in the syndication feed; I’m throwing them on the list of things to fix.
Notes
Good Words
A Memory Called Empire by Arkady Martine
Heavy Rotation
Profound Mysteries III by Röyksopp