23 KiB
Writing Guide
Reference for creating content on levineuwirth.org. Covers file placement, all frontmatter fields, and every authoring feature available in the Markdown source.
File placement
| Type | Location | Output URL |
|---|---|---|
| Essay | content/essays/my-essay.md |
/essays/my-essay.html |
| Blog post | content/blog/my-post.md |
/blog/my-post.html |
| Poetry | content/poetry/my-poem.md |
/poetry/my-poem.html |
| Fiction | content/fiction/my-story.md |
/fiction/my-story.html |
| Composition | content/music/{slug}/index.md |
/music/{slug}/ |
| Standalone page | content/my-page.md |
/my-page.html |
| Standalone page (with co-located assets) | content/my-page/index.md |
/my-page.html |
File names become URL slugs. Use lowercase, hyphen-separated words.
If a standalone page embeds co-located SVG score fragments or other relative assets,
place it in its own directory (content/my-page/index.md) rather than as a flat file.
Score fragment paths are resolved relative to the source file's directory; a flat
content/my-page.md would resolve them from content/, which is wrong.
Frontmatter
Every file begins with a YAML block fenced by ---. Keys are read by Hakyll
and passed to templates; unknown keys are silently ignored and can be used as
custom template variables.
Essays
---
title: "The Title of the Essay"
date: 2026-03-15 # required; used for ordering, feed, and display
abstract: > # optional; shown in the metadata block and link previews
A one-paragraph description of the piece.
tags: # optional; see Tags section
- nonfiction
- nonfiction/philosophy
authors: # optional; overrides the default "Levi Neuwirth" link
- "Levi Neuwirth | /me.html"
- "Collaborator | https://their.site"
- "Plain Name" # URL optional; omit for plain-text credit
further-reading: # optional; see Citations section
- someKey
- anotherKey
bibliography: data/custom.bib # optional; overrides data/bibliography.bib
csl: data/custom.csl # optional; overrides Chicago Author-Date
no-collapse: true # optional; disables collapsible h2/h3 sections
js: scripts/my-widget.js # optional; per-page JS file (see Page scripts)
# js: [scripts/a.js, scripts/b.js] # or a list
# Epistemic Effort — all optional; the entire section is hidden unless `status` is set
status: "Working model" # Draft | Working model | Durable | Refined | Superseded | Deprecated
confidence: 72 # 0–100 integer (%)
importance: 3 # 1–5 integer (rendered as filled/empty dots ●●●○○)
evidence: 2 # 1–5 integer (same)
scope: average # personal | local | average | broad | civilizational
novelty: moderate # conventional | moderate | idiosyncratic | innovative
practicality: moderate # abstract | low | moderate | high | exceptional
confidence-history: # list of integers; trend arrow derived from last two entries
- 55
- 63
- 72
# Version history — optional; falls back to git log, then to date frontmatter
history:
- date: "2026-03-01" # ISO date as a quoted string (prevent YAML date parsing)
note: Initial draft
- date: "2026-03-14"
note: Expanded typography section; added citations
---
Blog posts
Same fields as essays. date formats the <time> element in the post header
and blog index. Posts appear in /feed.xml.
Poetry
Same fields as essays. Poetry uses a narrow-measure codex reading mode
(reading.html): 52ch measure, 1.85 line-height, stanza spacing, no drop cap.
poetryCompiler enables Ext_hard_line_breaks — each source newline becomes
a <br>, so verse lines render without trailing-space tricks.
External / non-original poems — use poet: instead of authors: to credit
an external author without generating a (broken) author index page:
---
title: Sonnet 60
date: 1609-05-20
poet: William Shakespeare
tags: [poetry]
---
poet: is a plain string. The metadata block shows "by William Shakespeare" as
plain text. Do not use authors: for poems you did not write; that would create
an /authors/william-shakespeare/ index page linking to the poem.
Fiction
Same fields as essays. Fiction uses the same codex reading mode as poetry:
62ch measure, chapter drop caps + smallcaps lead-in on h2 + p.
Standalone pages
Only title is required. Pages use pageCtx: no TOC, no bibliography, no
reading-time, no epistemic footer.
---
title: About
---
Add js: for any page-specific interactivity (see Page scripts).
Music compositions
Compositions live in their own directory. See the Music section for full details.
---
title: "Symphony No. 1"
date: 2026-01-15
abstract: >
A four-movement work for large orchestra.
tags: [music]
instrumentation: "orchestra (2222/4231/timp+2perc/str)"
duration: "ca. 32'"
premiere: "2026-05-20"
commissioned-by: "—" # optional
recording: audio/full-recording.mp3 # optional; full-piece audio player
pdf: scores/symphony.pdf # optional; download link
category: orchestral # orchestral | chamber | solo | vocal | choral | electronic
featured: true # optional; appears in Featured section of /music/
score-pages: # required for score reader; omit if no score
- scores/page-01.svg
- scores/page-02.svg
movements: # optional
- name: "I. Allegro"
page: 1 # 1-indexed starting page in the reader
duration: "10'"
audio: audio/movement-1.mp3 # optional; per-movement player
- name: "II. Adagio"
page: 18
duration: "12'"
---
Tags
Tags use slash-separated hierarchy. nonfiction/philosophy expands to both
nonfiction and nonfiction/philosophy, so the piece appears on both index
pages.
tags:
- nonfiction/philosophy
- research/epistemology
The top-level segment maps to a portal in the nav:
| Portal | URL |
|---|---|
| AI | /ai/ |
| Fiction | /fiction/ |
| Miscellany | /miscellany/ |
| Music | /music/ |
| Nonfiction | /nonfiction/ |
| Poetry | /poetry/ |
| Research | /research/ |
| Tech | /tech/ |
Tag index pages are paginated at 20 items and auto-generated on build.
Authors
The authors key controls the author line in the metadata block. When omitted,
it defaults to "Levi Neuwirth" linking to /authors/levi-neuwirth/.
Author pages are generated at /authors/{slug}/ and list all attributed content.
Pipe syntax: "Name | URL" — name is the link text, URL is the href.
The URL part is optional.
Citations
The citation pipeline uses Chicago Author-Date style. The bibliography lives at
data/bibliography.bib (BibLaTeX format) by default; override per-page with
bibliography and csl.
Inline citations
This claim is contested.[@smith2020]
Multiple sources agree.[@jones2019; @brown2021]
Inline citations render as numbered superscripts [1], [2], etc. The
bibliography section appears automatically in the page footer. citations.js
adds hover previews showing the full reference.
Further reading
Keys under further-reading appear in a separate Further Reading section
in the page footer. These are not cited inline.
further-reading:
- keyNotCitedInline
- anotherBackgroundSource
A key can appear both inline and in further-reading; it is numbered in the
bibliography and not duplicated.
Footnotes → sidenotes
Standard Markdown footnotes are converted to sidenotes at build time.
This sentence has a note.[^1]
[^1]: The note content, which may contain **bold**, *italic*, links, etc.
On wide viewports the note floats into the right margin. On narrow viewports it falls back to a standard footnote. Sidenotes are numbered automatically.
Avoid block-level elements (headings, lists) inside footnotes.
Wikilinks
Internal links can be written as wikilinks. The title is slugified to produce the URL.
[[Page Title]] → links to /page-title
[[Page Title|Link text]] → links to /page-title with custom display text
Slug rules: lowercase, spaces → hyphens, non-alphanumeric characters removed.
[[My Essay (2024)]] → /my-essay-2024.
Use standard Markdown link syntax [text](/path) for any link whose path is
not derivable from the page title.
Math
KaTeX renders client-side from raw LaTeX. CSS and JS are loaded conditionally
on pages that have math: true set in their context (all essays and posts have
this by default).
| Syntax | Usage |
|---|---|
$...$ |
Inline math |
$$...$$ on its own line |
Display math |
The quadratic formula is $x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$.
$$
\int_0^\infty e^{-x^2}\,dx = \frac{\sqrt{\pi}}{2}
$$
Links
Internal links: standard Markdown or wikilinks.
External links are classified automatically at build time:
- Opened in a new tab (
target="_blank" rel="noopener noreferrer") - Decorated with a domain icon via CSS
mask-image
| Domain | Icon |
|---|---|
wikipedia.org |
Wikipedia |
arxiv.org |
arXiv |
doi.org |
DOI |
github.com |
GitHub |
| Everything else | Generic external arrow |
Link preview popups
Hovering over an internal link shows a popup: title, abstract, authors, tags,
reading time. Hovering over a Wikipedia link shows an article excerpt.
Citation markers ([1]) show the full reference. All automatic.
Code
Fenced code blocks with a language identifier get syntax highlighting (JetBrains Mono). The language annotation also enables the selection popup's docs-search feature (maps to MDN, Hoogle, docs.python.org, etc.).
```haskell
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
```
Inline code: `like this`
Images
Place images in static/images/. All images automatically get loading="lazy".

