ADR-008: Code folds shut by default, except where it is the answer
Status
Accepted (2026-07-31). Revised the same day: first accepted as code-fold: show (foldable but open), then changed to code-fold: true (folded shut) on the owner’s judgement after seeing both states live. The original reasoning and why it lost are kept below, because the argument it was based on is still the reason for the exceptions in decisions 2 and 3.
Context
Several pages are code-heavy enough that a reader has to scroll past a lot of Python to follow the prose. Measured on 2026-07-31:
- 6,461 lines of bare on-page Python across 54 pages; 16 pages carry more than 150 lines. Heaviest:
basics/05-frequency-domain(373),basics/04-z-domain(317),topics/ppg(308),topics/time-delay-estimation(284),basics/06-filter-design(261). - About a third of it (2,085 lines) is matplotlib plumbing: axis labels, legends, grid calls, annotations. On the heaviest basics chapters it reaches 40 to 52 percent. This is code the reader gains nothing from, because the figure caption already states what the figure shows.
- The exercise sets look like the worst offenders (around 500 lines each) but are not: every line already sits inside a
collapse="true"solution panel, so it is hidden until asked for. - Separately, 3,466 lines of static C/C++ across 31 embedded pages. Quarto’s
code-folddoes not apply to these; it only affects executable cells.
Quarto offers three settings: code-fold: false (no expander), true (expander, collapsed), and show (expander, open). Two pages had already opted in per-cell before this ADR: topics/wavelets/index.qmd folds 8 figure-producing blocks, and basics/pole-zero-explorer.qmd folds one with a custom code-summary.
The tension: folding everything by default would tidy the pages, but on Basics the code is the explanation. ADR-003 frames that section as pedagogical, building intuition step by step, and CLAUDE.md requires code that is clean and importable rather than notebook-only. A site whose teaching material is collapsed by default quietly demotes the thing it is teaching. The same argument applies with more force to the embedded C, which ADR-005 and CLAUDE.md explicitly frame as a companion implementation and not a read-only afterthought.
Decision
1. Global default is code-fold: true, summary “Show the code”
Set in _quarto.yml. Every executable cell starts collapsed, so a page reads as prose and figures, with the code one click away. The code-tools menu (already enabled) expands all of them at once.
How this was decided. The first version of this ADR chose code-fold: show (an expander on every cell, all open) to avoid demoting the teaching material. It shipped, and it was the wrong call for the problem: show is visually almost identical to no folding at all, so it bought the reader an option without delivering the readability the change existed to produce. The owner compared both states on the live site and chose folded-shut. Readability of the prose won over the worry about demoting the code, on the reasoning that a reader who wants the code is one click away and a reader who wants the argument is no longer scrolling past 40 lines of matplotlib to find it.
The worry that motivated show was not wrong, only outweighed, and it survives as the two exceptions below. Where code IS the content rather than the illustration, it does not fold.
2. Exercise pages do not fold (code-fold: false in their front matter)
On every page under exercises/, the Python already sits inside a collapse="true" solution panel: 100 percent of it on seven of the eight pages with executable cells. Folding it again would cost a second click to reveal an answer the reader has just explicitly asked for, which is the one place on the site where hiding code is clearly wrong. These pages therefore override the global default in their own front matter.
exercises/index.qmd and exercises/spot-the-bug.qmd need no override: neither has executable cells (spot-the-bug’s snippets are static listings, which code-fold does not touch).
3. The per-cell lever is now code-fold: false, for blocks that ARE the lesson
Now that the global default is true, a per-cell #| code-fold: true is a no-op, and the existing ones (8 in topics/wavelets/index.qmd, 1 in basics/pole-zero-explorer.qmd) are harmless but redundant. They are left in place rather than swept: they do no damage, and removing them touches two shipped pages for no reader-visible gain.
The lever that now matters is the inverse. Where a block does not illustrate the point but is the point, open it explicitly:
#| code-fold: false
Use it sparingly, on Basics especially, where a short block that demonstrates the algorithm under discussion is the explanation itself and reads worse behind a click. Long blocks that merely draw a figure stay folded: their caption already carries the meaning, and about a third of the site’s on-page Python is matplotlib plumbing of exactly that kind.
Apply this as pages are touched for other reasons, per the grow-by-deepening policy. No sweep is scheduled, and one is not required for the site to be correct.
4. Static C/C++ listings are not folded
code-fold does not reach them, and no substitute mechanism (a collapse="true" callout wrapper, custom CSS) is adopted. The embedded pages are where “math to metal” is delivered; their listings are the deliverable, not an appendix. Revisit only if a specific page becomes unreadable, and then per page, not globally.
Consequences
- Pages read as prose and figures. The roughly 6,500 lines of on-page Python stop competing with the argument, and the third of it that is matplotlib plumbing stops being scrolled past at all.
- Code is one click away rather than zero. This is a real cost on Basics, where a reader following an explanation may want the code inline; decision 3 exists to buy specific blocks back.
- Every rendered figure still renders. Folding hides the source, not the output, so captions, plots and printed results are unaffected.
- Notebooks (
demo.ipynb) and static C/C++ listings are untouched, so the embedded pages look exactly as before. - A future session that finds a page reads worse folded should reach for decision 3 on the specific blocks, not flip decision 1 back. Both global states have now been tried on the live site; this one was chosen with the other one visible for comparison.