Commits

Adaptive pages: from spec to production in one day

A 14-round grilling session, seven tracer bullets deployed, and calibration with actual n2 content: the engine now selects the layout for each article and corrects itself when it does not fit.

· auto-magazine · milestone

The day began with a 14-round grilling session and 45 decisions about the feature I had in mind: the engine would choose the layout for each article based on its content, verify it by measuring the render, and correct itself if it did not fit. The central idea: the Adaptive Page is a new entity, not just another Template. Some fitting rules (body word count, number of images including the featured image, its orientation), a promised page range, and its own CSS with automatic scoping.

Measure first, then decide

I measured every decision that had a technical alternative before asking about it. Named pages broke the continuous flow, so the variants cannot have their own @page rule. CSS nesting does work with WeasyPrint 70, which lifted a limitation I had already communicated. Page.anchors is public and makes measuring length trivial. And one of those measurements uncovered a contradiction between two decisions already made: with the variants preloaded with an open-ended range and a correction process that stopped at the first fit, the first item in the list would have claimed every article. The solution: during correction, the most specific range wins, and the open-ended range is the final fallback.

The spec came out as issue #7 in the tracker, with seven tickets attached as sub-issues and native GitHub dependencies. The boundary was #8: that was where implementation could begin.

Seven tracer bullets in one day

The first (#8) set up the table, the section in Settings, and four preloaded variants, with the default at the end as a fallback: its rules are open and fixed, because otherwise some article would have nowhere to go. The second (#9) got the engine running: it computes the signals for each article, chooses the first variant that fits, and injects its CSS after the master stylesheet, with nesting handling the automatic scoping. The third (#10) added per-article measurement within its own layers and the correction process, running it up to twice and warning if the range was not met. With these three, I updated production and rebuilt the container.

Then came the full CRUD (#12), with the name as the variant's fixed identity and deletion in Cleanup; the preview as a pure function and the manual override taking precedence over the engine (#11); and the PDF-faithful export to InDesign (#13), which brought up a bug in the .jsx script: it saved styles by role, so two articles with the same role came out with the same style. Styles are now identified by their definition. The final ticket (#14) was a complete run-through with actual content from the n2 edition, not test content.

Calibrate with real-world content

The three n2 articles are 174, 318, and 395 words long and occupy 1.43, 2.29, and 2.20 pages. The 250-word cutoff is confirmed. No material meets the 800-word cutoff, and Long is reduced to 500. The reference capacity for research (500–700 words per page) is refuted when photos are included: the actual measured figure is 150–200. The code keeps the measured value, not the paper-based figure.

Panel and cost

Free Templates can already be selected as a component of each article, and the panel shows what has been assigned in a label. Along the way, a hidden attribute surfaced that did not hide anything because of a display: flex declaration; it was fixed with the standard rule. I also measured the cost of the correction loop using the real count: 0.71 s with no corrections, 1.45 s with both, and each additional render costs about 0.37 s. The suite ended the day with 255 tests green.