Scaffolding post — it exists so every styled element has somewhere to be seen, and so there’s a file to copy when writing the first real one. Delete it, or keep it as a reference. Nothing depends on it.

The structure here is Jekyll’s minima theme, unchanged: a header with the wordmark and nav, a reverse-chronological list of dates and titles, and a three-column footer. The styling is the dataterminals palette — oxblood, bone, terracotta — over the same looping background the landing page uses.

Writing a post

Drop a file in _posts/ named YYYY-MM-DD-some-slug.md, with front matter:

---
layout: post
title: "The title, in quotes if it has a colon"
---

The date in the filename sets both the publication date and the URL, which resolves to /blog/2026/08/05/some-slug/. Anything dated in the future stays out of the build until that date passes, which makes scheduling a post a matter of naming it correctly.

The type scale

Third-level heading

Body text runs at 1.02rem over 1.75 line-height on a 660px measure — the same width the landing page uses, which lands at roughly 70 characters a line. Long enough not to feel cramped, short enough that the eye finds the next line.

Fifth-level heading

Below the fourth level, headings stop being serif and become tracked uppercase in the faint ink, because a small serif heading reads as emphasis rather than structure.

Inline elements

Links look like this, set in terracotta with a hairline under them — visible without hovering, since a hover-only underline disappears against a moving background. There’s bold, italic, inline code, and strikethrough.

A blockquote borrows the sub-tagline’s voice: serif, italic, dimmed, held off the left margin by an ember rule.

  • Unordered lists
  • take faint markers
  • so the text leads
  1. Ordered lists
  2. number in the same ink

Code

Fenced blocks are highlighted by Rouge, in a warm palette matched to the site rather than the usual cool defaults:

def parse_manifest(path: Path) -> dict[str, Entry]:
    """Read a mod manifest and index its entries by id."""
    with path.open(encoding="utf-8") as fh:
        raw = json.load(fh)

    entries = {}
    for item in raw.get("entries", []):
        entries[item["id"]] = Entry(
            name=item["name"],
            version=item.get("version", "0.0.0"),
            requires=tuple(item.get("requires", ())),
        )
    return entries

Lines longer than the measure scroll inside the block instead of widening the page:

2026-08-05T14:22:09Z  WARN  loader  entry 'tfw.workbench.compat' declares a dependency on 'tfw.core>=2.4.0' but the resolved graph pins 2.3.11 — continuing with the older version

Tables

Field Type Notes
id string Unique within a manifest
version semver Defaults to 0.0.0 when absent
requires list Resolved depth-first, cycles rejected

Images

Any image dropped in assets/ and referenced with a leading slash gets a hairline border and rounded corners:

![Alt text describing the image]({{ '/assets/some-image.png' | relative_url }})

That relative_url filter matters — it prepends /blog, which is what keeps links working both here and on a local preview. Bare /assets/… paths break in one of the two.