For a captioned figure, add a title string. Pandoc wraps it in <figure>/<figcaption>:

Lightbox: standalone images automatically get data-lightbox="true".
Clicking opens a fullscreen overlay; close with ×, click outside, or Escape.
Images inside hyperlinks get lazy loading only — no lightbox.
Section collapsing
By default, every h2 and h3 heading in an essay gets a collapse toggle.
State persists in localStorage.
To disable on a specific page:
no-collapse: true
Exhibits and annotations
Pandoc fenced divs (:::) create structured callout blocks.
Exhibits
Always-visible numbered blocks with overlay zoom on click. Use for equations or proofs you want to reference structurally. Listed under their parent heading in the TOC.
::: {.exhibit .exhibit--equation}
$$E = mc^2$$
:::
::: {.exhibit .exhibit--proof}
*Proof.* Suppose for contradiction that … ∎
:::
Annotation callouts
Editorial sidebars — context, caveats, tangential detail.
::: {.annotation .annotation--static}
Always visible. Use for important caveats or context.
:::
::: {.annotation .annotation--collapsible}
Collapsed by default. Use for tangential detail.
:::
Score fragments
Inline SVG music notation, integrated with the gallery/exhibit system. Clicking a fragment opens the shared overlay alongside any math exhibits on the page.
::: {.score-fragment score-name="Main Theme, mm. 1–8" score-caption="The opening gesture."}

