The site is a static one, built with Lektor. What follows is what a page is made of and why, for anyone who wants to borrow the arrangement.
The example page is a working demonstration of the arrangement: it uses the real proposition model and the real template, and its own text describes what each part of it is.
Read it alongside a page of the text proper — Proposition I.1 is the same shape with Euclid in it. Both show a statement in a boxed heading, a proof beneath it, citations in the margin beside the steps they support, a figure that can be dragged and also stepped through the proof a line at a time, and commentary below the whole thing. The example adds a lemma box before the proposition and a note box after, so all three section kinds are visible at once.
None of that is one piece of prose. The statement is quoted by other pages. The proof is walked. The citations point at the postulates they lean on and are collected back into a used in list on those pages. The commentary stays put while the proof moves. So the page is stored as those parts, separately, and the template puts them back together:
_model: proposition
---
title: Proposition 1
---
order: 1
---
short_label: I.1
---
sections:
#### prop_section ####
kind: proposition
---
statement: To construct an equilateral triangle on a given finite straight line.
---
proof:
Let {AB} be the given finite straight line.
<figure class="diagram"> … the geomlib element list and slides … </figure>
Describe the circle BCD with centre A and radius AB. [!just I.Post.3]
…
---
guide:
This proposition is the first…
_model picks both the field set and the template. Fields are separated by a
line of three dashes, and a flow block opens with #### name #### and separates
its own fields with four. short_label is the form other pages cite. order
sorts the page among its siblings, which is also what drives the previous and
next links, so no page needs to know its neighbours.
Three things inside the proof are extensions rather than plain markdown.
{AB} marks an element reference, bound to the figure beside it, so hovering
the letters lights the matching part of the diagram — described in full further
down. is a
justification, rendered in the margin and linked to what it names. And the
figure's element list feeds both the static diagram and the stepped proof, so
there is one description of the geometry rather than two that can disagree.
A model is the set of fields a kind of page has. There is one per kind of thing the text contains, rather than a single generic page, because they differ in what they carry and in how they are cited.
prematter_index for its listing.A flow block is a section a page can hold any number of, in order.
kind field says which, and the book's contents listing reads the same field to decide what appears as the book's entry for that proposition.definition.html, postulate.html and commonnotion.html do the same for their own fields.book.html and section_index.html for the levels beneath it — all drawing on the shared macros in _toc_macros.html, so an entry looks the same wherever it appears.page.html and the prematter templates.Every page is a directory holding a contents.lr file, and the directory path
is the URL — so content/elements/books/bookI/propositions/propI1/ is served at
/elements/books/bookI/propositions/propI1/. Anything sitting beside that file
is an attachment of the page and is copied through, which is where the original
diagram images live as fallbacks for readers without scripting.
The tree therefore mirrors the work: books, and under each of them its
definitions, postulates, common notions and propositions, with the front matter
and the essays alongside. Nothing computes a page's place from its content;
position in the tree and the order field are enough for the navigation, the
listings and the breadcrumbs.
Three small plugins, each answering one mismatch between the text and the tooling.
@I.47 and the plugin resolves it — and builds the reverse index from the same data. It also renders the {AB} element references.defX.III.6; Python reads the trailing .6 as an unrecognised file extension, and the development server offered the pages as downloads instead of displaying them.That last one is the honest shape of most of this work — a text's own numbering outliving the assumptions of the tools carrying it.
The lettered references in the prose are linked to the figure. Move the cursor over one — or tap it on a touch screen — and the element it names lights up in the diagram. Every other mention of the same element on the page lights up with it, so you can see at a glance where else a line or angle is being talked about.
Some pages carry more than one figure, and a reference can be tied to a particular one — or to several at once, which is useful when two figures show the same construction in different cases. The two triangles below share the labels A, B and C deliberately, so you can see the difference:
Highlighting runs both ways: when a step of a proof walk-through lights an element, the matching words in the prose light up too.
The reference is written {ABC} in the source. The plugin turns it into a
span bound to a figure, and the binding is what the trailing selector chooses:
no selector binds to the figure in context, {ABC:canvas_1} names one figure,
a comma-separated list names several, and {DISPLAY|element} shows one name
while lighting another.
A static build validates its own model, not yours. A page whose diagram is misspecified still builds; the figure simply does not appear, and the page looks finished.
So every figure is exercised before anything is published: check-decks.js runs each page's diagram code against the real library in a sandbox, and a page that throws, or that names an element which does not exist, stops the deploy. It paid for itself at once, finding a Book IV figure whose element declarations had been transposed during conversion and which had been quietly falling back to a static image ever since. check-versions.js keeps the library version consistent, since a version written into prose cannot be templated and will rot unnoticed.
The site is open source at github.com/brownnrl/euclids-elements-lektor. The models, flow blocks, templates and plugins are the parts worth lifting; the content is Joyce's. See geomlib for the diagram library, and the Quickstart for putting a figure on a page of your own.