A page is a constraint, not a canvas. Swiss grid systems compiled into executable rules — so that layouts can be counted, scored, sampled, and re-rolled.
01 The demonstration
It is a book, composed a moment ago, inside your browser. A grid was built from the size of the box below, the paragraphs were broken into lines by Knuth and Plass's 1981 algorithm, the plates were fitted into the fields the text left empty, and every baseline was laid on the lattice. Select a sentence: it is live text. Press new grid, and it is composed again from nothing.
02 The argument
That is Gerstner, in 1964, and it is the whole API. You do not author a layout. You author the constraints a layout must satisfy — and the library enumerates the ones that do, scores them by rules taken from the books, and lets a seed choose. The same seed returns the same page, forever.
Every typesetting engine can set a beautiful line. TeX has done it since 1981, and this library borrows its algorithm without apology. What no typesetter does is treat the grid as the primitive: a picture is three fields tall, never one hundred and thirty‑eight points; a caption lives in the row gutter; a column of text is a run of free field rows.
Which sounds like a cage and turns out to be a door. Because the set of legal pages is finite, layout stops being a matter of nudging boxes and becomes a search. The rules decide which pages are permitted. Taste — a small object of named weights — decides which permitted page is good.
This is generative art's proposition, applied to the page: not a template filled in, but a space of correct outcomes, sampled. The grid is not an afterthought bolted onto a typesetter. Here the grid is the point.
03 The canon, as code
The books were read cover to cover and encoded as assertions; the test suite reproduces the books' own dimensioned grids. When the library and the book disagree, the book is right. A sample:
| The rule | Source |
|---|---|
| No half lines in typography: every vertical distance is whole lines | MB 58–59 |
| Field height is a whole number of text lines; gutters are empty lines | MB 64, 134 |
| The narrower the column, the smaller the typeface | MB 57 |
| Seven to ten words a line; 8–12 pt for the text of books | MB 18, 30–31 |
| Cross‑size register: every style's line height meets one module | MB 59, 66 |
| A picture's width matches the width of a column of text | MB 63 |
| The band under a captioned picture grows in whole body lines | MB 64 |
| Captions belong in the caption column, beside their picture | MB 63, 87, 120 |
| The margin column sits at the outer edge, and mirrors | MB 55 |
| Few graded picture sizes; one picture may dominate | MB 11, 70 |
| The type area floats high: mass may not sink | MB 51 |
| Lines may be set flush right and left — but never the last | MB 80 |
| The mobile grid: one unit count, many simultaneous divisions | Gerstner 58–61 |
| The morphological box: the parameter space, enumerated | Gerstner 59 |
04 Books it has made
Nobody laid these pages out. Each is a story, a set of anchored figures, a grid, a type system, and a seed — passed to one function, which returned pages.
An essay on the Dutch flower piece, twenty plates, dark ground. The drop caps, the marginal captions, the widow control — all derived.
PDF ↓
The same words and the same pictures, a different grid and a different face. One argument, two typographies, no hand-work between them.
PDF ↓
All‑DIN, marginal caption column, signal numerals, a measured title. Bleeds, duotone plates, full‑page plates.
PDF ↓
A square book, made to exercise the difficult things: pull quotes in grid fields, rotated marginal text, justified setting, dropped initials.
PDF ↓05 The shape of it
// A programme: the constraints a page must satisfy.
const pages = composeDocument({
grid: ({ side }) => grid({ page: A4, margins, columns: 4,
line: body.lineHeight, rows: 8, side }),
styles: typeSystem({ module: 24, roles: { display, body, caption } }).roles,
story: paragraphs, // text, headings, dropped initials
figures: plates, // anchored: "(Plate 3)" in the prose
textSpan: 2, // a text column spans 2 grid columns
bodyAlign: 'justify', // grid-aware Knuth–Plass
seed: 'a-seed', // same seed, same book, forever
}).pages;
renderDOM(pages[0], el); // live text on the baseline grid
renderPage(pages[0]); // or SVG, or PDF