Skip to main content

max / makeover-layout

0.27.4: any width, one answer State the sibling of "First paint is final paint": a rendering is a pure function of the description and the viewport, so the same description at the same width is the same output whatever widths came before it. No renderer carries geometry across frames, and none narrows by counting. Width and Priority are the mechanism and both are already declared, so the rule is a statement of what the tree does rather than a change to it. Say what several fills do while the section is open: they divide what is left equally, which is the only sharing rule that does not depend on the order the description was written in.
Author: Max Johnson <me@maxj.phd> · 2026-08-16 21:39 UTC
Signed with PGP, not checked
Commit: e1d21b1e41a1bf21fcdf790d8ab282e2a2c978fb
Parent: 7389e98
2 files changed, +51 insertions, -5 deletions
M Cargo.toml +1 -1
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makeover-layout"
3 - version = "0.27.3"
3 + version = "0.27.4"
4 4 edition = "2024"
5 5 # One copy of this vocabulary per dependency graph, enforced by cargo rather
6 6 # than by remembering. Two versions of a description layer in one build means
M src/lib.rs +50 -4
@@ -365,6 +365,37 @@
365 365 //! the query, fixed for the life of the screen — and a renderer sizes for the
366 366 //! answer it was handed rather than for the one it hopes is coming.
367 367 //!
368 + //! # Any width, one answer
369 + //!
370 + //! The sibling of the rule above, and settled the same day. That one is
371 + //! independence from *when*; this one is independence from *how you got here*.
372 + //!
373 + //! A rendering is a pure function of the description and the viewport. The same
374 + //! description at the same width is the same output, whatever widths came
375 + //! before it. No renderer may carry geometry across frames, and none may narrow
376 + //! by counting.
377 + //!
378 + //! The failure this forbids is ordinary enough to be the default everywhere
379 + //! else: a page that hides its sidebar below some width, remembers that it hid
380 + //! it, and does not bring it back the same way. Layout there is a function of
381 + //! `(width, history)`, so dragging a window to 900 wide is a different screen
382 + //! depending on whether you came from 1400 or from 600. Nobody chose that; it
383 + //! is what measuring and remembering produce.
384 + //!
385 + //! The mechanism is [`Width`] for what grows and [`Priority`] for what drops.
386 + //! Both are declared, both are read off the description, and neither needs a
387 + //! measurement. A renderer narrows by raising a cutoff over a total order,
388 + //! never by counting what fits and stopping — `makeover-tui`'s table states
389 + //! that as its own rule and tests it, and `makeover-webview` reaches the same
390 + //! place with `@media` and `display: none`, which is path-independent by
391 + //! construction because CSS has nowhere to keep the previous width.
392 + //!
393 + //! Two things follow for anything new. A member that would need last frame's
394 + //! size to lay out this frame is refused, the same way a member that cannot be
395 + //! sized before it is filled is refused. And a fact about what disappears
396 + //! belongs in the description, because a host that has to infer it can only
397 + //! infer it from a measurement.
398 + //!
368 399 //! # Where the description stops
369 400 //!
370 401 //! The rule is that a member is added when an app needs a fact the vocabulary
@@ -3213,11 +3244,12 @@
3213 3244 }
3214 3245 }
3215 3246
3216 - /// How much room a column asks for.
3247 + /// How much room a placement asks for.
3217 3248 ///
3218 - /// An intent, so the actual floor stays with `makeover-geometry`. goingson's
3219 - /// task table spells these as `minmax(200px, 1fr)`, `140px` and content-sized;
3220 - /// only the first three words of that survive deferral.
3249 + /// A column says it, and so does a [`Field`]. An intent, so the actual floor
3250 + /// stays with `makeover-geometry`. goingson's task table spells these as
3251 + /// `minmax(200px, 1fr)`, `140px` and content-sized; only the first three words
3252 + /// of that survive deferral.
3221 3253 /// `#[non_exhaustive]`, for the reason [`Fill`] and [`FieldKind`] are: a
3222 3254 /// renderer matches on this and a vocabulary that grows must not break every
3223 3255 /// renderer when it does.
@@ -3229,6 +3261,20 @@
3229 3261 /// A fixed share, the same at every width.
3230 3262 Fixed,
3231 3263 /// Absorbs whatever is left over.
3264 + ///
3265 + /// **Several fills divide what is left equally.** Stated because it would
3266 + /// otherwise be undefined and each renderer would invent something, and
3267 + /// stated this way because equal division is the only sharing rule that
3268 + /// answers to "Any width, one answer" without a tiebreak: allocating in
3269 + /// declaration order makes the result depend on the order the description
3270 + /// was written in, which is a fact about the source file and not about the
3271 + /// screen. It documents what both renderers already do — CSS grid gives
3272 + /// `1fr 1fr`, ratatui gives each a `Constraint::Fill(1)` — rather than
3273 + /// changing anything.
3274 + ///
3275 + /// So a row of fills is a legal thing to describe, and there is no rule
3276 + /// against it. Measured 2026-08-16, every table in the tree uses exactly
3277 + /// one, which is the discipline this would otherwise have had to forbid.
3232 3278 Fill,
3233 3279 }
3234 3280