:::
Both attributes are optional, but score-name is strongly recommended — it
drives the overlay label and the TOC badge.
The image path is resolved relative to the source file's directory:
| Source file | SVG path | Reference as |
|---|---|---|
content/essays/my-essay.md |
content/essays/scores/theme.svg |
scores/theme.svg |
content/music/symphony/index.md |
content/music/symphony/scores/motif.svg |
scores/motif.svg |
content/me/index.md |
content/me/scores/vln.svg |
scores/vln.svg |
SVGs are inlined at build time. Black fill/stroke values are replaced with
currentColor so notation renders correctly in dark mode.
Excerpts
Pull-quotes from other works, displayed as indented blockquotes with a small attribution line and a stretched invisible link covering the whole figure.
Poem excerpt
Use when quoting verse that has a dedicated page on the site. The entire figure becomes a click target pointing to the poem; the attribution line remains an independent link.
<figure class="poem-excerpt">
<blockquote>
Like as the waves make towards the pebbled shore,
So do our minutes hasten to their end;
</blockquote>
<figcaption><a href="/poetry/sonnet-60.html">William Shakespeare — <em>Sonnet 60</em></a></figcaption>
</figure>
The blockquote renders with the default dashed left border and italic muted text.
No box or background. The figcaption is small-caps and left-aligned.
Prose excerpt
For quoting prose or for excerpts that do not have a page on the site:
<figure class="prose-excerpt">
<blockquote>
The passage you want to quote goes here.
</blockquote>
<figcaption><a href="https://example.com">Author — <em>Source Title</em></a></figcaption>
</figure>
prose-excerpt is full-width (width: auto; max-width: 100%) rather than
fit-content-wide like poem-excerpt. Both reset the image-figure box styles
(no background, no border, no padding).
Commonplace book
The /commonplace page is a YAML-driven quotation collection. Add entries to
data/commonplace.yaml; the page rebuilds automatically.
- text: |-
Like as the waves make towards the pebbled shore,
So do our minutes hasten to their end;
Each changing place with that which goes before,
In sequent toil all forwards do contend.
attribution: William Shakespeare
source: Sonnet 60
source-url: /poetry/sonnet-60.html
tags: [time, mortality]
commentary: >
Optional note. Shown below the quote in muted italic. Omit entirely if
not needed — most entries will not have one.
date-added: 2026-03-17
| Field | Required | Notes |
|---|---|---|
text |
yes | Use ` |
attribution |
yes | Author name, plain text |
source |
no | Title of the source work |
source-url |
no | Makes source a link |
tags |
no | Separate from content tags; used for "by theme" grouping |
commentary |
no | Your own remark on the passage |
date-added |
no | ISO date; used for chronological sort |
The page has a by theme / chronological toggle (state persists in
localStorage). Untagged entries appear under "miscellany" in the themed view.
Music
Catalog index (/music/)
content/music/index.md is the catalog homepage. Write prose about your
compositional work in the body; provide an abstract for the intro line.
The composition listing is auto-generated from all content/music/*/index.md
files — no manual list needed.
---
title: Music
abstract: Compositions spanning orchestral, chamber, and solo writing.
tags: [music]
---
[Your prose here — influences, preoccupations, approach to the craft.]
Composition pages
Each composition lives in its own subdirectory:
content/music/symphony-no-1/
├── index.md ← frontmatter + program notes prose
├── scores/
│ ├── page-01.svg ← one file per score page
│ └── symphony.pdf ← optional PDF download
└── audio/
├── full.mp3 ← optional full-piece recording
└── movement-1.mp3 ← optional per-movement recordings
Two URLs are generated automatically from one source:
/music/symphony-no-1/— prose landing page with metadata, audio players, movements/music/symphony-no-1/score/— minimal page-turn score reader
The score reader is only generated when score-pages is non-empty.
Catalog indicators — the /music/ catalog auto-derives:
- ◼ (score available):
score-pageslist is non-empty - ♫ (recording available):
recordingkey is present, or any movement hasaudio
Catalog grouping — category controls which section the work appears in.
Valid values: orchestral, chamber, solo, vocal, choral, electronic.
Anything else appears under "Other". Omitting category defaults to "other".
Featured works — set featured: true to also appear in the Featured section
at the top of the catalog.
Page scripts
For pages that need custom JavaScript (interactive widgets, visualisations, etc.),
place the JS file alongside the content and reference it via the js: frontmatter
key. The file is copied to _site/ and injected as a deferred <script> at the
bottom of <body>.
js: scripts/memento-mori.js # single file
or a list:
js:
- scripts/widget-a.js
- scripts/widget-b.js
Paths are relative to the content file. A composition at
content/music/symphony/index.md with js: scripts/widget.js serves the
script at /music/symphony/scripts/widget.js.
No changes to the build system are needed — the content/**/*.js glob rule
copies all JS files from content/ to _site/ automatically.
Epistemic profile
The epistemic footer section appears when status is set. All other fields
are optional and are shown or hidden independently.
| Field | Compact display | Expanded (<details>) |
|---|---|---|
status |
chip (always shown if present) | — |
confidence |
72% |
— |
importance |
●●●○○ |
— |
evidence |
●●○○○ |
— |
stability |
— | auto-computed from git history |
scope |
— | if set |
novelty |
— | if set |
practicality |
— | if set |
last-reviewed |
— | most recent commit date |
confidence-trend |
— | ↑/↓/→ from last two confidence-history entries |
Stability is auto-computed from git log --follow at every build. The
heuristic: ≤1 commits or age <14 days → volatile; ≤5 commits and age <90
days → revising; ≤15 commits or age <365 days → fairly stable; ≤30
commits or age <730 days → stable; otherwise → established.
To pin a manual stability label for one build, add the file path to IGNORE.txt
(one path per line). The file is cleared automatically by make build.
Version history
The version history footer section uses a three-tier fallback:
history:frontmatter — your authored notes, shown exactly as written.- Git log — if no
history:key, dates are extracted fromgit log --follow. Entries have no message (date only). date:frontmatter — if git has no commits for the file, falls back to thedatefield as a single "Created" entry.
make build auto-commits content/ before running the Hakyll build, so the
git log stays current without manual commits.
Write authored notes when the git log would be too noisy or insufficiently descriptive:
history:
- date: "2026-03-01"
note: Initial draft
- date: "2026-03-14"
note: Expanded section 3; incorporated feedback from peer review
Typography features
Applied automatically at build time; no markup needed.
| Feature | What it does |
|---|---|
| Smart quotes | "foo" / 'bar' → curly quotes |
| Em dashes | --- → — |
| En dashes | -- → – |
| Smallcaps | [TEXT]{.smallcaps} → font-variant-caps: small-caps |
| Drop cap | First letter of the first <p> gets a decorative large initial |
| Explicit drop cap | ::: dropcap fenced div applies drop cap to any paragraph |
| Ligatures | Spectral liga, dlig OT features active for body text |
| Old-style figures | Spectral onum active; use .lining-nums class to override |
To mark a phrase as small caps explicitly:
The [CPU]{.smallcaps} handles this.
Explicit drop cap
Wrap any paragraph in a ::: dropcap fenced div to get a drop cap regardless
of its position in the document. The first line is automatically rendered in
small caps.
::: dropcap
COMPOSITION [IS]{.smallcaps} PERHAPS MORE THAN ANYTHING ELSE THE PRACTICE OF MY
LIFE. I say these strong words because I feel strongly about this process.
:::
The opening word (or words, before a space) should be written in ALL CAPS in
source — they will render as small caps via ::first-line. The [IS]{.smallcaps}
span is not strictly necessary but can force specific words into the smallcaps
run if needed.
A paragraph that immediately follows a ::: dropcap block will be indented
correctly (text-indent: 1.5em), matching the paragraph-after-paragraph rule.
Text selection popup
Selecting any text (≥ 2 characters) shows a context-aware toolbar after 450 ms.
| Context | Buttons |
|---|---|
| Prose (multi-word) | BibTeX · Copy · DuckDuckGo · Here · Wikipedia |
| Prose (single word) | BibTeX · Copy · Define · DuckDuckGo · Here · Wikipedia |
| Math | Copy · nLab · OEIS · Wolfram |
| Code (known language) | Copy · <MDN / Hoogle / Docs…> |
| Code (unknown) | Copy |
BibTeX generates a @online{...} BibLaTeX entry with the selected text in
note={\enquote{...}} and copies it to the clipboard. Define opens English
Wiktionary. Here opens the Pagefind search page pre-filled with the selection.
Page footer sections
Essays get a structured footer. Sections with no data are hidden.
| Section | Shown when |
|---|---|
| Version history | Always (falls back through three-tier system) |
| Epistemic | status frontmatter key is present |
| Bibliography | At least one inline citation |
| Further Reading | further-reading key is present |
| Backlinks | Other pages link to this page |
Backlinks are auto-generated at build time. No markup needed — any internal link from another page creates an entry here, showing the source title and the surrounding paragraph as context.
Build
make build # auto-commit content/, compile, run pagefind, clear IGNORE.txt
make deploy # build + optional GitHub push + rsync to VPS
make watch # Hakyll live-reload dev server at http://localhost:8000
make dev # clean build + python HTTP server at http://localhost:8000
make clean # wipe _site/ and _cache/
make watch hot-reloads changes to Markdown, CSS, JS, and templates.
After any change to a .hs file, always run make clean && make build —
Hakyll's cache is keyed to source file mtimes and will serve stale output after
Haskell-side changes.
make deploy pushes to GitHub if GITHUB_TOKEN and GITHUB_REPO are set in
.env (see .env.example), then rsyncs _site/ to the VPS.