Files in the top-level directory in any check-in
- .fossil-settings
- blog
- newsletter
- pages
- port-watchers
- static
- web-pages
- writing
- being-in-the-graph-mind.page.rkt
- dicewordbook.page.rkt
- feed.rkt
- friction.page.rkt
- graph-mind.page.rkt
- index.page.rkt
- info.rkt
- LICENSE.md
- main.rkt
- makefile
- og-card-draw.rkt
- og-card.rkt
- projects.page.rkt
- publish_excludes.txt
- publishing-nerds.page.rkt
- README.md
- render-page.rkt
- screech.page.rkt
- site.rkt
- start-server
- static-dynamic.page.rkt
- tools.rkt
- two-approaches.page.rkt
This repo contains the source code for the site published at https://joeldueck.com.
All content here is copyrighted by Joel Dueck. User-facing prose content is licensed under
CC BY 4.0. You can make use of the code under a permissive license if send me an email
introducing yourself (see LICENSE for details).
Tour
This site is published with Punct and Camp.
Writing posts: quick reference
Sources are #lang punct jdcom files: Markdown, a metadata block at the top, and • functions
from main.rkt. Blog posts go in blog/, essays in writing/, standalone
pages in pages/. The filename (minus .md.rkt or .page.rkt) is the page’s slug.
---
title: High voltage
date: 2026-09-19
summary: One line, used in listings, feeds and link previews
---
Other metas: series (essays; groups them in listings), draft?: '#t (keeps the page out of
listings and feeds; it is still built) and card-style (see below).
The functions used most:
| Function | What it does |
|---|---|
•photo["name" #:ext "avif"]{Caption} |
Figure from static/images/. With "avif" it builds a srcset and expects name-500.avif, -800, -1000, -1200 and -1600. The default ext is "webp": one file, name.webp. Alt text is the caption unless you pass #:alt. |
•@{Mind Palaces} |
Link to another page by its slug, loosely matched (“Mind Palaces” finds mind-palaces). The link text is that page’s title; use •@["mind palaces"]{mind palace} for other link text. |
•aside{…} |
Margin note. |
•inline-note{…} |
A note in the text column rather than the margin. |
•—{Name, [_Source_][ref]} |
Attribution line at the end of a blockquote. |
•dialog{ •say["Who"]{What they said} … } |
Script-style dialogue; one •say per line. |
•notecard["Title"]{…} |
Titled, linkable card (dated notes, updates). #:id overrides the anchor. |
•key{Ctrl} |
Keyboard key. |
•caps{NASA} |
Small caps. |
•img-url{file.png} |
Just the /images/… URL, for plain Markdown links. |
•post-listing[] |
Essays grouped by year; •post-listing["Series name"] for one series. |
Link preview cards
Every page gets a 1200×630 og:image card, drawn at build time by
og-card.rkt. There are three styles:
essay— big title with the summary under it. The default everywhere except the blog.run-in— no display title; the post’s first paragraph, with the title as a bold run-in at the start, fading out at the bottom. The default for blog posts.lede— likerun-inbut without the title.
To override the style for one page, add a card-style meta:
---
title: High voltage
date: 2026-09-19
card-style: essay
---
A misspelled style name stops the build with an error rather than quietly falling back. If a
run-in or lede page has no paragraph to show, the card uses the summary; with no summary either,
it falls back to essay.
Cards are named slug-<hash>.png, where the hash covers everything that affects the pixels, so
they’re only redrawn when the title, summary, date or (for run-in/lede) first paragraph changes.
They’re cached in cards/cache/ and copied into publish/cards/; the cache survives
raco camp build --fresh. To force every card to be redrawn after a design change, bump
CARD-VERSION in og-card.rkt. Drawing needs macOS, the site’s fonts installed, and the small-caps
copy of Fabiol described in cards/freeze-smcp.py; on a machine
without those, pages whose cards aren’t already cached use the old static image, and the build says
so.