Working notes for Claude
Quick reference for editing this site. README.md covers user-facing setup; this file covers things that help with code changes.
Stack
- Lume 3 (Deno static site generator) with SSX as the JSX runtime (
lume/jsx-runtime). - TypeScript everywhere. No React, no Node, no npm install step.
- Deployed to GitHub Pages: a push to
mainruns.github/workflows/deploy.yml(setup-deno →deno task build→ publish_site/). Custom domaincbusby.devis set in repo Settings → Pages and pinned by the rootCNAMEfile (added viasite.add("/CNAME")).wwwredirects to apex via aCNAME www → cdbusby.github.ioDNS record. - No custom response headers: GitHub Pages ignores
_headers-style files, so caching/security headers fall back to GitHub's defaults.
Verify a change
deno task build # full build to _site/ — fast (sub-second), run this after any change
Built HTML lives in _site/<route>/index.html. Use grep against those files to confirm rendered output rather than starting the dev server.
File conventions
| Location | What it is |
|---|---|
*.tsx at the project root, or <dir>/index.tsx |
A page (because pageSubExtension: "" in _config.ts). Exports default (renderer), plus title, description, layout. |
_includes/*.tsx or _includes/*.vto |
A layout. JSX layouts receive children from the rendered page body. |
<dir>/_data.yml |
Sets defaults for all pages in that dir (e.g. training/_data.yml sets layout: training-log.tsx). |
*.md |
Markdown page. Frontmatter goes through the same preprocessors as .tsx/.jsx (image dim extraction, draft filter). |
Lume scans for files automatically — no manifest to update when adding a new .md or .tsx.
JSX / SSX gotchas
- Use
class, notclassName.forinstead ofhtmlFor(SSX still maps both, but match the codebase style). - Fragments work (
<>...</>). - To render raw HTML (e.g. the markdown body in a layout), just emit
{children}— when a JSX layout receives string content, Lume wraps it as{__html: ...}and SSX's escape function passes__htmlthrough unescaped. - No hooks, no state, no
useEffect. SSX is render-only. Browser-side JS goes in_includes/layout.vto<script>block. - For attributes with hyphens (like
transform-images), write them literally — SSX passes unknown props through.
Lume data API
Pages/layouts can be a function (data: Lume.Data) => JSX. Useful members:
data.search.pages(query, sort)— query pages. Examples:pages("url^=/training/ url!=/training/", "date=desc")— all training logs, newest first.pages("url^=/reading/ url!=/reading/")— all book reviews.
data.url,data.title,data.date, plus any frontmatter fields.children(layouts only) — the rendered body of the page using this layout.
Filters defined in _config.ts
Available in .vto templates as value |> filterName. In .tsx, prefer inline JS (the training-log.tsx layout duplicates formatDuration / activityTotals for this reason — keeps the file self-contained and typed).
formatDuration(seconds)→ "Xh Ym" or "Ym"activityTotals(days)→{ run: {distance, seconds, count}, ... }groupByDate(items, "month"|"year", dateField)
Image pipeline
- Drop sources into
images/<section>/. Reference via/images/...paths. - Add
transform-images="webp jpg <width1> <width2> ..."to<img>to generate responsive variants. - The preprocessor in
_config.tsauto-fillswidth/heightfrom sharp metadata forhero,cover, andbooks[].coverfrontmatter fields. No need to specify dimensions manually. - Add
class="fade-in"for the load-in animation (data-loadedset by JS inlayout.vto). - One LCP image per page should get
loading="eager" fetchpriority="high"; the restloading="lazy".
Training logs
- Day blocks with empty
activities: []render as "Rest" rows. activity.typeof"run"shows full columns;"strength"collapses workout/distance/time/pace/HR into onecell-summarycell holding the notes.- The layout (
_includes/training-log.tsx) computeshasTotalsandhasNotesand hides those sections entirely when empty (e.g. a full rest week). - Prev/next pager at the bottom auto-links to adjacent logs by date.
Books
- Single source of truth:
_data/books.yml. Books index reads this; the home page pickswip+ latest reviewed entries from it. review: <slug>links a book toreading/<slug>.md. A preprocessor stripsreview:if the target review hasdraft: true(setLUME_DRAFTS=trueenv to keep drafts visible locally).
CSS
- Single
styles.cssat the root. Lightning CSS plugin handles minification. - CSS custom properties defined at
:root(colors, fonts,--measurefor content width). - Cache-busted with
?v={{ buildTime }}(set toDate.now()in_config.ts). - Add new component styles at the bottom in a
/* --- Section --- */block.
Things to not do
- Don't add React, npm packages, or a bundler.
- Don't manually set
width/heighton<img>forhero/coverfields — the preprocessor does it. - Don't write to
_site/— it's the build output, gitignored, blown away each build. - Don't add new layouts without checking the existing pattern in
_includes/— book reviews use.vto, training logs use.tsx, both work fine.